@dynamic-labs-wallet/browser 0.0.259 → 0.0.261
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +211 -99
- package/index.esm.js +212 -101
- package/package.json +2 -2
- package/src/backup/encryption/core.d.ts.map +1 -1
- package/src/client.d.ts +14 -0
- package/src/client.d.ts.map +1 -1
- package/src/normalizeAddress.d.ts +7 -0
- package/src/normalizeAddress.d.ts.map +1 -0
- package/src/queue.d.ts.map +1 -1
- package/src/utils.d.ts +11 -1
- package/src/utils.d.ts.map +1 -1
package/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BitcoinAddressType, SigningAlgorithm, MPC_RELAY_PROD_API_URL, getMPCChainConfig, AuthMode, BackupLocation, WalletOperation, WalletReadyState, parseNamespacedVersion, FEATURE_FLAGS, ThresholdSignatureScheme, getClientThreshold, MPC_CONFIG, getTSSConfig, serializeMessageForForwardMPC, getReshareConfig, verifiedCredentialNameToChainEnum,
|
|
1
|
+
import { BitcoinAddressType, SigningAlgorithm, MPC_RELAY_PROD_API_URL, getMPCChainConfig, AuthMode, BackupLocation, ENCRYPTED_SHARES_STORAGE_SUFFIX, WalletOperation, WalletReadyState, parseNamespacedVersion, FEATURE_FLAGS, ThresholdSignatureScheme, getClientThreshold, MPC_CONFIG, getTSSConfig, serializeMessageForForwardMPC, getReshareConfig, verifiedCredentialNameToChainEnum, DynamicApiClient, getEnvironmentFromUrl, IFRAME_DOMAIN_MAP } from '@dynamic-labs-wallet/core';
|
|
2
2
|
export * from '@dynamic-labs-wallet/core';
|
|
3
3
|
import { BIP340, ExportableEd25519, Ecdsa, MessageHash, EcdsaSignature, EcdsaKeygenResult, ExportableEd25519KeygenResult, BIP340KeygenResult } from '#internal/web';
|
|
4
4
|
export { BIP340, BIP340InitKeygenResult, BIP340KeygenResult, Ecdsa, EcdsaInitKeygenResult, EcdsaKeygenResult, EcdsaPublicKey, EcdsaSignature, Ed25519, Ed25519InitKeygenResult, Ed25519KeygenResult, MessageHash } from '#internal/web';
|
|
@@ -222,6 +222,11 @@ const ensureBase64Padding = (str)=>{
|
|
|
222
222
|
]);
|
|
223
223
|
};
|
|
224
224
|
|
|
225
|
+
const INVALID_PASSWORD_ERROR = 'Decryption failed: Invalid password. Please check your password and try again.';
|
|
226
|
+
/**
|
|
227
|
+
* Check if an error is an OperationError from SubtleCrypto.decrypt,
|
|
228
|
+
* which indicates authentication failure (wrong password/derived key)
|
|
229
|
+
*/ const isInvalidPasswordError = (error)=>error instanceof Error && error.name === 'OperationError';
|
|
225
230
|
/**
|
|
226
231
|
* Get the appropriate key derivation function based on the encryption config
|
|
227
232
|
*/ const getKey = async (params, encryptionConfig)=>{
|
|
@@ -310,10 +315,15 @@ const ensureBase64Padding = (str)=>{
|
|
|
310
315
|
}, key, cipherBytes);
|
|
311
316
|
return new TextDecoder().decode(decryptedData);
|
|
312
317
|
} catch (retryError) {
|
|
313
|
-
|
|
318
|
+
if (isInvalidPasswordError(retryError)) {
|
|
319
|
+
throw new Error(INVALID_PASSWORD_ERROR);
|
|
320
|
+
}
|
|
314
321
|
throw new Error(`Decryption failed after retry with parallelism=1: ${retryError}`);
|
|
315
322
|
}
|
|
316
323
|
}
|
|
324
|
+
if (isInvalidPasswordError(error)) {
|
|
325
|
+
throw new Error(INVALID_PASSWORD_ERROR);
|
|
326
|
+
}
|
|
317
327
|
throw new Error('Decryption failed: ' + error);
|
|
318
328
|
}
|
|
319
329
|
};
|
|
@@ -506,6 +516,12 @@ const ERROR_EXPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error exporting pri
|
|
|
506
516
|
const ERROR_IMPORT_PRIVATE_KEY = '[DynamicWaasWalletClient]: Error importing private key';
|
|
507
517
|
const ERROR_PUBLIC_KEY_MISMATCH = 'Mismatch between the public keys of the server and the ones provided';
|
|
508
518
|
|
|
519
|
+
/**
|
|
520
|
+
* Normalizes an address to lowercase for consistent map key lookups.
|
|
521
|
+
* This ensures that addresses with different casing (e.g., EIP-55 checksummed vs lowercase)
|
|
522
|
+
* resolve to the same wallet entry.
|
|
523
|
+
*/ const normalizeAddress = (address)=>address.toLowerCase();
|
|
524
|
+
|
|
509
525
|
// eslint-disable-next-line @nx/enforce-module-boundaries
|
|
510
526
|
const isBrowser = ()=>typeof window !== 'undefined';
|
|
511
527
|
/**
|
|
@@ -696,6 +712,16 @@ const downloadStringAsFile = ({ filename, content, mimeType = 'application/json'
|
|
|
696
712
|
a.click();
|
|
697
713
|
URL.revokeObjectURL(url);
|
|
698
714
|
};
|
|
715
|
+
/**
|
|
716
|
+
* Checks if a wallet has cached encrypted shares in storage.
|
|
717
|
+
*/ const hasEncryptedSharesCached = async ({ accountAddress, backupInfo, storage })=>{
|
|
718
|
+
if (!(backupInfo == null ? void 0 : backupInfo.passwordEncrypted)) {
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
const encryptedStorageKey = `${normalizeAddress(accountAddress)}${ENCRYPTED_SHARES_STORAGE_SUFFIX}`;
|
|
722
|
+
const encryptedData = await storage.getItem(encryptedStorageKey);
|
|
723
|
+
return !!encryptedData;
|
|
724
|
+
};
|
|
699
725
|
/**
|
|
700
726
|
* Checks if an error is a public key mismatch error that can be recovered
|
|
701
727
|
* by re-fetching the client key share from backup.
|
|
@@ -1240,6 +1266,7 @@ const hasDelegatedBackup = (backupInfo)=>{
|
|
|
1240
1266
|
* Get or create the heavy operation queue for a wallet.
|
|
1241
1267
|
* Heavy operations (refresh/reshare/recover) run with concurrency=1.
|
|
1242
1268
|
*/ static getHeavyOpQueue(accountAddress) {
|
|
1269
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
1243
1270
|
if (!this.heavyOpQueues.has(accountAddress)) {
|
|
1244
1271
|
const queue = new PQueue({
|
|
1245
1272
|
concurrency: 1,
|
|
@@ -1259,6 +1286,7 @@ const hasDelegatedBackup = (backupInfo)=>{
|
|
|
1259
1286
|
* Get or create the sign queue for a wallet.
|
|
1260
1287
|
* Sign operations run with unlimited concurrency (they're read-only on key shares).
|
|
1261
1288
|
*/ static getSignQueue(accountAddress) {
|
|
1289
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
1262
1290
|
if (!this.signQueues.has(accountAddress)) {
|
|
1263
1291
|
const queue = new PQueue({
|
|
1264
1292
|
concurrency: Infinity,
|
|
@@ -1277,12 +1305,14 @@ const hasDelegatedBackup = (backupInfo)=>{
|
|
|
1277
1305
|
/**
|
|
1278
1306
|
* Check if wallet has heavy operations in progress
|
|
1279
1307
|
*/ static isHeavyOpInProgress(accountAddress) {
|
|
1308
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
1280
1309
|
const queue = this.heavyOpQueues.get(accountAddress);
|
|
1281
1310
|
return queue ? queue.size > 0 || queue.pending > 0 : false;
|
|
1282
1311
|
}
|
|
1283
1312
|
/**
|
|
1284
1313
|
* Check if wallet has operations in any queue (heavy or sign)
|
|
1285
1314
|
*/ static isWalletBusy(accountAddress) {
|
|
1315
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
1286
1316
|
const heavyQueue = this.heavyOpQueues.get(accountAddress);
|
|
1287
1317
|
const signQueue = this.signQueues.get(accountAddress);
|
|
1288
1318
|
const heavyBusy = heavyQueue ? heavyQueue.size > 0 || heavyQueue.pending > 0 : false;
|
|
@@ -1292,22 +1322,22 @@ const hasDelegatedBackup = (backupInfo)=>{
|
|
|
1292
1322
|
/**
|
|
1293
1323
|
* Check if recovery is in progress for a wallet
|
|
1294
1324
|
*/ static isRecoveryInProgress(accountAddress) {
|
|
1295
|
-
return this.pendingRecoveryPromises.has(accountAddress);
|
|
1325
|
+
return this.pendingRecoveryPromises.has(normalizeAddress(accountAddress));
|
|
1296
1326
|
}
|
|
1297
1327
|
/**
|
|
1298
1328
|
* Get existing pending recovery promise if one exists
|
|
1299
1329
|
*/ static getPendingRecoveryPromise(accountAddress) {
|
|
1300
|
-
return this.pendingRecoveryPromises.get(accountAddress);
|
|
1330
|
+
return this.pendingRecoveryPromises.get(normalizeAddress(accountAddress));
|
|
1301
1331
|
}
|
|
1302
1332
|
/**
|
|
1303
1333
|
* Track a pending recovery promise
|
|
1304
1334
|
*/ static setPendingRecoveryPromise(accountAddress, promise) {
|
|
1305
|
-
this.pendingRecoveryPromises.set(accountAddress, promise);
|
|
1335
|
+
this.pendingRecoveryPromises.set(normalizeAddress(accountAddress), promise);
|
|
1306
1336
|
}
|
|
1307
1337
|
/**
|
|
1308
1338
|
* Clear pending recovery promise for a wallet
|
|
1309
1339
|
*/ static clearPendingRecoveryPromise(accountAddress) {
|
|
1310
|
-
this.pendingRecoveryPromises.delete(accountAddress);
|
|
1340
|
+
this.pendingRecoveryPromises.delete(normalizeAddress(accountAddress));
|
|
1311
1341
|
}
|
|
1312
1342
|
/**
|
|
1313
1343
|
* Queue a heavy operation with type validation.
|
|
@@ -1319,6 +1349,7 @@ const hasDelegatedBackup = (backupInfo)=>{
|
|
|
1319
1349
|
* @param callback - The operation to execute
|
|
1320
1350
|
* @throws Error if operation is not a valid heavy queue operation
|
|
1321
1351
|
*/ static async queueHeavyOperation(accountAddress, operation, callback) {
|
|
1352
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
1322
1353
|
// Runtime validation to catch any type assertion bypasses
|
|
1323
1354
|
if (!isHeavyQueueOperation(operation)) {
|
|
1324
1355
|
throw new Error(`Invalid heavy queue operation: ${operation}. Must be REFRESH, RESHARE, or RECOVER.`);
|
|
@@ -1349,6 +1380,7 @@ const hasDelegatedBackup = (backupInfo)=>{
|
|
|
1349
1380
|
* @throws Error if operation is not a valid sign queue operation
|
|
1350
1381
|
* @returns Promise resolving to the sign result
|
|
1351
1382
|
*/ static async queueSignOperation(accountAddress, operation, callback) {
|
|
1383
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
1352
1384
|
// Runtime validation to catch any type assertion bypasses
|
|
1353
1385
|
if (!isSignQueueOperation(operation)) {
|
|
1354
1386
|
throw new Error(`Invalid sign queue operation: ${operation}. Must be SIGN_MESSAGE or SIGN_TRANSACTION.`);
|
|
@@ -1384,6 +1416,7 @@ const hasDelegatedBackup = (backupInfo)=>{
|
|
|
1384
1416
|
* @throws Error if operation is not a valid recover queue operation
|
|
1385
1417
|
* @returns Promise resolving to the recovery result
|
|
1386
1418
|
*/ static async queueRecoverOperation(accountAddress, operation, callback) {
|
|
1419
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
1387
1420
|
// Runtime validation to catch any type assertion bypasses
|
|
1388
1421
|
if (!isRecoverQueueOperation(operation)) {
|
|
1389
1422
|
throw new Error(`Invalid recover queue operation: ${operation}. Must be RECOVER.`);
|
|
@@ -1668,6 +1701,19 @@ class DynamicWalletClient {
|
|
|
1668
1701
|
*/ static resetStaticState() {
|
|
1669
1702
|
WalletQueueManager.resetForTesting();
|
|
1670
1703
|
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Get wallet properties from the wallet map using normalized address.
|
|
1706
|
+
* Normalizes the address to lowercase for consistent lookups regardless of input casing.
|
|
1707
|
+
*/ getWalletFromMap(accountAddress) {
|
|
1708
|
+
return this.walletMap[normalizeAddress(accountAddress)];
|
|
1709
|
+
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Update wallet properties in the wallet map using normalized address.
|
|
1712
|
+
* Normalizes the address to lowercase for consistent storage regardless of input casing.
|
|
1713
|
+
*/ updateWalletMap(accountAddress, updates) {
|
|
1714
|
+
const normalizedAddr = normalizeAddress(accountAddress);
|
|
1715
|
+
this.walletMap[normalizedAddr] = _extends({}, this.walletMap[normalizedAddr], updates);
|
|
1716
|
+
}
|
|
1671
1717
|
getAuthMode() {
|
|
1672
1718
|
return this.authMode;
|
|
1673
1719
|
}
|
|
@@ -2366,7 +2412,7 @@ class DynamicWalletClient {
|
|
|
2366
2412
|
* from walletMap, with fallback to deriving it from derivationPath.
|
|
2367
2413
|
*/ getBitcoinConfigForChain(chainName, accountAddress) {
|
|
2368
2414
|
if (chainName !== 'BTC') return undefined;
|
|
2369
|
-
const walletProperties = this.
|
|
2415
|
+
const walletProperties = this.getWalletFromMap(accountAddress);
|
|
2370
2416
|
let addressType = walletProperties == null ? void 0 : walletProperties.addressType;
|
|
2371
2417
|
// Fallback: derive addressType from derivationPath if not explicitly set
|
|
2372
2418
|
if (!addressType && (walletProperties == null ? void 0 : walletProperties.derivationPath)) {
|
|
@@ -2505,9 +2551,11 @@ class DynamicWalletClient {
|
|
|
2505
2551
|
* }>} Object containing new and existing client keygen results, IDs and shares
|
|
2506
2552
|
* @todo Support higher to lower reshare strategies
|
|
2507
2553
|
*/ async reshareStrategy({ chainName, wallet, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme }) {
|
|
2554
|
+
const bitcoinConfig = this.getBitcoinConfigForChain(chainName, accountAddress);
|
|
2508
2555
|
const mpcSigner = getMPCSigner({
|
|
2509
2556
|
chainName,
|
|
2510
|
-
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
2557
|
+
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
2558
|
+
bitcoinConfig
|
|
2511
2559
|
});
|
|
2512
2560
|
// Determine share counts based on threshold signature schemes
|
|
2513
2561
|
const { newClientShareCount, existingClientShareCount } = getReshareConfig({
|
|
@@ -2525,7 +2573,8 @@ class DynamicWalletClient {
|
|
|
2525
2573
|
})).slice(0, existingClientShareCount);
|
|
2526
2574
|
const existingClientKeygenIds = await Promise.all(existingClientKeyShares.map(async (keyShare)=>await this.getExportId({
|
|
2527
2575
|
chainName,
|
|
2528
|
-
clientKeyShare: keyShare
|
|
2576
|
+
clientKeyShare: keyShare,
|
|
2577
|
+
bitcoinConfig
|
|
2529
2578
|
})));
|
|
2530
2579
|
return {
|
|
2531
2580
|
newClientInitKeygenResults,
|
|
@@ -2583,6 +2632,7 @@ class DynamicWalletClient {
|
|
|
2583
2632
|
...newClientKeygenIds,
|
|
2584
2633
|
...existingClientKeygenIds
|
|
2585
2634
|
];
|
|
2635
|
+
const bitcoinConfig = this.getBitcoinConfigForChain(chainName, accountAddress);
|
|
2586
2636
|
// Server to create the room and complete the server reshare logics
|
|
2587
2637
|
const data = await this.apiClient.reshare({
|
|
2588
2638
|
walletId: wallet.walletId,
|
|
@@ -2603,7 +2653,6 @@ class DynamicWalletClient {
|
|
|
2603
2653
|
...serverKeygenIds,
|
|
2604
2654
|
...newServerKeygenIds
|
|
2605
2655
|
];
|
|
2606
|
-
const bitcoinConfig = this.getBitcoinConfigForChain(chainName, accountAddress);
|
|
2607
2656
|
const mpcSigner = getMPCSigner({
|
|
2608
2657
|
chainName,
|
|
2609
2658
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
@@ -2682,7 +2731,7 @@ class DynamicWalletClient {
|
|
|
2682
2731
|
allShares: allClientShares
|
|
2683
2732
|
});
|
|
2684
2733
|
}
|
|
2685
|
-
this.
|
|
2734
|
+
this.updateWalletMap(accountAddress, {
|
|
2686
2735
|
thresholdSignatureScheme: newThresholdSignatureScheme
|
|
2687
2736
|
});
|
|
2688
2737
|
// store client key shares to storage (localStorage or secureStorage)
|
|
@@ -2711,7 +2760,7 @@ class DynamicWalletClient {
|
|
|
2711
2760
|
}
|
|
2712
2761
|
});
|
|
2713
2762
|
// reset user wallet when reshare fails, this would allow the client to recover wallets from an active state
|
|
2714
|
-
this.
|
|
2763
|
+
this.updateWalletMap(accountAddress, {
|
|
2715
2764
|
thresholdSignatureScheme: oldThresholdSignatureScheme
|
|
2716
2765
|
});
|
|
2717
2766
|
await this.setClientKeySharesToStorage({
|
|
@@ -2724,7 +2773,6 @@ class DynamicWalletClient {
|
|
|
2724
2773
|
}
|
|
2725
2774
|
async performDelegationOperation({ accountAddress, password, signedSessionId, mfaToken, newThresholdSignatureScheme, revokeDelegation = false, operationName }) {
|
|
2726
2775
|
try {
|
|
2727
|
-
var _this_walletMap_accountAddress;
|
|
2728
2776
|
const delegateToProjectEnvironment = this.featureFlags && this.featureFlags[FEATURE_FLAGS.ENABLE_DELEGATED_KEY_SHARES_FLAG] === true;
|
|
2729
2777
|
if (!delegateToProjectEnvironment) {
|
|
2730
2778
|
throw new Error('Delegation is not allowed for this project environment');
|
|
@@ -2738,11 +2786,12 @@ class DynamicWalletClient {
|
|
|
2738
2786
|
if (wallet.chainName === 'SUI') {
|
|
2739
2787
|
throw new Error('Delegation is not allowed for SUI');
|
|
2740
2788
|
}
|
|
2741
|
-
const
|
|
2789
|
+
const walletData = this.getWalletFromMap(accountAddress);
|
|
2790
|
+
const currentThresholdSignatureScheme = walletData.thresholdSignatureScheme;
|
|
2742
2791
|
// Get active cloud providers to maintain existing backups
|
|
2743
|
-
const activeProviders = getActiveCloudProviders(
|
|
2792
|
+
const activeProviders = getActiveCloudProviders(walletData == null ? void 0 : walletData.clientKeySharesBackupInfo);
|
|
2744
2793
|
await this.reshare({
|
|
2745
|
-
chainName:
|
|
2794
|
+
chainName: walletData.chainName,
|
|
2746
2795
|
accountAddress,
|
|
2747
2796
|
oldThresholdSignatureScheme: currentThresholdSignatureScheme,
|
|
2748
2797
|
newThresholdSignatureScheme,
|
|
@@ -2773,7 +2822,7 @@ class DynamicWalletClient {
|
|
|
2773
2822
|
newThresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_THREE,
|
|
2774
2823
|
operationName: 'delegateKeyShares'
|
|
2775
2824
|
});
|
|
2776
|
-
const backupInfo = this.
|
|
2825
|
+
const backupInfo = this.getWalletFromMap(accountAddress).clientKeySharesBackupInfo;
|
|
2777
2826
|
const delegatedKeyShares = backupInfo.backups[BackupLocation.DELEGATED] || [];
|
|
2778
2827
|
return delegatedKeyShares;
|
|
2779
2828
|
}
|
|
@@ -2972,6 +3021,7 @@ class DynamicWalletClient {
|
|
|
2972
3021
|
/**
|
|
2973
3022
|
* helper function to store encrypted backup by wallet from iframe local storage
|
|
2974
3023
|
*/ async getClientKeySharesFromLocalStorage({ accountAddress }) {
|
|
3024
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
2975
3025
|
const walletObject = await this.storage.getItem(accountAddress);
|
|
2976
3026
|
if (!walletObject) {
|
|
2977
3027
|
this.logger.debug(`[DynamicWaasWalletClient] No item found in iframe local storage for accountAddress: ${accountAddress}`);
|
|
@@ -3000,6 +3050,7 @@ class DynamicWalletClient {
|
|
|
3000
3050
|
* Helper function to get client key shares from storage.
|
|
3001
3051
|
* Uses secureStorage when available (mobile), otherwise falls back to localStorage (browser).
|
|
3002
3052
|
*/ async getClientKeySharesFromStorage({ accountAddress }) {
|
|
3053
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
3003
3054
|
// Use secure storage if available (mobile)
|
|
3004
3055
|
if (this.secureStorage) {
|
|
3005
3056
|
try {
|
|
@@ -3032,7 +3083,7 @@ class DynamicWalletClient {
|
|
|
3032
3083
|
* @param derivationPath - Optional derivation path (will be computed from chainConfig if not provided)
|
|
3033
3084
|
* @param additionalProps - Any chain-specific additional properties to merge
|
|
3034
3085
|
*/ initializeWalletMapEntry({ accountAddress, walletId, chainName, thresholdSignatureScheme, derivationPath, additionalProps = {} }) {
|
|
3035
|
-
this.
|
|
3086
|
+
this.updateWalletMap(accountAddress, _extends({
|
|
3036
3087
|
accountAddress,
|
|
3037
3088
|
walletId,
|
|
3038
3089
|
chainName,
|
|
@@ -3041,7 +3092,7 @@ class DynamicWalletClient {
|
|
|
3041
3092
|
derivationPath
|
|
3042
3093
|
} : {}, {
|
|
3043
3094
|
clientKeySharesBackupInfo: getClientKeyShareBackupInfo()
|
|
3044
|
-
}, additionalProps);
|
|
3095
|
+
}, additionalProps));
|
|
3045
3096
|
this.logger.debug('walletMap initialized for wallet', {
|
|
3046
3097
|
context: {
|
|
3047
3098
|
accountAddress,
|
|
@@ -3055,6 +3106,7 @@ class DynamicWalletClient {
|
|
|
3055
3106
|
* Helper function to store client key shares in storage.
|
|
3056
3107
|
* Uses secureStorage when available (mobile), otherwise falls back to localStorage (browser).
|
|
3057
3108
|
*/ async setClientKeySharesToLocalStorage({ accountAddress, clientKeyShares, overwriteOrMerge = 'merge' }) {
|
|
3109
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
3058
3110
|
const stringifiedClientKeyShares = JSON.stringify({
|
|
3059
3111
|
clientKeyShares: overwriteOrMerge === 'overwrite' ? clientKeyShares : mergeUniqueKeyShares(await this.getClientKeySharesFromLocalStorage({
|
|
3060
3112
|
accountAddress
|
|
@@ -3066,6 +3118,7 @@ class DynamicWalletClient {
|
|
|
3066
3118
|
* Helper function to store client key shares in storage.
|
|
3067
3119
|
* Uses secureStorage when available (mobile), otherwise falls back to localStorage (browser).
|
|
3068
3120
|
*/ async setClientKeySharesToStorage({ accountAddress, clientKeyShares, overwriteOrMerge = 'merge' }) {
|
|
3121
|
+
accountAddress = normalizeAddress(accountAddress);
|
|
3069
3122
|
// Use secure storage if available (mobile)
|
|
3070
3123
|
if (this.secureStorage) {
|
|
3071
3124
|
try {
|
|
@@ -3111,8 +3164,8 @@ class DynamicWalletClient {
|
|
|
3111
3164
|
async backupSharesWithDistribution({ accountAddress, password, signedSessionId, distribution, preserveDelegatedLocation = false }) {
|
|
3112
3165
|
const dynamicRequestId = v4();
|
|
3113
3166
|
try {
|
|
3114
|
-
|
|
3115
|
-
if (!(
|
|
3167
|
+
const walletData = this.getWalletFromMap(accountAddress);
|
|
3168
|
+
if (!(walletData == null ? void 0 : walletData.walletId)) {
|
|
3116
3169
|
const error = new Error(`WalletId not found for accountAddress ${accountAddress}`);
|
|
3117
3170
|
logError({
|
|
3118
3171
|
message: 'Error in backupSharesWithDistribution, wallet or walletId not found from the wallet map',
|
|
@@ -3132,7 +3185,7 @@ class DynamicWalletClient {
|
|
|
3132
3185
|
password
|
|
3133
3186
|
})));
|
|
3134
3187
|
const data = await this.apiClient.storeEncryptedBackupByWallet({
|
|
3135
|
-
walletId:
|
|
3188
|
+
walletId: walletData.walletId,
|
|
3136
3189
|
encryptedKeyShares: encryptedDynamicShares,
|
|
3137
3190
|
passwordEncrypted: isPasswordEncrypted,
|
|
3138
3191
|
encryptionVersion: ENCRYPTION_VERSION_CURRENT,
|
|
@@ -3178,7 +3231,7 @@ class DynamicWalletClient {
|
|
|
3178
3231
|
var _publicKey_key_keyId;
|
|
3179
3232
|
const encryptedDelegatedKeyShareEnvelope = await encryptDelegatedKeyShare(JSON.stringify(distribution.delegatedShare), publicKey == null ? void 0 : (_publicKey_key1 = publicKey.key) == null ? void 0 : _publicKey_key1.publicKeyPemB64, (_publicKey_key_keyId = publicKey == null ? void 0 : (_publicKey_key2 = publicKey.key) == null ? void 0 : _publicKey_key2.keyId) != null ? _publicKey_key_keyId : publicKey == null ? void 0 : publicKey.keyId);
|
|
3180
3233
|
const { status } = await this.apiClient.publishDelegatedKeyShare({
|
|
3181
|
-
walletId:
|
|
3234
|
+
walletId: walletData.walletId,
|
|
3182
3235
|
encryptedKeyShare: encryptedDelegatedKeyShareEnvelope,
|
|
3183
3236
|
signedSessionId,
|
|
3184
3237
|
requiresSignedSessionId: this.requiresSignedSessionId(),
|
|
@@ -3200,23 +3253,23 @@ class DynamicWalletClient {
|
|
|
3200
3253
|
});
|
|
3201
3254
|
}
|
|
3202
3255
|
const backupData = await this.apiClient.markKeySharesAsBackedUp({
|
|
3203
|
-
walletId:
|
|
3256
|
+
walletId: walletData.walletId,
|
|
3204
3257
|
locations,
|
|
3205
3258
|
dynamicRequestId
|
|
3206
3259
|
});
|
|
3207
3260
|
const updatedBackupInfo = getClientKeyShareBackupInfo({
|
|
3208
3261
|
walletProperties: {
|
|
3209
|
-
derivationPath:
|
|
3262
|
+
derivationPath: walletData.derivationPath,
|
|
3210
3263
|
keyShares: backupData.locationsWithKeyShares.map((ks)=>({
|
|
3211
3264
|
id: ks.keyShareId,
|
|
3212
3265
|
backupLocation: ks.location,
|
|
3213
3266
|
externalKeyShareId: ks.externalKeyShareId,
|
|
3214
3267
|
passwordEncrypted: isPasswordEncrypted
|
|
3215
3268
|
})),
|
|
3216
|
-
thresholdSignatureScheme:
|
|
3269
|
+
thresholdSignatureScheme: walletData.thresholdSignatureScheme
|
|
3217
3270
|
}
|
|
3218
3271
|
});
|
|
3219
|
-
this.
|
|
3272
|
+
this.updateWalletMap(accountAddress, {
|
|
3220
3273
|
clientKeySharesBackupInfo: updatedBackupInfo
|
|
3221
3274
|
});
|
|
3222
3275
|
await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
|
|
@@ -3264,16 +3317,17 @@ class DynamicWalletClient {
|
|
|
3264
3317
|
* @param params.backupToGoogleDrive - Whether to backup to Google Drive (defaults to false)
|
|
3265
3318
|
* @returns Promise with backup metadata including share locations and IDs
|
|
3266
3319
|
*/ async storeEncryptedBackupByWallet({ accountAddress, clientKeyShares = undefined, password = undefined, signedSessionId, cloudProviders = [], delegatedKeyshare = undefined }) {
|
|
3267
|
-
var
|
|
3320
|
+
var _this_getWalletFromMap;
|
|
3268
3321
|
const keySharesToBackup = clientKeyShares != null ? clientKeyShares : await this.getClientKeySharesFromStorage({
|
|
3269
3322
|
accountAddress
|
|
3270
3323
|
});
|
|
3271
3324
|
// Check if we should backup to cloud providers (either requested or already exists)
|
|
3272
|
-
const
|
|
3325
|
+
const walletBackupInfo = (_this_getWalletFromMap = this.getWalletFromMap(accountAddress)) == null ? void 0 : _this_getWalletFromMap.clientKeySharesBackupInfo;
|
|
3326
|
+
const activeCloudProviders = getActiveCloudProviders(walletBackupInfo);
|
|
3273
3327
|
const shouldBackupToCloudProviders = cloudProviders.length > 0 || activeCloudProviders.length > 0;
|
|
3274
3328
|
// Use requested providers, or fall back to existing active providers
|
|
3275
3329
|
const providersToBackup = cloudProviders.length > 0 ? cloudProviders : activeCloudProviders;
|
|
3276
|
-
const hasExistingDelegation = hasDelegatedBackup(
|
|
3330
|
+
const hasExistingDelegation = hasDelegatedBackup(walletBackupInfo);
|
|
3277
3331
|
let distribution;
|
|
3278
3332
|
let preserveDelegatedLocation = false;
|
|
3279
3333
|
// Generic distribution logic - works with any cloud providers
|
|
@@ -3437,7 +3491,7 @@ class DynamicWalletClient {
|
|
|
3437
3491
|
}
|
|
3438
3492
|
async internalRecoverEncryptedBackupByWallet({ accountAddress, password, walletOperation, signedSessionId, shareCount = undefined, storeRecoveredShares = true, mfaToken }) {
|
|
3439
3493
|
try {
|
|
3440
|
-
const wallet = this.
|
|
3494
|
+
const wallet = this.getWalletFromMap(accountAddress);
|
|
3441
3495
|
this.logger.debug(`recoverEncryptedBackupByWallet wallet: ${walletOperation}`, wallet);
|
|
3442
3496
|
const { shares } = this.recoverStrategy({
|
|
3443
3497
|
clientKeyShareBackupInfo: wallet.clientKeySharesBackupInfo,
|
|
@@ -3485,7 +3539,12 @@ class DynamicWalletClient {
|
|
|
3485
3539
|
if (!wallets) {
|
|
3486
3540
|
return;
|
|
3487
3541
|
}
|
|
3488
|
-
|
|
3542
|
+
const parsedWallets = JSON.parse(wallets);
|
|
3543
|
+
this.walletMap = Object.keys(parsedWallets).reduce((acc, key)=>{
|
|
3544
|
+
const normalizedKey = normalizeAddress(key);
|
|
3545
|
+
acc[normalizedKey] = parsedWallets[key];
|
|
3546
|
+
return acc;
|
|
3547
|
+
}, {});
|
|
3489
3548
|
}
|
|
3490
3549
|
/**
|
|
3491
3550
|
* Internal helper method that handles the complete flow for ensuring wallet key shares are backed up to a cloud provider.
|
|
@@ -3499,11 +3558,12 @@ class DynamicWalletClient {
|
|
|
3499
3558
|
password,
|
|
3500
3559
|
signedSessionId
|
|
3501
3560
|
});
|
|
3502
|
-
const
|
|
3561
|
+
const walletData = this.getWalletFromMap(accountAddress);
|
|
3562
|
+
const currentThresholdSignatureScheme = walletData.thresholdSignatureScheme;
|
|
3503
3563
|
if (currentThresholdSignatureScheme === ThresholdSignatureScheme.TWO_OF_TWO) {
|
|
3504
3564
|
// Reshare to 2-of-3, which will automatically handle the backup distribution
|
|
3505
3565
|
await this.reshare({
|
|
3506
|
-
chainName:
|
|
3566
|
+
chainName: walletData.chainName,
|
|
3507
3567
|
accountAddress,
|
|
3508
3568
|
oldThresholdSignatureScheme: currentThresholdSignatureScheme,
|
|
3509
3569
|
newThresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_THREE,
|
|
@@ -3613,7 +3673,7 @@ class DynamicWalletClient {
|
|
|
3613
3673
|
const accessToken = await this.apiClient.getAccessToken({
|
|
3614
3674
|
oauthAccountId
|
|
3615
3675
|
});
|
|
3616
|
-
const thresholdSignatureScheme = this.
|
|
3676
|
+
const thresholdSignatureScheme = this.getWalletFromMap(accountAddress).thresholdSignatureScheme;
|
|
3617
3677
|
const fileName = getClientKeyShareExportFileName({
|
|
3618
3678
|
thresholdSignatureScheme,
|
|
3619
3679
|
accountAddress,
|
|
@@ -3653,7 +3713,7 @@ class DynamicWalletClient {
|
|
|
3653
3713
|
if (encryptedKeyShares.length === 0) {
|
|
3654
3714
|
throw new Error('No key shares found');
|
|
3655
3715
|
}
|
|
3656
|
-
const thresholdSignatureScheme = this.
|
|
3716
|
+
const thresholdSignatureScheme = this.getWalletFromMap(accountAddress).thresholdSignatureScheme;
|
|
3657
3717
|
const backupData = createBackupData({
|
|
3658
3718
|
encryptedKeyShares,
|
|
3659
3719
|
accountAddress,
|
|
@@ -3682,7 +3742,7 @@ class DynamicWalletClient {
|
|
|
3682
3742
|
const accessToken = await this.apiClient.getAccessToken({
|
|
3683
3743
|
oauthAccountId
|
|
3684
3744
|
});
|
|
3685
|
-
const thresholdSignatureScheme = this.
|
|
3745
|
+
const thresholdSignatureScheme = this.getWalletFromMap(accountAddress).thresholdSignatureScheme;
|
|
3686
3746
|
const backupFileName = getClientKeyShareExportFileName({
|
|
3687
3747
|
thresholdSignatureScheme,
|
|
3688
3748
|
accountAddress,
|
|
@@ -3758,6 +3818,7 @@ class DynamicWalletClient {
|
|
|
3758
3818
|
}
|
|
3759
3819
|
}
|
|
3760
3820
|
async exportClientKeyshares({ accountAddress, password, signedSessionId }) {
|
|
3821
|
+
var _this_getWalletFromMap;
|
|
3761
3822
|
await this.verifyPassword({
|
|
3762
3823
|
accountAddress,
|
|
3763
3824
|
password,
|
|
@@ -3769,7 +3830,7 @@ class DynamicWalletClient {
|
|
|
3769
3830
|
}
|
|
3770
3831
|
// Ensure client key shares exist before export
|
|
3771
3832
|
const clientKeyShares = await this.ensureClientShare(accountAddress);
|
|
3772
|
-
const derivationPath = this.
|
|
3833
|
+
const derivationPath = (_this_getWalletFromMap = this.getWalletFromMap(accountAddress)) == null ? void 0 : _this_getWalletFromMap.derivationPath;
|
|
3773
3834
|
const text = JSON.stringify({
|
|
3774
3835
|
keyShares: clientKeyShares,
|
|
3775
3836
|
derivationPath
|
|
@@ -3802,7 +3863,7 @@ class DynamicWalletClient {
|
|
|
3802
3863
|
let thresholdSignatureSchemeCheck = false;
|
|
3803
3864
|
let derivationPathCheck = false;
|
|
3804
3865
|
// check if wallet exists
|
|
3805
|
-
const existingWallet = this.
|
|
3866
|
+
const existingWallet = this.getWalletFromMap(accountAddress);
|
|
3806
3867
|
if (existingWallet) {
|
|
3807
3868
|
walletCheck = true;
|
|
3808
3869
|
}
|
|
@@ -3840,7 +3901,7 @@ class DynamicWalletClient {
|
|
|
3840
3901
|
* and decryption without storing the restored key shares. If unsuccessful, it throws an error.
|
|
3841
3902
|
*/ async verifyPassword({ accountAddress, password = undefined, walletOperation = WalletOperation.NO_OPERATION, signedSessionId }) {
|
|
3842
3903
|
// Only load wallet if it's not already loaded (to avoid double eager loading)
|
|
3843
|
-
if (!this.
|
|
3904
|
+
if (!this.getWalletFromMap(accountAddress)) {
|
|
3844
3905
|
await this.getWallet({
|
|
3845
3906
|
accountAddress,
|
|
3846
3907
|
walletOperation,
|
|
@@ -3899,7 +3960,7 @@ class DynamicWalletClient {
|
|
|
3899
3960
|
});
|
|
3900
3961
|
const { requiredShareCount } = this.recoverStrategy({
|
|
3901
3962
|
clientKeyShareBackupInfo: clientKeySharesBackupInfo,
|
|
3902
|
-
thresholdSignatureScheme: this.
|
|
3963
|
+
thresholdSignatureScheme: this.getWalletFromMap(accountAddress).thresholdSignatureScheme,
|
|
3903
3964
|
walletOperation
|
|
3904
3965
|
});
|
|
3905
3966
|
if (clientKeyShares.length >= requiredShareCount) {
|
|
@@ -3910,10 +3971,11 @@ class DynamicWalletClient {
|
|
|
3910
3971
|
async getWalletClientKeyShareBackupInfo({ accountAddress }) {
|
|
3911
3972
|
const dynamicRequestId = v4();
|
|
3912
3973
|
try {
|
|
3913
|
-
var
|
|
3974
|
+
var _this_getWalletFromMap, _walletBackupInfo_backups_BackupLocation_DYNAMIC, _walletBackupInfo_backups, _user_verifiedCredentials;
|
|
3914
3975
|
// Return existing backup info if it exists
|
|
3915
|
-
|
|
3916
|
-
|
|
3976
|
+
const walletBackupInfo = (_this_getWalletFromMap = this.getWalletFromMap(accountAddress)) == null ? void 0 : _this_getWalletFromMap.clientKeySharesBackupInfo;
|
|
3977
|
+
if (walletBackupInfo && ((_walletBackupInfo_backups = walletBackupInfo.backups) == null ? void 0 : (_walletBackupInfo_backups_BackupLocation_DYNAMIC = _walletBackupInfo_backups[BackupLocation.DYNAMIC]) == null ? void 0 : _walletBackupInfo_backups_BackupLocation_DYNAMIC.length) > 0) {
|
|
3978
|
+
return walletBackupInfo;
|
|
3917
3979
|
}
|
|
3918
3980
|
// Get backup info from server
|
|
3919
3981
|
const user = await this.apiClient.getUser(dynamicRequestId);
|
|
@@ -3944,30 +4006,36 @@ class DynamicWalletClient {
|
|
|
3944
4006
|
});
|
|
3945
4007
|
if (existingWalletCheck) {
|
|
3946
4008
|
this.logger.debug(`[DynamicWaasWalletClient] Wallet ${accountAddress} already exists`);
|
|
3947
|
-
|
|
4009
|
+
const wallet = this.getWalletFromMap(accountAddress);
|
|
4010
|
+
if (!wallet) {
|
|
4011
|
+
throw new Error(`Wallet not found for address: ${accountAddress}`);
|
|
4012
|
+
}
|
|
4013
|
+
return wallet;
|
|
3948
4014
|
}
|
|
3949
|
-
// Fetch and restore
|
|
4015
|
+
// Fetch and restore all waas wallets from server
|
|
3950
4016
|
const user = await this.apiClient.getUser(dynamicRequestId);
|
|
3951
|
-
const
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
4017
|
+
const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
|
|
4018
|
+
for (const vc of waasWallets != null ? waasWallets : []){
|
|
4019
|
+
const addr = vc.address;
|
|
4020
|
+
const props = vc.walletProperties;
|
|
4021
|
+
this.updateWalletMap(addr, {
|
|
4022
|
+
walletId: vc.id,
|
|
4023
|
+
chainName: verifiedCredentialNameToChainEnum[vc.chain],
|
|
4024
|
+
accountAddress: addr,
|
|
4025
|
+
thresholdSignatureScheme: props == null ? void 0 : props.thresholdSignatureScheme,
|
|
4026
|
+
derivationPath: props == null ? void 0 : props.derivationPath,
|
|
4027
|
+
clientKeySharesBackupInfo: getClientKeyShareBackupInfo({
|
|
4028
|
+
walletProperties: props
|
|
4029
|
+
}),
|
|
4030
|
+
addressType: props == null ? void 0 : props.addressType
|
|
4031
|
+
});
|
|
4032
|
+
}
|
|
3965
4033
|
if (walletOperation !== WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
|
|
3966
4034
|
accountAddress,
|
|
3967
4035
|
walletOperation
|
|
3968
4036
|
})) {
|
|
3969
4037
|
var _walletData_clientKeySharesBackupInfo;
|
|
3970
|
-
const walletData = this.
|
|
4038
|
+
const walletData = this.getWalletFromMap(accountAddress);
|
|
3971
4039
|
var _walletData_clientKeySharesBackupInfo_passwordEncrypted;
|
|
3972
4040
|
const isPasswordEncrypted = (_walletData_clientKeySharesBackupInfo_passwordEncrypted = (_walletData_clientKeySharesBackupInfo = walletData.clientKeySharesBackupInfo) == null ? void 0 : _walletData_clientKeySharesBackupInfo.passwordEncrypted) != null ? _walletData_clientKeySharesBackupInfo_passwordEncrypted : false;
|
|
3973
4041
|
// Password-encrypted wallet without password - fetch and cache for eager loading
|
|
@@ -3985,14 +4053,19 @@ class DynamicWalletClient {
|
|
|
3985
4053
|
requiresSignedSessionId: this.requiresSignedSessionId()
|
|
3986
4054
|
});
|
|
3987
4055
|
// Cache encrypted shares for later decryption by unlockWallet
|
|
3988
|
-
const encryptedStorageKey = `${accountAddress}${ENCRYPTED_SHARES_STORAGE_SUFFIX}`;
|
|
4056
|
+
const encryptedStorageKey = `${normalizeAddress(accountAddress)}${ENCRYPTED_SHARES_STORAGE_SUFFIX}`;
|
|
3989
4057
|
await this.storage.setItem(encryptedStorageKey, JSON.stringify(data));
|
|
3990
4058
|
// Update wallet state to locked
|
|
3991
|
-
this.
|
|
4059
|
+
this.updateWalletMap(accountAddress, {
|
|
3992
4060
|
walletReadyState: WalletReadyState.ENCRYPTED
|
|
3993
4061
|
});
|
|
4062
|
+
await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
|
|
3994
4063
|
// Return wallet in locked state (no error for eager loading)
|
|
3995
|
-
|
|
4064
|
+
const wallet = this.getWalletFromMap(accountAddress);
|
|
4065
|
+
if (!wallet) {
|
|
4066
|
+
throw new Error(`Wallet not found for address: ${accountAddress}`);
|
|
4067
|
+
}
|
|
4068
|
+
return wallet;
|
|
3996
4069
|
}
|
|
3997
4070
|
// TODO(zfaizal2): throw error if signedSessionId is not provided after service deploy
|
|
3998
4071
|
const decryptedKeyShares = await this.recoverEncryptedBackupByWallet({
|
|
@@ -4019,7 +4092,11 @@ class DynamicWalletClient {
|
|
|
4019
4092
|
if (walletCount === 1) {
|
|
4020
4093
|
return Object.values(this.walletMap)[0];
|
|
4021
4094
|
}
|
|
4022
|
-
|
|
4095
|
+
const wallet = this.getWalletFromMap(accountAddress);
|
|
4096
|
+
if (!wallet) {
|
|
4097
|
+
throw new Error(`Wallet not found for address: ${accountAddress}`);
|
|
4098
|
+
}
|
|
4099
|
+
return wallet;
|
|
4023
4100
|
} catch (error) {
|
|
4024
4101
|
logError({
|
|
4025
4102
|
message: 'Error in getWallet',
|
|
@@ -4050,17 +4127,21 @@ class DynamicWalletClient {
|
|
|
4050
4127
|
* @returns WalletRecoveryState indicating the wallet's lock state and share availability
|
|
4051
4128
|
* @throws Error if recovery fails or no shares available after recovery
|
|
4052
4129
|
*/ async getWalletRecoveryState({ accountAddress, signedSessionId, password }) {
|
|
4053
|
-
const walletData = this.
|
|
4130
|
+
const walletData = this.getWalletFromMap(accountAddress);
|
|
4054
4131
|
if (!walletData) {
|
|
4055
4132
|
throw new Error(`Wallet not found for address: ${accountAddress}`);
|
|
4056
4133
|
}
|
|
4057
4134
|
let clientKeyShares = await this.getClientKeySharesFromStorage({
|
|
4058
4135
|
accountAddress
|
|
4059
4136
|
});
|
|
4060
|
-
|
|
4137
|
+
let hasEncryptedShares = await hasEncryptedSharesCached({
|
|
4138
|
+
accountAddress,
|
|
4139
|
+
backupInfo: walletData.clientKeySharesBackupInfo,
|
|
4140
|
+
storage: this.storage
|
|
4141
|
+
});
|
|
4142
|
+
// If no local shares and no cached encrypted shares, trigger recovery via getWallet
|
|
4061
4143
|
// getWallet -> recoverEncryptedBackupByWallet handles deduplication via inFlightRecovery
|
|
4062
|
-
if (clientKeyShares.length === 0 && signedSessionId) {
|
|
4063
|
-
var _walletData_clientKeySharesBackupInfo;
|
|
4144
|
+
if (clientKeyShares.length === 0 && !hasEncryptedShares && signedSessionId) {
|
|
4064
4145
|
await this.getWallet({
|
|
4065
4146
|
accountAddress,
|
|
4066
4147
|
walletOperation: WalletOperation.RECOVER,
|
|
@@ -4071,15 +4152,11 @@ class DynamicWalletClient {
|
|
|
4071
4152
|
clientKeyShares = await this.getClientKeySharesFromStorage({
|
|
4072
4153
|
accountAddress
|
|
4073
4154
|
});
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
const encryptedStorageKey = `${accountAddress}${ENCRYPTED_SHARES_STORAGE_SUFFIX}`;
|
|
4080
|
-
const encryptedData = await this.storage.getItem(encryptedStorageKey);
|
|
4081
|
-
hasEncryptedShares = !!encryptedData;
|
|
4082
|
-
}
|
|
4155
|
+
hasEncryptedShares = await hasEncryptedSharesCached({
|
|
4156
|
+
accountAddress,
|
|
4157
|
+
backupInfo: walletData.clientKeySharesBackupInfo,
|
|
4158
|
+
storage: this.storage
|
|
4159
|
+
});
|
|
4083
4160
|
if (clientKeyShares.length === 0 && !hasEncryptedShares) {
|
|
4084
4161
|
throw new Error(`No key shares available for wallet ${accountAddress} after recovery`);
|
|
4085
4162
|
}
|
|
@@ -4100,15 +4177,15 @@ class DynamicWalletClient {
|
|
|
4100
4177
|
*/ async unlockWallet({ accountAddress, password, signedSessionId }) {
|
|
4101
4178
|
const dynamicRequestId = v4();
|
|
4102
4179
|
try {
|
|
4103
|
-
if (!this.
|
|
4180
|
+
if (!this.getWalletFromMap(accountAddress)) {
|
|
4104
4181
|
await this.getWallet({
|
|
4105
4182
|
accountAddress,
|
|
4106
4183
|
walletOperation: WalletOperation.NO_OPERATION,
|
|
4107
4184
|
signedSessionId
|
|
4108
4185
|
});
|
|
4109
4186
|
}
|
|
4110
|
-
const
|
|
4111
|
-
const encryptedStorageKey = `${
|
|
4187
|
+
const normalizedAccountAddress = normalizeAddress(accountAddress);
|
|
4188
|
+
const encryptedStorageKey = `${normalizedAccountAddress}${ENCRYPTED_SHARES_STORAGE_SUFFIX}`;
|
|
4112
4189
|
let encryptedData = await this.storage.getItem(encryptedStorageKey);
|
|
4113
4190
|
// If no cached encrypted shares, fetch via getWallet with RECOVER
|
|
4114
4191
|
if (!encryptedData) {
|
|
@@ -4122,24 +4199,41 @@ class DynamicWalletClient {
|
|
|
4122
4199
|
if (!encryptedData) {
|
|
4123
4200
|
throw new Error('No encrypted shares found for wallet');
|
|
4124
4201
|
}
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
const decryptedKeyShares = await Promise.all(data.keyShares.map((keyShare)=>this.decryptKeyShare({
|
|
4128
|
-
keyShare: keyShare.encryptedAccountCredential,
|
|
4129
|
-
password
|
|
4130
|
-
})));
|
|
4131
|
-
await this.setClientKeySharesToStorage({
|
|
4202
|
+
// Decrypt the requested wallet
|
|
4203
|
+
await this.decryptAndStoreWalletShares({
|
|
4132
4204
|
accountAddress,
|
|
4133
|
-
|
|
4205
|
+
encryptedData,
|
|
4206
|
+
password
|
|
4134
4207
|
});
|
|
4135
|
-
|
|
4136
|
-
|
|
4208
|
+
// Decrypt all other password-encrypted wallets with the same password
|
|
4209
|
+
const otherEncryptedWallets = Object.entries(this.walletMap).filter(([addr, w])=>{
|
|
4210
|
+
var _w_clientKeySharesBackupInfo;
|
|
4211
|
+
return addr !== normalizedAccountAddress && w.walletReadyState !== WalletReadyState.READY && ((_w_clientKeySharesBackupInfo = w.clientKeySharesBackupInfo) == null ? void 0 : _w_clientKeySharesBackupInfo.passwordEncrypted);
|
|
4137
4212
|
});
|
|
4213
|
+
await Promise.all(otherEncryptedWallets.map(async ([otherAddr])=>{
|
|
4214
|
+
try {
|
|
4215
|
+
const otherEncryptedStorageKey = `${otherAddr}${ENCRYPTED_SHARES_STORAGE_SUFFIX}`;
|
|
4216
|
+
const otherEncryptedData = await this.storage.getItem(otherEncryptedStorageKey);
|
|
4217
|
+
if (!otherEncryptedData) {
|
|
4218
|
+
return;
|
|
4219
|
+
}
|
|
4220
|
+
await this.decryptAndStoreWalletShares({
|
|
4221
|
+
accountAddress: otherAddr,
|
|
4222
|
+
encryptedData: otherEncryptedData,
|
|
4223
|
+
password
|
|
4224
|
+
});
|
|
4225
|
+
} catch (err) {
|
|
4226
|
+
this.logger.debug('[DynamicWaasWalletClient] Failed to unlock additional wallet', {
|
|
4227
|
+
accountAddress: otherAddr,
|
|
4228
|
+
error: err
|
|
4229
|
+
});
|
|
4230
|
+
}
|
|
4231
|
+
}));
|
|
4138
4232
|
await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
|
|
4139
4233
|
this.logger.debug('[DynamicWaasWalletClient] Wallet unlocked successfully', {
|
|
4140
4234
|
accountAddress
|
|
4141
4235
|
});
|
|
4142
|
-
return this.
|
|
4236
|
+
return this.getWalletFromMap(accountAddress);
|
|
4143
4237
|
} catch (error) {
|
|
4144
4238
|
logError({
|
|
4145
4239
|
message: 'Error in unlockWallet',
|
|
@@ -4152,6 +4246,22 @@ class DynamicWalletClient {
|
|
|
4152
4246
|
throw error;
|
|
4153
4247
|
}
|
|
4154
4248
|
}
|
|
4249
|
+
/**
|
|
4250
|
+
* Decrypts cached encrypted key shares for a wallet and stores them locally.
|
|
4251
|
+
*/ async decryptAndStoreWalletShares({ accountAddress, encryptedData, password }) {
|
|
4252
|
+
const data = JSON.parse(encryptedData);
|
|
4253
|
+
const decryptedKeyShares = await Promise.all(data.keyShares.map((keyShare)=>this.decryptKeyShare({
|
|
4254
|
+
keyShare: keyShare.encryptedAccountCredential,
|
|
4255
|
+
password
|
|
4256
|
+
})));
|
|
4257
|
+
await this.setClientKeySharesToStorage({
|
|
4258
|
+
accountAddress,
|
|
4259
|
+
clientKeyShares: decryptedKeyShares
|
|
4260
|
+
});
|
|
4261
|
+
this.updateWalletMap(accountAddress, {
|
|
4262
|
+
walletReadyState: WalletReadyState.READY
|
|
4263
|
+
});
|
|
4264
|
+
}
|
|
4155
4265
|
async getWallets() {
|
|
4156
4266
|
const dynamicRequestId = v4();
|
|
4157
4267
|
try {
|
|
@@ -4160,8 +4270,8 @@ class DynamicWalletClient {
|
|
|
4160
4270
|
this.userId = user.id;
|
|
4161
4271
|
const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
|
|
4162
4272
|
const wallets = waasWallets.map((vc)=>{
|
|
4163
|
-
var
|
|
4164
|
-
var
|
|
4273
|
+
var _this_getWalletFromMap, _vc_walletProperties, _vc_walletProperties1;
|
|
4274
|
+
var _this_getWalletFromMap_derivationPath;
|
|
4165
4275
|
return {
|
|
4166
4276
|
walletId: vc.id,
|
|
4167
4277
|
chainName: vc.chain,
|
|
@@ -4169,23 +4279,24 @@ class DynamicWalletClient {
|
|
|
4169
4279
|
clientKeySharesBackupInfo: getClientKeyShareBackupInfo({
|
|
4170
4280
|
walletProperties: vc.walletProperties || {}
|
|
4171
4281
|
}),
|
|
4172
|
-
derivationPath: (
|
|
4282
|
+
derivationPath: (_this_getWalletFromMap_derivationPath = (_this_getWalletFromMap = this.getWalletFromMap(vc.address)) == null ? void 0 : _this_getWalletFromMap.derivationPath) != null ? _this_getWalletFromMap_derivationPath : undefined,
|
|
4173
4283
|
thresholdSignatureScheme: (_vc_walletProperties = vc.walletProperties) == null ? void 0 : _vc_walletProperties.thresholdSignatureScheme,
|
|
4174
4284
|
addressType: (_vc_walletProperties1 = vc.walletProperties) == null ? void 0 : _vc_walletProperties1.addressType
|
|
4175
4285
|
};
|
|
4176
4286
|
});
|
|
4287
|
+
const existingWalletMap = this.walletMap;
|
|
4177
4288
|
this.walletMap = wallets.reduce((acc, wallet)=>{
|
|
4178
|
-
|
|
4179
|
-
const
|
|
4180
|
-
acc[
|
|
4289
|
+
const normalizedAddress = normalizeAddress(wallet.accountAddress);
|
|
4290
|
+
const existingWallet = existingWalletMap[normalizedAddress];
|
|
4291
|
+
acc[normalizedAddress] = {
|
|
4181
4292
|
walletId: wallet.walletId,
|
|
4182
4293
|
chainName: wallet.chainName,
|
|
4183
4294
|
accountAddress: wallet.accountAddress,
|
|
4184
4295
|
clientKeySharesBackupInfo: wallet.clientKeySharesBackupInfo,
|
|
4185
|
-
derivationPath:
|
|
4296
|
+
derivationPath: existingWallet == null ? void 0 : existingWallet.derivationPath,
|
|
4186
4297
|
thresholdSignatureScheme: wallet.thresholdSignatureScheme,
|
|
4187
4298
|
addressType: wallet.addressType,
|
|
4188
|
-
walletReadyState:
|
|
4299
|
+
walletReadyState: existingWallet == null ? void 0 : existingWallet.walletReadyState
|
|
4189
4300
|
};
|
|
4190
4301
|
return acc;
|
|
4191
4302
|
}, {});
|
|
@@ -4380,4 +4491,4 @@ class DynamicWalletClient {
|
|
|
4380
4491
|
DynamicWalletClient.rooms = {};
|
|
4381
4492
|
DynamicWalletClient.roomsInitializing = {};
|
|
4382
4493
|
|
|
4383
|
-
export { DynamicWalletClient, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_PUBLIC_KEY_MISMATCH, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, HEAVY_QUEUE_OPERATIONS, RECOVER_QUEUE_OPERATIONS, SIGN_QUEUE_OPERATIONS, WalletBusyError, WalletNotReadyError, cancelICloudAuth, createAddCloudProviderToExistingDelegationDistribution, createBackupData, createCloudProviderDistribution, createDelegationOnlyDistribution, createDelegationWithCloudProviderDistribution, createDynamicOnlyDistribution, deleteICloudBackup, downloadStringAsFile, extractPubkey, formatEvmMessage, formatMessage, getActiveCloudProviders, getBitcoinAddressTypeFromDerivationPath, getClientKeyShareBackupInfo, getClientKeyShareExportFileName, getGoogleOAuthAccountId, getICloudBackup, getMPCSignatureScheme, getMPCSigner, hasCloudProviderBackup, hasDelegatedBackup, initializeCloudKit, isBrowser, isHeavyQueueOperation, isHexString, isICloudAuthenticated, isPublicKeyMismatchError, isRecoverQueueOperation, isSignQueueOperation, listICloudBackups, mergeUniqueKeyShares, retryPromise, timeoutPromise };
|
|
4494
|
+
export { DynamicWalletClient, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_PUBLIC_KEY_MISMATCH, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, HEAVY_QUEUE_OPERATIONS, RECOVER_QUEUE_OPERATIONS, SIGN_QUEUE_OPERATIONS, WalletBusyError, WalletNotReadyError, cancelICloudAuth, createAddCloudProviderToExistingDelegationDistribution, createBackupData, createCloudProviderDistribution, createDelegationOnlyDistribution, createDelegationWithCloudProviderDistribution, createDynamicOnlyDistribution, deleteICloudBackup, downloadStringAsFile, extractPubkey, formatEvmMessage, formatMessage, getActiveCloudProviders, getBitcoinAddressTypeFromDerivationPath, getClientKeyShareBackupInfo, getClientKeyShareExportFileName, getGoogleOAuthAccountId, getICloudBackup, getMPCSignatureScheme, getMPCSigner, hasCloudProviderBackup, hasDelegatedBackup, hasEncryptedSharesCached, initializeCloudKit, isBrowser, isHeavyQueueOperation, isHexString, isICloudAuthenticated, isPublicKeyMismatchError, isRecoverQueueOperation, isSignQueueOperation, listICloudBackups, mergeUniqueKeyShares, retryPromise, timeoutPromise };
|