@dynamic-labs-wallet/browser 0.0.258 → 0.0.260
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 +454 -185
- package/index.esm.js +450 -187
- package/package.json +4 -3
- package/src/client.d.ts +37 -11
- package/src/client.d.ts.map +1 -1
- package/src/constants.d.ts +0 -1
- package/src/constants.d.ts.map +1 -1
- package/src/queue.d.ts +96 -0
- package/src/queue.d.ts.map +1 -0
- package/src/types.d.ts +39 -1
- package/src/types.d.ts.map +1 -1
package/index.cjs.js
CHANGED
|
@@ -9,6 +9,7 @@ var sdkApiCore = require('@dynamic-labs/sdk-api-core');
|
|
|
9
9
|
var loadArgon2idWasm = require('argon2id');
|
|
10
10
|
var axios = require('axios');
|
|
11
11
|
var createHttpError = require('http-errors');
|
|
12
|
+
var PQueue = require('p-queue');
|
|
12
13
|
|
|
13
14
|
function _extends() {
|
|
14
15
|
_extends = Object.assign || function assign(target) {
|
|
@@ -492,7 +493,6 @@ const SIGNED_SESSION_ID_MIN_VERSION_BY_NAMESPACE = {
|
|
|
492
493
|
};
|
|
493
494
|
const ROOM_EXPIRATION_TIME = 1000 * 60 * 10; // 10 minutes
|
|
494
495
|
const ROOM_CACHE_COUNT = 5;
|
|
495
|
-
const WALLET_BUSY_LOCK_TIMEOUT_MS = 20000; // 20 seconds
|
|
496
496
|
|
|
497
497
|
const ERROR_KEYGEN_FAILED = '[DynamicWaasWalletClient]: Error with keygen';
|
|
498
498
|
const ERROR_CREATE_WALLET_ACCOUNT = '[DynamicWaasWalletClient]: Error creating wallet account';
|
|
@@ -1111,6 +1111,341 @@ const initializeCloudKit = async (config, signInButtonId, onSignInRequired, onSi
|
|
|
1111
1111
|
}
|
|
1112
1112
|
};
|
|
1113
1113
|
|
|
1114
|
+
class WalletNotReadyError extends Error {
|
|
1115
|
+
constructor(accountAddress, walletReadyState){
|
|
1116
|
+
super(`Wallet ${accountAddress} is not ready and requires password to be unlocked`);
|
|
1117
|
+
this.name = 'WalletNotReadyError';
|
|
1118
|
+
this.accountAddress = accountAddress;
|
|
1119
|
+
this.walletReadyState = walletReadyState;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Set of allowed heavy queue operations for runtime validation.
|
|
1124
|
+
*/ const HEAVY_QUEUE_OPERATIONS = new Set([
|
|
1125
|
+
core.WalletOperation.REFRESH,
|
|
1126
|
+
core.WalletOperation.RESHARE,
|
|
1127
|
+
core.WalletOperation.RECOVER
|
|
1128
|
+
]);
|
|
1129
|
+
/**
|
|
1130
|
+
* Type guard to validate that an operation is a valid heavy queue operation.
|
|
1131
|
+
*/ const isHeavyQueueOperation = (operation)=>HEAVY_QUEUE_OPERATIONS.has(operation);
|
|
1132
|
+
/**
|
|
1133
|
+
* Set of allowed sign queue operations for runtime validation.
|
|
1134
|
+
*/ const SIGN_QUEUE_OPERATIONS = new Set([
|
|
1135
|
+
core.WalletOperation.SIGN_MESSAGE,
|
|
1136
|
+
core.WalletOperation.SIGN_TRANSACTION
|
|
1137
|
+
]);
|
|
1138
|
+
/**
|
|
1139
|
+
* Type guard to validate that an operation is a valid sign queue operation.
|
|
1140
|
+
*/ const isSignQueueOperation = (operation)=>SIGN_QUEUE_OPERATIONS.has(operation);
|
|
1141
|
+
/**
|
|
1142
|
+
* Set of allowed recover queue operations for runtime validation.
|
|
1143
|
+
*/ const RECOVER_QUEUE_OPERATIONS = new Set([
|
|
1144
|
+
core.WalletOperation.RECOVER
|
|
1145
|
+
]);
|
|
1146
|
+
/**
|
|
1147
|
+
* Type guard to validate that an operation is a valid recover queue operation.
|
|
1148
|
+
*/ const isRecoverQueueOperation = (operation)=>RECOVER_QUEUE_OPERATIONS.has(operation);
|
|
1149
|
+
class WalletBusyError extends Error {
|
|
1150
|
+
constructor(accountAddress, operation){
|
|
1151
|
+
super(`Wallet ${accountAddress} is currently performing a ${operation} operation`);
|
|
1152
|
+
this.name = 'WalletBusyError';
|
|
1153
|
+
this.operation = operation;
|
|
1154
|
+
this.accountAddress = accountAddress;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Creates distribution where shares go to specified cloud providers
|
|
1159
|
+
* Last share goes to cloud providers, rest to Dynamic backend
|
|
1160
|
+
* @param providers - Array of cloud providers to backup to
|
|
1161
|
+
* @param allShares - All key shares to distribute
|
|
1162
|
+
*/ const createCloudProviderDistribution = ({ providers, allShares })=>{
|
|
1163
|
+
const cloudProviderShares = {};
|
|
1164
|
+
// Last share goes to cloud providers, rest to Dynamic
|
|
1165
|
+
const sharesForCloud = allShares.slice(-1);
|
|
1166
|
+
providers.forEach((provider)=>{
|
|
1167
|
+
cloudProviderShares[provider] = sharesForCloud;
|
|
1168
|
+
});
|
|
1169
|
+
return {
|
|
1170
|
+
clientShares: allShares.slice(0, -1),
|
|
1171
|
+
cloudProviderShares
|
|
1172
|
+
};
|
|
1173
|
+
};
|
|
1174
|
+
/**
|
|
1175
|
+
* Creates distribution with delegation + cloud backup
|
|
1176
|
+
* Client shares backed up to Dynamic AND cloud providers
|
|
1177
|
+
* Delegated share goes to webhook
|
|
1178
|
+
*/ const createDelegationWithCloudProviderDistribution = ({ providers, existingShares, delegatedShare })=>{
|
|
1179
|
+
const cloudProviderShares = {};
|
|
1180
|
+
providers.forEach((provider)=>{
|
|
1181
|
+
cloudProviderShares[provider] = existingShares;
|
|
1182
|
+
});
|
|
1183
|
+
return {
|
|
1184
|
+
clientShares: existingShares,
|
|
1185
|
+
cloudProviderShares,
|
|
1186
|
+
delegatedShare
|
|
1187
|
+
};
|
|
1188
|
+
};
|
|
1189
|
+
/**
|
|
1190
|
+
* Creates distribution for adding cloud backup to existing delegation
|
|
1191
|
+
* Client shares go to both Dynamic AND cloud providers
|
|
1192
|
+
* delegatedShare is undefined - we don't re-publish, but preserve the location
|
|
1193
|
+
*/ const createAddCloudProviderToExistingDelegationDistribution = ({ providers, clientShares })=>{
|
|
1194
|
+
const cloudProviderShares = {};
|
|
1195
|
+
providers.forEach((provider)=>{
|
|
1196
|
+
cloudProviderShares[provider] = clientShares;
|
|
1197
|
+
});
|
|
1198
|
+
return {
|
|
1199
|
+
clientShares,
|
|
1200
|
+
cloudProviderShares
|
|
1201
|
+
};
|
|
1202
|
+
};
|
|
1203
|
+
/**
|
|
1204
|
+
* Checks if wallet has backup on any of the specified providers
|
|
1205
|
+
*/ const hasCloudProviderBackup = (backupInfo, providers)=>{
|
|
1206
|
+
if (!(backupInfo == null ? void 0 : backupInfo.backups)) return false;
|
|
1207
|
+
return providers.some((provider)=>{
|
|
1208
|
+
var _backupInfo_backups_provider;
|
|
1209
|
+
var _backupInfo_backups_provider_length;
|
|
1210
|
+
return ((_backupInfo_backups_provider_length = (_backupInfo_backups_provider = backupInfo.backups[provider]) == null ? void 0 : _backupInfo_backups_provider.length) != null ? _backupInfo_backups_provider_length : 0) > 0;
|
|
1211
|
+
});
|
|
1212
|
+
};
|
|
1213
|
+
/**
|
|
1214
|
+
* Gets all cloud providers that have backups for this wallet
|
|
1215
|
+
*/ const getActiveCloudProviders = (backupInfo)=>{
|
|
1216
|
+
if (!(backupInfo == null ? void 0 : backupInfo.backups)) return [];
|
|
1217
|
+
return Object.entries(backupInfo.backups).filter(([location, backups])=>location !== core.BackupLocation.DYNAMIC && location !== core.BackupLocation.DELEGATED && backups.length > 0).map(([location])=>location);
|
|
1218
|
+
};
|
|
1219
|
+
const createDelegationOnlyDistribution = ({ existingShares, delegatedShare })=>({
|
|
1220
|
+
clientShares: existingShares,
|
|
1221
|
+
cloudProviderShares: {},
|
|
1222
|
+
delegatedShare
|
|
1223
|
+
});
|
|
1224
|
+
const createDynamicOnlyDistribution = ({ allShares })=>({
|
|
1225
|
+
clientShares: allShares,
|
|
1226
|
+
cloudProviderShares: {}
|
|
1227
|
+
});
|
|
1228
|
+
const hasDelegatedBackup = (backupInfo)=>{
|
|
1229
|
+
var _backupInfo_backups_BackupLocation_DELEGATED, _backupInfo_backups;
|
|
1230
|
+
var _backupInfo_backups_BackupLocation_DELEGATED_length;
|
|
1231
|
+
return ((_backupInfo_backups_BackupLocation_DELEGATED_length = backupInfo == null ? void 0 : (_backupInfo_backups = backupInfo.backups) == null ? void 0 : (_backupInfo_backups_BackupLocation_DELEGATED = _backupInfo_backups[core.BackupLocation.DELEGATED]) == null ? void 0 : _backupInfo_backups_BackupLocation_DELEGATED.length) != null ? _backupInfo_backups_BackupLocation_DELEGATED_length : 0) > 0;
|
|
1232
|
+
};
|
|
1233
|
+
|
|
1234
|
+
/**
|
|
1235
|
+
* Queue manager for wallet operations.
|
|
1236
|
+
* Manages heavy operation queues (refresh/reshare/recover) and sign queues per wallet.
|
|
1237
|
+
*/ class WalletQueueManager {
|
|
1238
|
+
/**
|
|
1239
|
+
* Get or create the heavy operation queue for a wallet.
|
|
1240
|
+
* Heavy operations (refresh/reshare/recover) run with concurrency=1.
|
|
1241
|
+
*/ static getHeavyOpQueue(accountAddress) {
|
|
1242
|
+
if (!this.heavyOpQueues.has(accountAddress)) {
|
|
1243
|
+
const queue = new PQueue({
|
|
1244
|
+
concurrency: 1,
|
|
1245
|
+
timeout: 20000
|
|
1246
|
+
});
|
|
1247
|
+
queue.on('error', (error)=>{
|
|
1248
|
+
logger.error('[WalletQueueManager] Heavy operation queue error', {
|
|
1249
|
+
accountAddress,
|
|
1250
|
+
error: error instanceof Error ? error.message : error
|
|
1251
|
+
});
|
|
1252
|
+
});
|
|
1253
|
+
this.heavyOpQueues.set(accountAddress, queue);
|
|
1254
|
+
}
|
|
1255
|
+
return this.heavyOpQueues.get(accountAddress);
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Get or create the sign queue for a wallet.
|
|
1259
|
+
* Sign operations run with unlimited concurrency (they're read-only on key shares).
|
|
1260
|
+
*/ static getSignQueue(accountAddress) {
|
|
1261
|
+
if (!this.signQueues.has(accountAddress)) {
|
|
1262
|
+
const queue = new PQueue({
|
|
1263
|
+
concurrency: Infinity,
|
|
1264
|
+
timeout: 20000
|
|
1265
|
+
});
|
|
1266
|
+
queue.on('error', (error)=>{
|
|
1267
|
+
logger.error('[WalletQueueManager] Sign queue error', {
|
|
1268
|
+
accountAddress,
|
|
1269
|
+
error: error instanceof Error ? error.message : error
|
|
1270
|
+
});
|
|
1271
|
+
});
|
|
1272
|
+
this.signQueues.set(accountAddress, queue);
|
|
1273
|
+
}
|
|
1274
|
+
return this.signQueues.get(accountAddress);
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Check if wallet has heavy operations in progress
|
|
1278
|
+
*/ static isHeavyOpInProgress(accountAddress) {
|
|
1279
|
+
const queue = this.heavyOpQueues.get(accountAddress);
|
|
1280
|
+
return queue ? queue.size > 0 || queue.pending > 0 : false;
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Check if wallet has operations in any queue (heavy or sign)
|
|
1284
|
+
*/ static isWalletBusy(accountAddress) {
|
|
1285
|
+
const heavyQueue = this.heavyOpQueues.get(accountAddress);
|
|
1286
|
+
const signQueue = this.signQueues.get(accountAddress);
|
|
1287
|
+
const heavyBusy = heavyQueue ? heavyQueue.size > 0 || heavyQueue.pending > 0 : false;
|
|
1288
|
+
const signBusy = signQueue ? signQueue.size > 0 || signQueue.pending > 0 : false;
|
|
1289
|
+
return heavyBusy || signBusy;
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Check if recovery is in progress for a wallet
|
|
1293
|
+
*/ static isRecoveryInProgress(accountAddress) {
|
|
1294
|
+
return this.pendingRecoveryPromises.has(accountAddress);
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Get existing pending recovery promise if one exists
|
|
1298
|
+
*/ static getPendingRecoveryPromise(accountAddress) {
|
|
1299
|
+
return this.pendingRecoveryPromises.get(accountAddress);
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Track a pending recovery promise
|
|
1303
|
+
*/ static setPendingRecoveryPromise(accountAddress, promise) {
|
|
1304
|
+
this.pendingRecoveryPromises.set(accountAddress, promise);
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Clear pending recovery promise for a wallet
|
|
1308
|
+
*/ static clearPendingRecoveryPromise(accountAddress) {
|
|
1309
|
+
this.pendingRecoveryPromises.delete(accountAddress);
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Queue a heavy operation with type validation.
|
|
1313
|
+
* Ensures only valid heavy operations (REFRESH, RESHARE, RECOVER) can be queued.
|
|
1314
|
+
* Waits for sign operations to complete before executing.
|
|
1315
|
+
*
|
|
1316
|
+
* @param accountAddress - The wallet address
|
|
1317
|
+
* @param operation - The operation type (must be a valid HeavyQueueOperation)
|
|
1318
|
+
* @param callback - The operation to execute
|
|
1319
|
+
* @throws Error if operation is not a valid heavy queue operation
|
|
1320
|
+
*/ static async queueHeavyOperation(accountAddress, operation, callback) {
|
|
1321
|
+
// Runtime validation to catch any type assertion bypasses
|
|
1322
|
+
if (!isHeavyQueueOperation(operation)) {
|
|
1323
|
+
throw new Error(`Invalid heavy queue operation: ${operation}. Must be REFRESH, RESHARE, or RECOVER.`);
|
|
1324
|
+
}
|
|
1325
|
+
const heavyQueue = this.getHeavyOpQueue(accountAddress);
|
|
1326
|
+
const signQueue = this.getSignQueue(accountAddress);
|
|
1327
|
+
// Wait for any in-progress sign operations to complete
|
|
1328
|
+
await signQueue.onIdle();
|
|
1329
|
+
// Add to heavy queue - will wait if other heavy operations are in progress
|
|
1330
|
+
return heavyQueue.add(async ()=>{
|
|
1331
|
+
this.walletsWithActiveHeavyOp.set(accountAddress, true);
|
|
1332
|
+
try {
|
|
1333
|
+
return await callback();
|
|
1334
|
+
} finally{
|
|
1335
|
+
this.walletsWithActiveHeavyOp.delete(accountAddress);
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* Queue a sign operation with type validation.
|
|
1341
|
+
* Ensures only valid sign operations (SIGN_MESSAGE, SIGN_TRANSACTION) can be queued.
|
|
1342
|
+
* Waits for heavy operations to complete before executing.
|
|
1343
|
+
* Allows concurrent sign operations.
|
|
1344
|
+
*
|
|
1345
|
+
* @param accountAddress - The wallet address
|
|
1346
|
+
* @param operation - The operation type (must be a valid SignQueueOperation)
|
|
1347
|
+
* @param callback - The sign operation to execute
|
|
1348
|
+
* @throws Error if operation is not a valid sign queue operation
|
|
1349
|
+
* @returns Promise resolving to the sign result
|
|
1350
|
+
*/ static async queueSignOperation(accountAddress, operation, callback) {
|
|
1351
|
+
// Runtime validation to catch any type assertion bypasses
|
|
1352
|
+
if (!isSignQueueOperation(operation)) {
|
|
1353
|
+
throw new Error(`Invalid sign queue operation: ${operation}. Must be SIGN_MESSAGE or SIGN_TRANSACTION.`);
|
|
1354
|
+
}
|
|
1355
|
+
const heavyQueue = this.getHeavyOpQueue(accountAddress);
|
|
1356
|
+
const signQueue = this.getSignQueue(accountAddress);
|
|
1357
|
+
// Wait for any running heavy operations to complete before signing.
|
|
1358
|
+
// This prevents signing with stale key shares during refresh/reshare/recover.
|
|
1359
|
+
// Note: There's a small race window between this check and adding to signQueue,
|
|
1360
|
+
// but if a collision occurs, the MPC protocol will detect the public key mismatch
|
|
1361
|
+
// and our recovery handler will automatically retry with fresh key shares.
|
|
1362
|
+
if (heavyQueue.pending > 0 || heavyQueue.size > 0) {
|
|
1363
|
+
await heavyQueue.onIdle();
|
|
1364
|
+
}
|
|
1365
|
+
// Add to sign queue - allows concurrent signing
|
|
1366
|
+
return signQueue.add(async ()=>{
|
|
1367
|
+
this.walletsWithActiveSignOp.set(accountAddress, true);
|
|
1368
|
+
try {
|
|
1369
|
+
return await callback();
|
|
1370
|
+
} finally{
|
|
1371
|
+
this.walletsWithActiveSignOp.delete(accountAddress);
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Queue a recovery operation with deduplication and type validation.
|
|
1377
|
+
* If a recovery is already in progress for the wallet, returns that promise.
|
|
1378
|
+
* If called from within a heavy operation, executes directly to avoid deadlock.
|
|
1379
|
+
*
|
|
1380
|
+
* @param accountAddress - The wallet address
|
|
1381
|
+
* @param operation - The operation type (must be RECOVER)
|
|
1382
|
+
* @param callback - The recovery operation to execute
|
|
1383
|
+
* @throws Error if operation is not a valid recover queue operation
|
|
1384
|
+
* @returns Promise resolving to the recovery result
|
|
1385
|
+
*/ static async queueRecoverOperation(accountAddress, operation, callback) {
|
|
1386
|
+
// Runtime validation to catch any type assertion bypasses
|
|
1387
|
+
if (!isRecoverQueueOperation(operation)) {
|
|
1388
|
+
throw new Error(`Invalid recover queue operation: ${operation}. Must be RECOVER.`);
|
|
1389
|
+
}
|
|
1390
|
+
// Deduplication: if recovery already in progress, return that promise
|
|
1391
|
+
const existing = this.getPendingRecoveryPromise(accountAddress);
|
|
1392
|
+
if (existing) {
|
|
1393
|
+
logger.debug(`[WalletQueueManager] Recovery already in progress for ${accountAddress}, returning existing promise`);
|
|
1394
|
+
return existing;
|
|
1395
|
+
}
|
|
1396
|
+
// If we're already inside a heavy or sign operation for this wallet, execute directly
|
|
1397
|
+
// to avoid deadlock (heavy queue has concurrency=1, sign queue waits for idle)
|
|
1398
|
+
if (this.walletsWithActiveHeavyOp.get(accountAddress) || this.walletsWithActiveSignOp.get(accountAddress)) {
|
|
1399
|
+
logger.debug(`[WalletQueueManager] Recovery called from within active op for ${accountAddress}, executing directly`);
|
|
1400
|
+
const directPromise = (async ()=>{
|
|
1401
|
+
try {
|
|
1402
|
+
return await callback();
|
|
1403
|
+
} finally{
|
|
1404
|
+
this.clearPendingRecoveryPromise(accountAddress);
|
|
1405
|
+
}
|
|
1406
|
+
})();
|
|
1407
|
+
this.setPendingRecoveryPromise(accountAddress, directPromise);
|
|
1408
|
+
return directPromise;
|
|
1409
|
+
}
|
|
1410
|
+
const heavyQueue = this.getHeavyOpQueue(accountAddress);
|
|
1411
|
+
const signQueue = this.getSignQueue(accountAddress);
|
|
1412
|
+
// Create promise and track it
|
|
1413
|
+
const recoveryPromise = (async ()=>{
|
|
1414
|
+
// Wait for any in-progress sign operations to complete
|
|
1415
|
+
await signQueue.onIdle();
|
|
1416
|
+
// Add to heavy queue - will wait if other heavy operations are in progress
|
|
1417
|
+
return heavyQueue.add(async ()=>{
|
|
1418
|
+
try {
|
|
1419
|
+
return await callback();
|
|
1420
|
+
} finally{
|
|
1421
|
+
// Clean up tracking when done
|
|
1422
|
+
this.clearPendingRecoveryPromise(accountAddress);
|
|
1423
|
+
}
|
|
1424
|
+
});
|
|
1425
|
+
})();
|
|
1426
|
+
// Track this recovery
|
|
1427
|
+
this.setPendingRecoveryPromise(accountAddress, recoveryPromise);
|
|
1428
|
+
return recoveryPromise;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Reset static state for testing purposes.
|
|
1432
|
+
* This clears all wallet queues and pending recovery promises.
|
|
1433
|
+
*/ static resetForTesting() {
|
|
1434
|
+
this.heavyOpQueues.clear();
|
|
1435
|
+
this.signQueues.clear();
|
|
1436
|
+
this.pendingRecoveryPromises.clear();
|
|
1437
|
+
this.walletsWithActiveHeavyOp.clear();
|
|
1438
|
+
this.walletsWithActiveSignOp.clear();
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
/** Heavy operation queues per wallet address. These run with concurrency=1. */ WalletQueueManager.heavyOpQueues = new Map();
|
|
1442
|
+
/** Sign queues per wallet address. These allow concurrent operations. */ WalletQueueManager.signQueues = new Map();
|
|
1443
|
+
/** Track pending recovery promises for deduplication */ WalletQueueManager.pendingRecoveryPromises = new Map();
|
|
1444
|
+
/** Track which wallets are currently executing inside a heavy operation.
|
|
1445
|
+
* Used to prevent deadlocks when recovery is called from within a heavy op. */ WalletQueueManager.walletsWithActiveHeavyOp = new Map();
|
|
1446
|
+
/** Track which wallets are currently executing inside a sign operation.
|
|
1447
|
+
* Used to prevent deadlocks when recovery is called from within a sign op. */ WalletQueueManager.walletsWithActiveSignOp = new Map();
|
|
1448
|
+
|
|
1114
1449
|
const ALG_LABEL_RSA = 'HYBRID-RSA-AES-256';
|
|
1115
1450
|
/**
|
|
1116
1451
|
* Convert base64 to base64url encoding
|
|
@@ -1258,99 +1593,6 @@ const localStorageWriteTest = {
|
|
|
1258
1593
|
}
|
|
1259
1594
|
});
|
|
1260
1595
|
|
|
1261
|
-
class WalletNotReadyError extends Error {
|
|
1262
|
-
constructor(accountAddress, walletReadyState){
|
|
1263
|
-
super(`Wallet ${accountAddress} is not ready and requires password to be unlocked`);
|
|
1264
|
-
this.name = 'WalletNotReadyError';
|
|
1265
|
-
this.accountAddress = accountAddress;
|
|
1266
|
-
this.walletReadyState = walletReadyState;
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
class WalletBusyError extends Error {
|
|
1270
|
-
constructor(accountAddress, operation){
|
|
1271
|
-
super(`Wallet ${accountAddress} is currently performing a ${operation} operation`);
|
|
1272
|
-
this.name = 'WalletBusyError';
|
|
1273
|
-
this.operation = operation;
|
|
1274
|
-
this.accountAddress = accountAddress;
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
/**
|
|
1278
|
-
* Creates distribution where shares go to specified cloud providers
|
|
1279
|
-
* Last share goes to cloud providers, rest to Dynamic backend
|
|
1280
|
-
* @param providers - Array of cloud providers to backup to
|
|
1281
|
-
* @param allShares - All key shares to distribute
|
|
1282
|
-
*/ const createCloudProviderDistribution = ({ providers, allShares })=>{
|
|
1283
|
-
const cloudProviderShares = {};
|
|
1284
|
-
// Last share goes to cloud providers, rest to Dynamic
|
|
1285
|
-
const sharesForCloud = allShares.slice(-1);
|
|
1286
|
-
providers.forEach((provider)=>{
|
|
1287
|
-
cloudProviderShares[provider] = sharesForCloud;
|
|
1288
|
-
});
|
|
1289
|
-
return {
|
|
1290
|
-
clientShares: allShares.slice(0, -1),
|
|
1291
|
-
cloudProviderShares
|
|
1292
|
-
};
|
|
1293
|
-
};
|
|
1294
|
-
/**
|
|
1295
|
-
* Creates distribution with delegation + cloud backup
|
|
1296
|
-
* Client shares backed up to Dynamic AND cloud providers
|
|
1297
|
-
* Delegated share goes to webhook
|
|
1298
|
-
*/ const createDelegationWithCloudProviderDistribution = ({ providers, existingShares, delegatedShare })=>{
|
|
1299
|
-
const cloudProviderShares = {};
|
|
1300
|
-
providers.forEach((provider)=>{
|
|
1301
|
-
cloudProviderShares[provider] = existingShares;
|
|
1302
|
-
});
|
|
1303
|
-
return {
|
|
1304
|
-
clientShares: existingShares,
|
|
1305
|
-
cloudProviderShares,
|
|
1306
|
-
delegatedShare
|
|
1307
|
-
};
|
|
1308
|
-
};
|
|
1309
|
-
/**
|
|
1310
|
-
* Creates distribution for adding cloud backup to existing delegation
|
|
1311
|
-
* Client shares go to both Dynamic AND cloud providers
|
|
1312
|
-
* delegatedShare is undefined - we don't re-publish, but preserve the location
|
|
1313
|
-
*/ const createAddCloudProviderToExistingDelegationDistribution = ({ providers, clientShares })=>{
|
|
1314
|
-
const cloudProviderShares = {};
|
|
1315
|
-
providers.forEach((provider)=>{
|
|
1316
|
-
cloudProviderShares[provider] = clientShares;
|
|
1317
|
-
});
|
|
1318
|
-
return {
|
|
1319
|
-
clientShares,
|
|
1320
|
-
cloudProviderShares
|
|
1321
|
-
};
|
|
1322
|
-
};
|
|
1323
|
-
/**
|
|
1324
|
-
* Checks if wallet has backup on any of the specified providers
|
|
1325
|
-
*/ const hasCloudProviderBackup = (backupInfo, providers)=>{
|
|
1326
|
-
if (!(backupInfo == null ? void 0 : backupInfo.backups)) return false;
|
|
1327
|
-
return providers.some((provider)=>{
|
|
1328
|
-
var _backupInfo_backups_provider;
|
|
1329
|
-
var _backupInfo_backups_provider_length;
|
|
1330
|
-
return ((_backupInfo_backups_provider_length = (_backupInfo_backups_provider = backupInfo.backups[provider]) == null ? void 0 : _backupInfo_backups_provider.length) != null ? _backupInfo_backups_provider_length : 0) > 0;
|
|
1331
|
-
});
|
|
1332
|
-
};
|
|
1333
|
-
/**
|
|
1334
|
-
* Gets all cloud providers that have backups for this wallet
|
|
1335
|
-
*/ const getActiveCloudProviders = (backupInfo)=>{
|
|
1336
|
-
if (!(backupInfo == null ? void 0 : backupInfo.backups)) return [];
|
|
1337
|
-
return Object.entries(backupInfo.backups).filter(([location, backups])=>location !== core.BackupLocation.DYNAMIC && location !== core.BackupLocation.DELEGATED && backups.length > 0).map(([location])=>location);
|
|
1338
|
-
};
|
|
1339
|
-
const createDelegationOnlyDistribution = ({ existingShares, delegatedShare })=>({
|
|
1340
|
-
clientShares: existingShares,
|
|
1341
|
-
cloudProviderShares: {},
|
|
1342
|
-
delegatedShare
|
|
1343
|
-
});
|
|
1344
|
-
const createDynamicOnlyDistribution = ({ allShares })=>({
|
|
1345
|
-
clientShares: allShares,
|
|
1346
|
-
cloudProviderShares: {}
|
|
1347
|
-
});
|
|
1348
|
-
const hasDelegatedBackup = (backupInfo)=>{
|
|
1349
|
-
var _backupInfo_backups_BackupLocation_DELEGATED, _backupInfo_backups;
|
|
1350
|
-
var _backupInfo_backups_BackupLocation_DELEGATED_length;
|
|
1351
|
-
return ((_backupInfo_backups_BackupLocation_DELEGATED_length = backupInfo == null ? void 0 : (_backupInfo_backups = backupInfo.backups) == null ? void 0 : (_backupInfo_backups_BackupLocation_DELEGATED = _backupInfo_backups[core.BackupLocation.DELEGATED]) == null ? void 0 : _backupInfo_backups_BackupLocation_DELEGATED.length) != null ? _backupInfo_backups_BackupLocation_DELEGATED_length : 0) > 0;
|
|
1352
|
-
};
|
|
1353
|
-
|
|
1354
1596
|
/**
|
|
1355
1597
|
* Determines the recovery state of a wallet based on backup info and local shares.
|
|
1356
1598
|
*
|
|
@@ -1403,48 +1645,27 @@ class DynamicWalletClient {
|
|
|
1403
1645
|
});
|
|
1404
1646
|
}
|
|
1405
1647
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1648
|
+
/**
|
|
1649
|
+
* Check if wallet has heavy operations in progress
|
|
1650
|
+
*/ static isHeavyOpInProgress(accountAddress) {
|
|
1651
|
+
return WalletQueueManager.isHeavyOpInProgress(accountAddress);
|
|
1408
1652
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1653
|
+
/**
|
|
1654
|
+
* Check if wallet has operations in any queue (heavy or sign)
|
|
1655
|
+
*/ static isWalletBusy(accountAddress) {
|
|
1656
|
+
return WalletQueueManager.isWalletBusy(accountAddress);
|
|
1411
1657
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
}
|
|
1417
|
-
const currentOperation = DynamicWalletClient.walletBusyMap[accountAddress];
|
|
1418
|
-
this.logger.debug(`[DynamicWaasWalletClient] Waiting for wallet ${accountAddress} to complete ${currentOperation} operation`);
|
|
1419
|
-
try {
|
|
1420
|
-
await busyPromise;
|
|
1421
|
-
} catch (e) {
|
|
1422
|
-
// Intentionally swallowing the error - we proceed with the queued operation regardless of whether the previous one succeeded or failed
|
|
1423
|
-
this.logger.debug(`[DynamicWaasWalletClient] Previous ${currentOperation} operation on wallet ${accountAddress} failed, proceeding with queued operation`);
|
|
1424
|
-
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Check if recovery is in progress for a wallet
|
|
1660
|
+
*/ static isRecoveryInProgress(accountAddress) {
|
|
1661
|
+
return WalletQueueManager.isRecoveryInProgress(accountAddress);
|
|
1425
1662
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
const executionPromise = (async ()=>{
|
|
1433
|
-
try {
|
|
1434
|
-
return await Promise.race([
|
|
1435
|
-
fn(),
|
|
1436
|
-
timeoutPromise({
|
|
1437
|
-
timeInMs: timeoutMs,
|
|
1438
|
-
activity: `${operation} operation`
|
|
1439
|
-
})
|
|
1440
|
-
]);
|
|
1441
|
-
} finally{
|
|
1442
|
-
delete DynamicWalletClient.walletBusyMap[accountAddress];
|
|
1443
|
-
delete DynamicWalletClient.walletBusyPromiseMap[accountAddress];
|
|
1444
|
-
}
|
|
1445
|
-
})();
|
|
1446
|
-
DynamicWalletClient.walletBusyPromiseMap[accountAddress] = executionPromise.then(()=>undefined, ()=>undefined);
|
|
1447
|
-
return executionPromise;
|
|
1663
|
+
/**
|
|
1664
|
+
* Reset static state for testing purposes.
|
|
1665
|
+
* This clears all wallet queues and in-flight recovery tracking.
|
|
1666
|
+
* @internal For testing only
|
|
1667
|
+
*/ static resetStaticState() {
|
|
1668
|
+
WalletQueueManager.resetForTesting();
|
|
1448
1669
|
}
|
|
1449
1670
|
getAuthMode() {
|
|
1450
1671
|
return this.authMode;
|
|
@@ -1965,7 +2186,8 @@ class DynamicWalletClient {
|
|
|
1965
2186
|
dynamicRequestId
|
|
1966
2187
|
}, this.getTraceContext(traceContext)));
|
|
1967
2188
|
// Recover key shares from backup (don't auto-store, we'll overwrite below)
|
|
1968
|
-
|
|
2189
|
+
// Use internal method directly to bypass queueing - we're already inside a queued operation
|
|
2190
|
+
const recoveredKeyShares = await this.internalRecoverEncryptedBackupByWallet({
|
|
1969
2191
|
accountAddress,
|
|
1970
2192
|
password: password != null ? password : this.environmentId,
|
|
1971
2193
|
walletOperation,
|
|
@@ -1988,24 +2210,23 @@ class DynamicWalletClient {
|
|
|
1988
2210
|
}
|
|
1989
2211
|
//todo: need to modify with imported flag
|
|
1990
2212
|
async sign({ accountAddress, message, chainName, password = undefined, isFormatted = false, signedSessionId, mfaToken, context, onError, traceContext, bitcoinConfig }) {
|
|
1991
|
-
return this.internalSign({
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2213
|
+
return WalletQueueManager.queueSignOperation(accountAddress, core.WalletOperation.SIGN_MESSAGE, ()=>this.internalSign({
|
|
2214
|
+
accountAddress,
|
|
2215
|
+
message,
|
|
2216
|
+
chainName,
|
|
2217
|
+
password,
|
|
2218
|
+
isFormatted,
|
|
2219
|
+
signedSessionId,
|
|
2220
|
+
mfaToken,
|
|
2221
|
+
context,
|
|
2222
|
+
onError,
|
|
2223
|
+
traceContext,
|
|
2224
|
+
bitcoinConfig
|
|
2225
|
+
}));
|
|
2004
2226
|
}
|
|
2005
2227
|
async internalSign({ accountAddress, message, chainName, password = undefined, isFormatted = false, signedSessionId, mfaToken, context, onError, traceContext, bitcoinConfig, hasAttemptedKeyShareRecovery = false }) {
|
|
2006
2228
|
const dynamicRequestId = uuid.v4();
|
|
2007
2229
|
try {
|
|
2008
|
-
await this.waitForWalletNotBusy(accountAddress);
|
|
2009
2230
|
await this.verifyPassword({
|
|
2010
2231
|
accountAddress,
|
|
2011
2232
|
password,
|
|
@@ -2130,18 +2351,14 @@ class DynamicWalletClient {
|
|
|
2130
2351
|
}
|
|
2131
2352
|
}
|
|
2132
2353
|
async refreshWalletAccountShares({ accountAddress, chainName, password = undefined, signedSessionId, mfaToken, traceContext }) {
|
|
2133
|
-
return this.
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
mfaToken,
|
|
2142
|
-
traceContext
|
|
2143
|
-
})
|
|
2144
|
-
});
|
|
2354
|
+
return WalletQueueManager.queueHeavyOperation(accountAddress, core.WalletOperation.REFRESH, ()=>this.internalRefreshWalletAccountShares({
|
|
2355
|
+
accountAddress,
|
|
2356
|
+
chainName,
|
|
2357
|
+
password,
|
|
2358
|
+
signedSessionId,
|
|
2359
|
+
mfaToken,
|
|
2360
|
+
traceContext
|
|
2361
|
+
}));
|
|
2145
2362
|
}
|
|
2146
2363
|
/**
|
|
2147
2364
|
* Gets the Bitcoin config for MPC operations by looking up addressType
|
|
@@ -2287,9 +2504,11 @@ class DynamicWalletClient {
|
|
|
2287
2504
|
* }>} Object containing new and existing client keygen results, IDs and shares
|
|
2288
2505
|
* @todo Support higher to lower reshare strategies
|
|
2289
2506
|
*/ async reshareStrategy({ chainName, wallet, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme }) {
|
|
2507
|
+
const bitcoinConfig = this.getBitcoinConfigForChain(chainName, accountAddress);
|
|
2290
2508
|
const mpcSigner = getMPCSigner({
|
|
2291
2509
|
chainName,
|
|
2292
|
-
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
2510
|
+
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
2511
|
+
bitcoinConfig
|
|
2293
2512
|
});
|
|
2294
2513
|
// Determine share counts based on threshold signature schemes
|
|
2295
2514
|
const { newClientShareCount, existingClientShareCount } = core.getReshareConfig({
|
|
@@ -2307,7 +2526,8 @@ class DynamicWalletClient {
|
|
|
2307
2526
|
})).slice(0, existingClientShareCount);
|
|
2308
2527
|
const existingClientKeygenIds = await Promise.all(existingClientKeyShares.map(async (keyShare)=>await this.getExportId({
|
|
2309
2528
|
chainName,
|
|
2310
|
-
clientKeyShare: keyShare
|
|
2529
|
+
clientKeyShare: keyShare,
|
|
2530
|
+
bitcoinConfig
|
|
2311
2531
|
})));
|
|
2312
2532
|
return {
|
|
2313
2533
|
newClientInitKeygenResults,
|
|
@@ -2317,22 +2537,18 @@ class DynamicWalletClient {
|
|
|
2317
2537
|
};
|
|
2318
2538
|
}
|
|
2319
2539
|
async reshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, signedSessionId, cloudProviders = [], delegateToProjectEnvironment = false, mfaToken, revokeDelegation = false }) {
|
|
2320
|
-
return this.
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
mfaToken,
|
|
2333
|
-
revokeDelegation
|
|
2334
|
-
})
|
|
2335
|
-
});
|
|
2540
|
+
return WalletQueueManager.queueHeavyOperation(accountAddress, core.WalletOperation.RESHARE, ()=>this.internalReshare({
|
|
2541
|
+
chainName,
|
|
2542
|
+
accountAddress,
|
|
2543
|
+
oldThresholdSignatureScheme,
|
|
2544
|
+
newThresholdSignatureScheme,
|
|
2545
|
+
password,
|
|
2546
|
+
signedSessionId,
|
|
2547
|
+
cloudProviders,
|
|
2548
|
+
delegateToProjectEnvironment,
|
|
2549
|
+
mfaToken,
|
|
2550
|
+
revokeDelegation
|
|
2551
|
+
}));
|
|
2336
2552
|
}
|
|
2337
2553
|
async internalReshare({ chainName, accountAddress, oldThresholdSignatureScheme, newThresholdSignatureScheme, password = undefined, signedSessionId, cloudProviders = [], delegateToProjectEnvironment = false, mfaToken, revokeDelegation = false, hasAttemptedKeyShareRecovery = false }) {
|
|
2338
2554
|
const dynamicRequestId = uuid.v4();
|
|
@@ -2369,6 +2585,7 @@ class DynamicWalletClient {
|
|
|
2369
2585
|
...newClientKeygenIds,
|
|
2370
2586
|
...existingClientKeygenIds
|
|
2371
2587
|
];
|
|
2588
|
+
const bitcoinConfig = this.getBitcoinConfigForChain(chainName, accountAddress);
|
|
2372
2589
|
// Server to create the room and complete the server reshare logics
|
|
2373
2590
|
const data = await this.apiClient.reshare({
|
|
2374
2591
|
walletId: wallet.walletId,
|
|
@@ -2389,7 +2606,6 @@ class DynamicWalletClient {
|
|
|
2389
2606
|
...serverKeygenIds,
|
|
2390
2607
|
...newServerKeygenIds
|
|
2391
2608
|
];
|
|
2392
|
-
const bitcoinConfig = this.getBitcoinConfigForChain(chainName, accountAddress);
|
|
2393
2609
|
const mpcSigner = getMPCSigner({
|
|
2394
2610
|
chainName,
|
|
2395
2611
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
@@ -2881,7 +3097,10 @@ class DynamicWalletClient {
|
|
|
2881
3097
|
/**
|
|
2882
3098
|
* Ensures that client key shares exist for the given account address.
|
|
2883
3099
|
* Throws an error if no shares are found.
|
|
2884
|
-
*
|
|
3100
|
+
*
|
|
3101
|
+
* Note: This method only checks for existing shares in storage.
|
|
3102
|
+
* Auto-recovery logic has been removed in favor of the queue pattern.
|
|
3103
|
+
* Callers should handle recovery explicitly if needed.
|
|
2885
3104
|
*/ async ensureClientShare(accountAddress) {
|
|
2886
3105
|
const shares = await this.getClientKeySharesFromStorage({
|
|
2887
3106
|
accountAddress
|
|
@@ -3208,6 +3427,17 @@ class DynamicWalletClient {
|
|
|
3208
3427
|
};
|
|
3209
3428
|
}
|
|
3210
3429
|
async recoverEncryptedBackupByWallet({ accountAddress, password, walletOperation, signedSessionId, shareCount = undefined, storeRecoveredShares = true, mfaToken }) {
|
|
3430
|
+
return WalletQueueManager.queueRecoverOperation(accountAddress, core.WalletOperation.RECOVER, ()=>this.internalRecoverEncryptedBackupByWallet({
|
|
3431
|
+
accountAddress,
|
|
3432
|
+
password,
|
|
3433
|
+
walletOperation,
|
|
3434
|
+
signedSessionId,
|
|
3435
|
+
shareCount,
|
|
3436
|
+
storeRecoveredShares,
|
|
3437
|
+
mfaToken
|
|
3438
|
+
}));
|
|
3439
|
+
}
|
|
3440
|
+
async internalRecoverEncryptedBackupByWallet({ accountAddress, password, walletOperation, signedSessionId, shareCount = undefined, storeRecoveredShares = true, mfaToken }) {
|
|
3211
3441
|
try {
|
|
3212
3442
|
const wallet = this.walletMap[accountAddress];
|
|
3213
3443
|
this.logger.debug(`recoverEncryptedBackupByWallet wallet: ${walletOperation}`, wallet);
|
|
@@ -3807,20 +4037,55 @@ class DynamicWalletClient {
|
|
|
3807
4037
|
}
|
|
3808
4038
|
}
|
|
3809
4039
|
/**
|
|
3810
|
-
* Gets the recovery state of a wallet
|
|
4040
|
+
* Gets the recovery state of a wallet, optionally recovering shares if not available.
|
|
3811
4041
|
* This method is useful for detecting if a wallet is password-encrypted
|
|
3812
4042
|
* and needs unlocking before use.
|
|
3813
4043
|
*
|
|
4044
|
+
* If shares are not available locally and recovery parameters are provided,
|
|
4045
|
+
* this method will call getWallet with RECOVER operation to fetch shares.
|
|
4046
|
+
* The underlying recoverEncryptedBackupByWallet handles deduplication via
|
|
4047
|
+
* inFlightRecovery, so concurrent calls won't start multiple recoveries.
|
|
4048
|
+
*
|
|
3814
4049
|
* @param accountAddress - The account address of the wallet
|
|
4050
|
+
* @param signedSessionId - Optional signed session ID for triggering recovery if shares not available
|
|
4051
|
+
* @param password - Optional password for decrypting recovered shares
|
|
3815
4052
|
* @returns WalletRecoveryState indicating the wallet's lock state and share availability
|
|
3816
|
-
|
|
4053
|
+
* @throws Error if recovery fails or no shares available after recovery
|
|
4054
|
+
*/ async getWalletRecoveryState({ accountAddress, signedSessionId, password }) {
|
|
3817
4055
|
const walletData = this.walletMap[accountAddress];
|
|
3818
4056
|
if (!walletData) {
|
|
3819
4057
|
throw new Error(`Wallet not found for address: ${accountAddress}`);
|
|
3820
4058
|
}
|
|
3821
|
-
|
|
4059
|
+
let clientKeyShares = await this.getClientKeySharesFromStorage({
|
|
3822
4060
|
accountAddress
|
|
3823
4061
|
});
|
|
4062
|
+
// If no local shares and signedSessionId provided, trigger recovery via getWallet
|
|
4063
|
+
// getWallet -> recoverEncryptedBackupByWallet handles deduplication via inFlightRecovery
|
|
4064
|
+
if (clientKeyShares.length === 0 && signedSessionId) {
|
|
4065
|
+
var _walletData_clientKeySharesBackupInfo;
|
|
4066
|
+
await this.getWallet({
|
|
4067
|
+
accountAddress,
|
|
4068
|
+
walletOperation: core.WalletOperation.RECOVER,
|
|
4069
|
+
signedSessionId,
|
|
4070
|
+
password
|
|
4071
|
+
});
|
|
4072
|
+
// Re-fetch shares after recovery
|
|
4073
|
+
clientKeyShares = await this.getClientKeySharesFromStorage({
|
|
4074
|
+
accountAddress
|
|
4075
|
+
});
|
|
4076
|
+
var _walletData_clientKeySharesBackupInfo_passwordEncrypted;
|
|
4077
|
+
// For password-encrypted wallets, also check for cached encrypted shares
|
|
4078
|
+
const isPasswordEncrypted = (_walletData_clientKeySharesBackupInfo_passwordEncrypted = (_walletData_clientKeySharesBackupInfo = walletData.clientKeySharesBackupInfo) == null ? void 0 : _walletData_clientKeySharesBackupInfo.passwordEncrypted) != null ? _walletData_clientKeySharesBackupInfo_passwordEncrypted : false;
|
|
4079
|
+
let hasEncryptedShares = false;
|
|
4080
|
+
if (isPasswordEncrypted && clientKeyShares.length === 0) {
|
|
4081
|
+
const encryptedStorageKey = `${accountAddress}${core.ENCRYPTED_SHARES_STORAGE_SUFFIX}`;
|
|
4082
|
+
const encryptedData = await this.storage.getItem(encryptedStorageKey);
|
|
4083
|
+
hasEncryptedShares = !!encryptedData;
|
|
4084
|
+
}
|
|
4085
|
+
if (clientKeyShares.length === 0 && !hasEncryptedShares) {
|
|
4086
|
+
throw new Error(`No key shares available for wallet ${accountAddress} after recovery`);
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
3824
4089
|
return determineWalletRecoveryState({
|
|
3825
4090
|
clientKeySharesBackupInfo: walletData.clientKeySharesBackupInfo,
|
|
3826
4091
|
hasLocalShares: clientKeyShares.length > 0
|
|
@@ -4116,8 +4381,6 @@ class DynamicWalletClient {
|
|
|
4116
4381
|
}
|
|
4117
4382
|
DynamicWalletClient.rooms = {};
|
|
4118
4383
|
DynamicWalletClient.roomsInitializing = {};
|
|
4119
|
-
DynamicWalletClient.walletBusyMap = {};
|
|
4120
|
-
DynamicWalletClient.walletBusyPromiseMap = {};
|
|
4121
4384
|
|
|
4122
4385
|
Object.defineProperty(exports, "BIP340", {
|
|
4123
4386
|
enumerable: true,
|
|
@@ -4182,6 +4445,9 @@ exports.ERROR_SIGN_MESSAGE = ERROR_SIGN_MESSAGE;
|
|
|
4182
4445
|
exports.ERROR_SIGN_TYPED_DATA = ERROR_SIGN_TYPED_DATA;
|
|
4183
4446
|
exports.ERROR_VERIFY_MESSAGE_SIGNATURE = ERROR_VERIFY_MESSAGE_SIGNATURE;
|
|
4184
4447
|
exports.ERROR_VERIFY_TRANSACTION_SIGNATURE = ERROR_VERIFY_TRANSACTION_SIGNATURE;
|
|
4448
|
+
exports.HEAVY_QUEUE_OPERATIONS = HEAVY_QUEUE_OPERATIONS;
|
|
4449
|
+
exports.RECOVER_QUEUE_OPERATIONS = RECOVER_QUEUE_OPERATIONS;
|
|
4450
|
+
exports.SIGN_QUEUE_OPERATIONS = SIGN_QUEUE_OPERATIONS;
|
|
4185
4451
|
exports.WalletBusyError = WalletBusyError;
|
|
4186
4452
|
exports.WalletNotReadyError = WalletNotReadyError;
|
|
4187
4453
|
exports.cancelICloudAuth = cancelICloudAuth;
|
|
@@ -4208,9 +4474,12 @@ exports.hasCloudProviderBackup = hasCloudProviderBackup;
|
|
|
4208
4474
|
exports.hasDelegatedBackup = hasDelegatedBackup;
|
|
4209
4475
|
exports.initializeCloudKit = initializeCloudKit;
|
|
4210
4476
|
exports.isBrowser = isBrowser;
|
|
4477
|
+
exports.isHeavyQueueOperation = isHeavyQueueOperation;
|
|
4211
4478
|
exports.isHexString = isHexString;
|
|
4212
4479
|
exports.isICloudAuthenticated = isICloudAuthenticated;
|
|
4213
4480
|
exports.isPublicKeyMismatchError = isPublicKeyMismatchError;
|
|
4481
|
+
exports.isRecoverQueueOperation = isRecoverQueueOperation;
|
|
4482
|
+
exports.isSignQueueOperation = isSignQueueOperation;
|
|
4214
4483
|
exports.listICloudBackups = listICloudBackups;
|
|
4215
4484
|
exports.mergeUniqueKeyShares = mergeUniqueKeyShares;
|
|
4216
4485
|
exports.retryPromise = retryPromise;
|