@dynamic-labs-wallet/browser 0.0.339 → 0.0.341
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 +238 -118
- package/index.esm.js +238 -118
- package/package.json +3 -3
- package/src/backup/errors.d.ts +13 -0
- package/src/backup/errors.d.ts.map +1 -0
- package/src/client.d.ts +21 -8
- package/src/client.d.ts.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.d.ts.map +1 -1
- package/src/utils.d.ts +0 -8
- package/src/utils.d.ts.map +1 -1
package/index.esm.js
CHANGED
|
@@ -355,6 +355,17 @@ class InvalidPasswordError extends Error {
|
|
|
355
355
|
return metadata;
|
|
356
356
|
};
|
|
357
357
|
|
|
358
|
+
class StaleLocalSharesError extends Error {
|
|
359
|
+
constructor(args){
|
|
360
|
+
super(args.localSharesRefreshed ? 'Local key shares were stale and have been refreshed from the server. Please retry the operation.' : 'Local key shares are stale and could not be refreshed automatically. Please recover or re-authenticate before re-attempting.');
|
|
361
|
+
this.name = 'StaleLocalSharesError';
|
|
362
|
+
this.accountAddress = args.accountAddress;
|
|
363
|
+
this.localKeygenIds = args.localKeygenIds;
|
|
364
|
+
this.recordedKeygenIds = args.recordedKeygenIds;
|
|
365
|
+
this.localSharesRefreshed = args.localSharesRefreshed;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
358
369
|
const GOOGLE_DRIVE_UPLOAD_API = 'https://www.googleapis.com';
|
|
359
370
|
const USER_ACTIONABLE_REASONS = new Set([
|
|
360
371
|
'auth_denied',
|
|
@@ -722,36 +733,6 @@ const getClientKeyShareBackupInfo = (params)=>{
|
|
|
722
733
|
passwordEncrypted
|
|
723
734
|
};
|
|
724
735
|
};
|
|
725
|
-
/**
|
|
726
|
-
* Helper function to merge keyshares and remove duplicates based on pubkey and secretShare
|
|
727
|
-
* @param existingKeyShares - Array of existing keyshares
|
|
728
|
-
* @param newKeyShares - Array of new keyshares to merge
|
|
729
|
-
* @returns Array of merged unique keyshares
|
|
730
|
-
*/ const mergeUniqueKeyShares = (existingKeyShares, newKeyShares)=>{
|
|
731
|
-
const hasDegeneratePubkeyToString = [
|
|
732
|
-
...existingKeyShares,
|
|
733
|
-
...newKeyShares
|
|
734
|
-
].some((share)=>{
|
|
735
|
-
var _share_pubkey;
|
|
736
|
-
return 'pubkey' in share && ((_share_pubkey = share.pubkey) == null ? void 0 : _share_pubkey.toString()) === '[object Object]' // NOSONAR
|
|
737
|
-
;
|
|
738
|
-
});
|
|
739
|
-
if (hasDegeneratePubkeyToString) {
|
|
740
|
-
Logger.warn('[mergeUniqueKeyShares] pubkey.toString() returned "[object Object]" — dedup falls back to secretShare-only comparison', {
|
|
741
|
-
existingCount: existingKeyShares.length,
|
|
742
|
-
newCount: newKeyShares.length
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
const uniqueKeyShares = newKeyShares.filter((newShare)=>!existingKeyShares.some((existingShare)=>{
|
|
746
|
-
if (!('pubkey' in newShare) || !('pubkey' in existingShare)) return false;
|
|
747
|
-
if (!newShare.pubkey || !existingShare.pubkey) return false;
|
|
748
|
-
return newShare.pubkey.toString() === existingShare.pubkey.toString() && newShare.secretShare === existingShare.secretShare;
|
|
749
|
-
}));
|
|
750
|
-
return [
|
|
751
|
-
...existingKeyShares,
|
|
752
|
-
...uniqueKeyShares
|
|
753
|
-
];
|
|
754
|
-
};
|
|
755
736
|
const timeoutPromise = ({ timeInMs, activity = 'Ceremony' })=>{
|
|
756
737
|
return new Promise((_, reject)=>setTimeout(()=>reject(new Error(`${activity} did not complete in ${timeInMs}ms`)), timeInMs));
|
|
757
738
|
};
|
|
@@ -2754,23 +2735,15 @@ class DynamicWalletClient {
|
|
|
2754
2735
|
walletOperation,
|
|
2755
2736
|
dynamicRequestId
|
|
2756
2737
|
}, this.getTraceContext(traceContext)));
|
|
2757
|
-
// Refresh walletMap from API before recovery to ensure backup info is up-to-date
|
|
2738
|
+
// Refresh walletMap from API before recovery to ensure backup info is up-to-date.
|
|
2758
2739
|
await this.getWallets();
|
|
2759
|
-
// Recover key shares from backup (don't auto-store, we'll overwrite below)
|
|
2760
|
-
// Use internal method directly to bypass queueing - we're already inside a queued operation
|
|
2761
2740
|
const recoveredKeyShares = await this.internalRecoverEncryptedBackupByWallet({
|
|
2762
2741
|
accountAddress,
|
|
2763
|
-
password
|
|
2764
|
-
walletOperation,
|
|
2742
|
+
password,
|
|
2765
2743
|
signedSessionId,
|
|
2744
|
+
walletOperation,
|
|
2766
2745
|
mfaToken,
|
|
2767
|
-
storeRecoveredShares:
|
|
2768
|
-
});
|
|
2769
|
-
// Overwrite local key shares with recovered ones (not merge)
|
|
2770
|
-
await this.setClientKeySharesToStorage({
|
|
2771
|
-
accountAddress,
|
|
2772
|
-
clientKeyShares: recoveredKeyShares,
|
|
2773
|
-
overwriteOrMerge: 'overwrite'
|
|
2746
|
+
storeRecoveredShares: true
|
|
2774
2747
|
});
|
|
2775
2748
|
this.logger.info('[DynamicWaasWalletClient] Key shares recovered, retrying operation', _extends({
|
|
2776
2749
|
accountAddress,
|
|
@@ -3104,8 +3077,7 @@ class DynamicWalletClient {
|
|
|
3104
3077
|
});
|
|
3105
3078
|
await this.setClientKeySharesToStorage({
|
|
3106
3079
|
accountAddress,
|
|
3107
|
-
clientKeyShares: refreshResults
|
|
3108
|
-
overwriteOrMerge: 'overwrite'
|
|
3080
|
+
clientKeyShares: refreshResults
|
|
3109
3081
|
});
|
|
3110
3082
|
} catch (error) {
|
|
3111
3083
|
// Expected condition — not a server error, no need to page on this.
|
|
@@ -3127,6 +3099,13 @@ class DynamicWalletClient {
|
|
|
3127
3099
|
throw error;
|
|
3128
3100
|
}
|
|
3129
3101
|
}
|
|
3102
|
+
async getKeygenIdForShare(mpcSigner, share) {
|
|
3103
|
+
// BIP340.exportID accepts the raw secretShare string; other signers accept the whole keygen result.
|
|
3104
|
+
if (mpcSigner instanceof BIP340) {
|
|
3105
|
+
return mpcSigner.exportID(share.secretShare);
|
|
3106
|
+
}
|
|
3107
|
+
return mpcSigner.exportID(share);
|
|
3108
|
+
}
|
|
3130
3109
|
async getExportId({ chainName, clientKeyShare, bitcoinConfig }) {
|
|
3131
3110
|
const mpcSigner = getMPCSigner({
|
|
3132
3111
|
chainName,
|
|
@@ -3134,15 +3113,7 @@ class DynamicWalletClient {
|
|
|
3134
3113
|
bitcoinConfig
|
|
3135
3114
|
});
|
|
3136
3115
|
try {
|
|
3137
|
-
|
|
3138
|
-
// The exportID accepts either BIP340KeygenResult or string (secretShare)
|
|
3139
|
-
let exportId;
|
|
3140
|
-
if (mpcSigner instanceof BIP340) {
|
|
3141
|
-
const secretShareString = clientKeyShare.secretShare;
|
|
3142
|
-
exportId = await mpcSigner.exportID(secretShareString);
|
|
3143
|
-
} else {
|
|
3144
|
-
exportId = await mpcSigner.exportID(clientKeyShare);
|
|
3145
|
-
}
|
|
3116
|
+
const exportId = await this.getKeygenIdForShare(mpcSigner, clientKeyShare);
|
|
3146
3117
|
this.logger.debug('[DynamicWaasWalletClient] getExportId succeeded', {
|
|
3147
3118
|
chainName,
|
|
3148
3119
|
exportId
|
|
@@ -3460,8 +3431,7 @@ class DynamicWalletClient {
|
|
|
3460
3431
|
// only after backup succeeds.
|
|
3461
3432
|
await this.setClientKeySharesToStorage({
|
|
3462
3433
|
accountAddress,
|
|
3463
|
-
clientKeyShares: distribution.clientShares
|
|
3464
|
-
overwriteOrMerge: 'overwrite'
|
|
3434
|
+
clientKeyShares: distribution.clientShares
|
|
3465
3435
|
});
|
|
3466
3436
|
} catch (error) {
|
|
3467
3437
|
logError({
|
|
@@ -3484,8 +3454,7 @@ class DynamicWalletClient {
|
|
|
3484
3454
|
});
|
|
3485
3455
|
await this.setClientKeySharesToStorage({
|
|
3486
3456
|
accountAddress,
|
|
3487
|
-
clientKeyShares: []
|
|
3488
|
-
overwriteOrMerge: 'overwrite'
|
|
3457
|
+
clientKeyShares: []
|
|
3489
3458
|
});
|
|
3490
3459
|
throw error;
|
|
3491
3460
|
}
|
|
@@ -3831,63 +3800,44 @@ class DynamicWalletClient {
|
|
|
3831
3800
|
* Helper function to store client key shares in storage.
|
|
3832
3801
|
* Uses secureStorage when available (mobile), otherwise falls back to localStorage (browser).
|
|
3833
3802
|
*/ /**
|
|
3834
|
-
*
|
|
3835
|
-
|
|
3836
|
-
* persistence-info log so we can audit storage operations from traces.
|
|
3837
|
-
*/ async resolveSharesToPersist({ accountAddress, clientKeyShares, overwriteOrMerge, source, readExisting }) {
|
|
3838
|
-
let sharesToStore;
|
|
3839
|
-
let existingCount = 0;
|
|
3840
|
-
if (overwriteOrMerge === 'overwrite') {
|
|
3841
|
-
sharesToStore = clientKeyShares;
|
|
3842
|
-
} else {
|
|
3843
|
-
const existing = await readExisting();
|
|
3844
|
-
existingCount = existing.length;
|
|
3845
|
-
sharesToStore = mergeUniqueKeyShares(existing, clientKeyShares);
|
|
3846
|
-
}
|
|
3803
|
+
* Emits a persistence-info log so storage writes are auditable from traces.
|
|
3804
|
+
*/ logSharePersistence({ accountAddress, clientKeyShares, source }) {
|
|
3847
3805
|
this.logger.info('[DynamicWaasWalletClient] Persisting client key shares', {
|
|
3848
3806
|
accountAddress,
|
|
3849
3807
|
source,
|
|
3850
|
-
|
|
3851
|
-
inputCount: clientKeyShares.length,
|
|
3852
|
-
existingCount,
|
|
3853
|
-
finalCount: sharesToStore.length
|
|
3808
|
+
inputCount: clientKeyShares.length
|
|
3854
3809
|
});
|
|
3855
|
-
return sharesToStore;
|
|
3856
3810
|
}
|
|
3857
|
-
async setClientKeySharesToLocalStorage({ accountAddress, clientKeyShares
|
|
3811
|
+
async setClientKeySharesToLocalStorage({ accountAddress, clientKeyShares }) {
|
|
3858
3812
|
accountAddress = normalizeAddress(accountAddress);
|
|
3859
|
-
|
|
3813
|
+
this.logSharePersistence({
|
|
3860
3814
|
accountAddress,
|
|
3861
3815
|
clientKeyShares,
|
|
3862
|
-
|
|
3863
|
-
source: 'localStorage',
|
|
3864
|
-
readExisting: ()=>this.getClientKeySharesFromLocalStorage({
|
|
3865
|
-
accountAddress
|
|
3866
|
-
})
|
|
3816
|
+
source: 'localStorage'
|
|
3867
3817
|
});
|
|
3868
3818
|
const stringifiedClientKeyShares = JSON.stringify({
|
|
3869
|
-
clientKeyShares
|
|
3819
|
+
clientKeyShares
|
|
3870
3820
|
});
|
|
3871
3821
|
await this.storage.setItem(accountAddress, stringifiedClientKeyShares);
|
|
3872
3822
|
}
|
|
3873
3823
|
/**
|
|
3874
|
-
*
|
|
3824
|
+
* Replaces the client key shares stored for `accountAddress` with `clientKeyShares`.
|
|
3875
3825
|
* Uses secureStorage when available (mobile), otherwise falls back to localStorage (browser).
|
|
3876
|
-
|
|
3826
|
+
*
|
|
3827
|
+
* A WaaS wallet holds exactly one client share per device (see share-distribution
|
|
3828
|
+
* factories in types.ts), so writes are always a full replacement — there is no
|
|
3829
|
+
* merge mode. Callers that need to clear local shares pass `clientKeyShares: []`.
|
|
3830
|
+
*/ async setClientKeySharesToStorage({ accountAddress, clientKeyShares }) {
|
|
3877
3831
|
accountAddress = normalizeAddress(accountAddress);
|
|
3878
3832
|
// Use secure storage if available (mobile)
|
|
3879
3833
|
if (this.secureStorage) {
|
|
3880
3834
|
try {
|
|
3881
|
-
|
|
3835
|
+
this.logSharePersistence({
|
|
3882
3836
|
accountAddress,
|
|
3883
3837
|
clientKeyShares,
|
|
3884
|
-
|
|
3885
|
-
source: 'secureStorage',
|
|
3886
|
-
readExisting: ()=>this.getClientKeySharesFromStorage({
|
|
3887
|
-
accountAddress
|
|
3888
|
-
})
|
|
3838
|
+
source: 'secureStorage'
|
|
3889
3839
|
});
|
|
3890
|
-
await this.secureStorage.setClientKeyShare(accountAddress,
|
|
3840
|
+
await this.secureStorage.setClientKeyShare(accountAddress, clientKeyShares);
|
|
3891
3841
|
return;
|
|
3892
3842
|
} catch (error) {
|
|
3893
3843
|
logError({
|
|
@@ -3903,8 +3853,7 @@ class DynamicWalletClient {
|
|
|
3903
3853
|
// Fallback to localStorage (browser)
|
|
3904
3854
|
await this.setClientKeySharesToLocalStorage({
|
|
3905
3855
|
accountAddress,
|
|
3906
|
-
clientKeyShares
|
|
3907
|
-
overwriteOrMerge
|
|
3856
|
+
clientKeyShares
|
|
3908
3857
|
});
|
|
3909
3858
|
}
|
|
3910
3859
|
/**
|
|
@@ -4263,6 +4212,93 @@ class DynamicWalletClient {
|
|
|
4263
4212
|
throw error;
|
|
4264
4213
|
}
|
|
4265
4214
|
}
|
|
4215
|
+
// Defense-in-depth check: before uploading a backup, confirm that local client shares
|
|
4216
|
+
// still match the keygenIds recorded for backups[DYNAMIC] on the server. On mismatch,
|
|
4217
|
+
// refreshes local storage from the server's encrypted backup (so a subsequent retry
|
|
4218
|
+
// sees fresh state) and then throws StaleLocalSharesError. We deliberately don't
|
|
4219
|
+
// continue the upload silently after recovery — callers like updatePassword hold
|
|
4220
|
+
// operation-specific context (existing/new password) that must be re-applied to
|
|
4221
|
+
// freshly-recovered shares. Throwing surfaces the stale-state event so the host can
|
|
4222
|
+
// re-enter the outer operation with correct context. Skipped on internal post-rotation
|
|
4223
|
+
// flows that pass clientKeyShares explicitly (the refresh path).
|
|
4224
|
+
async ensureLocalSharesAreFresh({ accountAddress, walletData, localShares, password, signedSessionId }) {
|
|
4225
|
+
var _freshBackupInfo_backups;
|
|
4226
|
+
// Always round-trip to the server — walletMap is populated at wallet-load and is
|
|
4227
|
+
// not invalidated when another tab rotates shares, so trusting it would
|
|
4228
|
+
// false-positive on cross-tab refresh.
|
|
4229
|
+
const { chainName } = walletData;
|
|
4230
|
+
const mpcSigner = getMPCSigner({
|
|
4231
|
+
chainName,
|
|
4232
|
+
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
4233
|
+
bitcoinConfig: this.getBitcoinConfigForChain(chainName, accountAddress)
|
|
4234
|
+
});
|
|
4235
|
+
const [freshBackupInfo, localKeygenIds] = await Promise.all([
|
|
4236
|
+
this.fetchFreshWalletBackupInfo({
|
|
4237
|
+
accountAddress
|
|
4238
|
+
}),
|
|
4239
|
+
Promise.all(localShares.map((share)=>this.getKeygenIdForShare(mpcSigner, share)))
|
|
4240
|
+
]);
|
|
4241
|
+
var _freshBackupInfo_backups_BackupLocation_DYNAMIC;
|
|
4242
|
+
const recordedKeygenIds = ((_freshBackupInfo_backups_BackupLocation_DYNAMIC = freshBackupInfo == null ? void 0 : (_freshBackupInfo_backups = freshBackupInfo.backups) == null ? void 0 : _freshBackupInfo_backups[BackupLocation.DYNAMIC]) != null ? _freshBackupInfo_backups_BackupLocation_DYNAMIC : []).map((entry)=>entry.keygenId).filter((id)=>typeof id === 'string' && id.length > 0);
|
|
4243
|
+
// First-time backup or legacy data (server didn't populate keygenId yet) — nothing to compare.
|
|
4244
|
+
if (recordedKeygenIds.length === 0) return;
|
|
4245
|
+
const recordedSet = new Set(recordedKeygenIds);
|
|
4246
|
+
const localMatchesRecorded = localKeygenIds.length === recordedSet.size && localKeygenIds.every((id)=>recordedSet.has(id));
|
|
4247
|
+
if (localMatchesRecorded) return;
|
|
4248
|
+
this.logger.warn('[storeEncryptedBackupByWallet] Stale local shares detected; refreshing local storage', {
|
|
4249
|
+
accountAddress,
|
|
4250
|
+
walletId: walletData.walletId,
|
|
4251
|
+
environmentId: this.environmentId,
|
|
4252
|
+
localKeygenIds,
|
|
4253
|
+
recordedKeygenIds
|
|
4254
|
+
});
|
|
4255
|
+
// Resolve a session, falling back to the host's reverse-channel callback when one
|
|
4256
|
+
// wasn't passed in. If neither source produces a session we skip the refresh and
|
|
4257
|
+
// still throw — the host can re-auth from the error.
|
|
4258
|
+
let resolvedSessionId;
|
|
4259
|
+
try {
|
|
4260
|
+
resolvedSessionId = await this.resolveSignedSessionId(signedSessionId);
|
|
4261
|
+
} catch (e) {
|
|
4262
|
+
resolvedSessionId = undefined;
|
|
4263
|
+
}
|
|
4264
|
+
let localSharesRefreshed = false;
|
|
4265
|
+
if (resolvedSessionId) {
|
|
4266
|
+
// The recovery decrypt key is whatever the server's blob was encrypted with — not the
|
|
4267
|
+
// caller's `password` arg, which is the key we'll re-encrypt the *next* upload with.
|
|
4268
|
+
// For setPassword the wallet is currently passwordless (blob encrypted with environmentId)
|
|
4269
|
+
// and the caller passed the new password; passing it through here would
|
|
4270
|
+
// InvalidPasswordError. Fall back to undefined so internalRecover picks environmentId.
|
|
4271
|
+
const recoveryPassword = freshBackupInfo.passwordEncrypted ? password : undefined;
|
|
4272
|
+
try {
|
|
4273
|
+
// walletMap was already refreshed by fetchFreshWalletBackupInfo above, so we skip the
|
|
4274
|
+
// getWallets() refresh that recoverKeySharesOnMismatch performs for its own callers.
|
|
4275
|
+
await this.internalRecoverEncryptedBackupByWallet({
|
|
4276
|
+
accountAddress,
|
|
4277
|
+
password: recoveryPassword,
|
|
4278
|
+
signedSessionId: resolvedSessionId,
|
|
4279
|
+
walletOperation: WalletOperation.RECOVER,
|
|
4280
|
+
storeRecoveredShares: true
|
|
4281
|
+
});
|
|
4282
|
+
localSharesRefreshed = true;
|
|
4283
|
+
this.logger.info('[storeEncryptedBackupByWallet] Local shares refreshed from server; caller should retry', {
|
|
4284
|
+
accountAddress,
|
|
4285
|
+
walletId: walletData.walletId
|
|
4286
|
+
});
|
|
4287
|
+
} catch (recoveryError) {
|
|
4288
|
+
this.logger.error('[storeEncryptedBackupByWallet] Failed to refresh local shares from server', {
|
|
4289
|
+
accountAddress,
|
|
4290
|
+
walletId: walletData.walletId,
|
|
4291
|
+
error: recoveryError instanceof Error ? recoveryError.message : String(recoveryError)
|
|
4292
|
+
});
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
throw new StaleLocalSharesError({
|
|
4296
|
+
accountAddress,
|
|
4297
|
+
localKeygenIds,
|
|
4298
|
+
recordedKeygenIds,
|
|
4299
|
+
localSharesRefreshed
|
|
4300
|
+
});
|
|
4301
|
+
}
|
|
4266
4302
|
/**
|
|
4267
4303
|
* Central backup orchestrator that encrypts and stores wallet key shares.
|
|
4268
4304
|
*
|
|
@@ -4295,10 +4331,24 @@ class DynamicWalletClient {
|
|
|
4295
4331
|
* @returns Promise with backup metadata including share locations and IDs
|
|
4296
4332
|
*/ async storeEncryptedBackupByWallet({ accountAddress, clientKeyShares = undefined, password = undefined, signedSessionId, cloudProviders = [], delegatedKeyshare = undefined, passwordUpdateBatchId, googleDriveAccessToken }) {
|
|
4297
4333
|
var _this_getWalletFromMap, _backupData_locationsWithKeyShares, _backupData_locationsWithKeyShares1;
|
|
4334
|
+
const hasProvidedShares = clientKeyShares !== undefined;
|
|
4298
4335
|
const keySharesToBackup = clientKeyShares != null ? clientKeyShares : await this.getClientKeySharesFromStorage({
|
|
4299
4336
|
accountAddress
|
|
4300
4337
|
});
|
|
4301
|
-
//
|
|
4338
|
+
// Freshness check — only when shares were loaded from local storage. Internal
|
|
4339
|
+
// post-rotation callers (refreshWalletAccountShares) pass clientKeyShares explicitly
|
|
4340
|
+
// and hold shares whose keygenIds are intentionally newer than the recorded backup.
|
|
4341
|
+
const walletDataForFreshness = this.getWalletFromMap(accountAddress);
|
|
4342
|
+
if (!hasProvidedShares && walletDataForFreshness) {
|
|
4343
|
+
await this.ensureLocalSharesAreFresh({
|
|
4344
|
+
accountAddress,
|
|
4345
|
+
walletData: walletDataForFreshness,
|
|
4346
|
+
localShares: keySharesToBackup,
|
|
4347
|
+
password,
|
|
4348
|
+
signedSessionId
|
|
4349
|
+
});
|
|
4350
|
+
}
|
|
4351
|
+
// Re-read after the freshness check: walletMap may have been updated with fresh server data.
|
|
4302
4352
|
const walletBackupInfo = (_this_getWalletFromMap = this.getWalletFromMap(accountAddress)) == null ? void 0 : _this_getWalletFromMap.clientKeySharesBackupInfo;
|
|
4303
4353
|
const activeCloudProviders = getActiveCloudProviders(walletBackupInfo);
|
|
4304
4354
|
const shouldBackupToCloudProviders = cloudProviders.length > 0 || activeCloudProviders.length > 0;
|
|
@@ -4696,8 +4746,7 @@ class DynamicWalletClient {
|
|
|
4696
4746
|
if (storeRecoveredShares) {
|
|
4697
4747
|
await this.setClientKeySharesToStorage({
|
|
4698
4748
|
accountAddress,
|
|
4699
|
-
clientKeyShares: decryptedKeyShares
|
|
4700
|
-
overwriteOrMerge: 'merge'
|
|
4749
|
+
clientKeyShares: decryptedKeyShares
|
|
4701
4750
|
});
|
|
4702
4751
|
await this.storage.setItem(this.storageKey, JSON.stringify(this.walletMap));
|
|
4703
4752
|
}
|
|
@@ -5183,24 +5232,27 @@ class DynamicWalletClient {
|
|
|
5183
5232
|
}
|
|
5184
5233
|
return true;
|
|
5185
5234
|
}
|
|
5235
|
+
async fetchWalletBackupInfoFromServer(accountAddress) {
|
|
5236
|
+
var _user_verifiedCredentials;
|
|
5237
|
+
const dynamicRequestId = v4();
|
|
5238
|
+
const user = await this.apiClient.getUser(dynamicRequestId);
|
|
5239
|
+
const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>{
|
|
5240
|
+
var _vc_address;
|
|
5241
|
+
return ((_vc_address = vc.address) == null ? void 0 : _vc_address.toLowerCase()) === accountAddress.toLowerCase();
|
|
5242
|
+
});
|
|
5243
|
+
return getClientKeyShareBackupInfo({
|
|
5244
|
+
walletProperties: wallet == null ? void 0 : wallet.walletProperties
|
|
5245
|
+
});
|
|
5246
|
+
}
|
|
5186
5247
|
async getWalletClientKeyShareBackupInfo({ accountAddress }) {
|
|
5187
5248
|
const dynamicRequestId = v4();
|
|
5188
5249
|
try {
|
|
5189
|
-
var _this_getWalletFromMap, _walletBackupInfo_backups_BackupLocation_DYNAMIC, _walletBackupInfo_backups
|
|
5190
|
-
// Return existing backup info if it exists
|
|
5250
|
+
var _this_getWalletFromMap, _walletBackupInfo_backups_BackupLocation_DYNAMIC, _walletBackupInfo_backups;
|
|
5191
5251
|
const walletBackupInfo = (_this_getWalletFromMap = this.getWalletFromMap(accountAddress)) == null ? void 0 : _this_getWalletFromMap.clientKeySharesBackupInfo;
|
|
5192
5252
|
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) {
|
|
5193
5253
|
return walletBackupInfo;
|
|
5194
5254
|
}
|
|
5195
|
-
|
|
5196
|
-
const user = await this.apiClient.getUser(dynamicRequestId);
|
|
5197
|
-
const wallet = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.find((vc)=>{
|
|
5198
|
-
var _vc_address;
|
|
5199
|
-
return ((_vc_address = vc.address) == null ? void 0 : _vc_address.toLowerCase()) === accountAddress.toLowerCase();
|
|
5200
|
-
});
|
|
5201
|
-
return getClientKeyShareBackupInfo({
|
|
5202
|
-
walletProperties: wallet == null ? void 0 : wallet.walletProperties
|
|
5203
|
-
});
|
|
5255
|
+
return this.fetchWalletBackupInfoFromServer(accountAddress);
|
|
5204
5256
|
} catch (error) {
|
|
5205
5257
|
logError({
|
|
5206
5258
|
message: 'Error in getWalletClientKeyShareBackupInfo',
|
|
@@ -5213,6 +5265,18 @@ class DynamicWalletClient {
|
|
|
5213
5265
|
throw error;
|
|
5214
5266
|
}
|
|
5215
5267
|
}
|
|
5268
|
+
// Bypasses the cache short-circuit in getWalletClientKeyShareBackupInfo. Required when
|
|
5269
|
+
// freshness matters across tabs/contexts — walletMap is populated at wallet-load time and
|
|
5270
|
+
// is not invalidated when another tab rotates shares.
|
|
5271
|
+
async fetchFreshWalletBackupInfo({ accountAddress }) {
|
|
5272
|
+
const freshBackupInfo = await this.fetchWalletBackupInfoFromServer(accountAddress);
|
|
5273
|
+
if (this.getWalletFromMap(accountAddress)) {
|
|
5274
|
+
this.updateWalletMap(accountAddress, {
|
|
5275
|
+
clientKeySharesBackupInfo: freshBackupInfo
|
|
5276
|
+
});
|
|
5277
|
+
}
|
|
5278
|
+
return freshBackupInfo;
|
|
5279
|
+
}
|
|
5216
5280
|
async getWallet({ accountAddress, walletOperation = WalletOperation.NO_OPERATION, shareCount = undefined, password = undefined, signedSessionId }) {
|
|
5217
5281
|
const dynamicRequestId = v4();
|
|
5218
5282
|
try {
|
|
@@ -5333,13 +5397,8 @@ class DynamicWalletClient {
|
|
|
5333
5397
|
signedSessionId,
|
|
5334
5398
|
shareCount
|
|
5335
5399
|
});
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
});
|
|
5339
|
-
await this.setClientKeySharesToStorage({
|
|
5340
|
-
accountAddress,
|
|
5341
|
-
clientKeyShares: mergeUniqueKeyShares(existingKeyShares, decryptedKeyShares)
|
|
5342
|
-
});
|
|
5400
|
+
// recoverEncryptedBackupByWallet (storeRecoveredShares: true by default) has
|
|
5401
|
+
// already written decryptedKeyShares to storage. No further write needed.
|
|
5343
5402
|
this.logger.debug('[DynamicWaasWalletClient] Recovered backup', {
|
|
5344
5403
|
decryptedKeyShares
|
|
5345
5404
|
});
|
|
@@ -5424,6 +5483,45 @@ class DynamicWalletClient {
|
|
|
5424
5483
|
});
|
|
5425
5484
|
}
|
|
5426
5485
|
/**
|
|
5486
|
+
* Fetches the encrypted-shares blob from the server and caches it in this.storage.
|
|
5487
|
+
* Returns the serialized blob, or null if the wallet is unknown or the API returns
|
|
5488
|
+
* no data. Does not decrypt — callers (e.g. unlockWallet, getWallet eager-load)
|
|
5489
|
+
* own the decrypt step.
|
|
5490
|
+
*/ async fetchAndCacheEncryptedShares({ accountAddress, signedSessionId, mfaToken }) {
|
|
5491
|
+
const walletData = this.getWalletFromMap(accountAddress);
|
|
5492
|
+
if (!walletData) {
|
|
5493
|
+
return null;
|
|
5494
|
+
}
|
|
5495
|
+
const { shares } = this.recoverStrategy({
|
|
5496
|
+
clientKeyShareBackupInfo: walletData.clientKeySharesBackupInfo,
|
|
5497
|
+
thresholdSignatureScheme: walletData.thresholdSignatureScheme,
|
|
5498
|
+
walletOperation: WalletOperation.RECOVER
|
|
5499
|
+
});
|
|
5500
|
+
const externalKeyShareIds = shares[BackupLocation.DYNAMIC] || [];
|
|
5501
|
+
this.logger.info('[unlockWallet] cache miss after getWallet, fetching encrypted shares from server', {
|
|
5502
|
+
context: {
|
|
5503
|
+
accountAddress,
|
|
5504
|
+
walletId: walletData.walletId,
|
|
5505
|
+
externalKeyShareIds
|
|
5506
|
+
}
|
|
5507
|
+
});
|
|
5508
|
+
const data = await this.apiClient.recoverEncryptedBackupByWallet({
|
|
5509
|
+
walletId: walletData.walletId,
|
|
5510
|
+
externalKeyShareIds,
|
|
5511
|
+
signedSessionId,
|
|
5512
|
+
mfaToken,
|
|
5513
|
+
requiresSignedSessionId: this.requiresSignedSessionId(),
|
|
5514
|
+
userId: this.userId
|
|
5515
|
+
});
|
|
5516
|
+
if (!data) {
|
|
5517
|
+
return null;
|
|
5518
|
+
}
|
|
5519
|
+
const serialized = JSON.stringify(data);
|
|
5520
|
+
const encryptedStorageKey = `${normalizeAddress(accountAddress)}${ENCRYPTED_SHARES_STORAGE_SUFFIX}`;
|
|
5521
|
+
await this.storage.setItem(encryptedStorageKey, serialized);
|
|
5522
|
+
return serialized;
|
|
5523
|
+
}
|
|
5524
|
+
/**
|
|
5427
5525
|
* Unlocks a password-encrypted wallet by decrypting cached encrypted shares.
|
|
5428
5526
|
* This method should be called after getWalletRecoveryState returns LOCKED state.
|
|
5429
5527
|
*
|
|
@@ -5431,7 +5529,7 @@ class DynamicWalletClient {
|
|
|
5431
5529
|
* @param password - The password to decrypt the shares
|
|
5432
5530
|
* @param signedSessionId - The signed session ID for authentication
|
|
5433
5531
|
* @returns The unlocked wallet properties
|
|
5434
|
-
*/ async unlockWallet({ accountAddress, password, signedSessionId }) {
|
|
5532
|
+
*/ async unlockWallet({ accountAddress, password, signedSessionId, mfaToken }) {
|
|
5435
5533
|
const dynamicRequestId = v4();
|
|
5436
5534
|
try {
|
|
5437
5535
|
await this.requireWalletFromMap(accountAddress, signedSessionId);
|
|
@@ -5451,6 +5549,18 @@ class DynamicWalletClient {
|
|
|
5451
5549
|
});
|
|
5452
5550
|
encryptedData = await this.storage.getItem(encryptedStorageKey);
|
|
5453
5551
|
}
|
|
5552
|
+
// getWallet short-circuits via checkWalletFields when client key shares are
|
|
5553
|
+
// durable but the encrypted-shares cache in this.storage was evicted (e.g. RN
|
|
5554
|
+
// secureStorage survives a WebKit content-process recycle while localStorage
|
|
5555
|
+
// does not). Populate the cache directly so the single decrypt path below
|
|
5556
|
+
// can proceed.
|
|
5557
|
+
if (!encryptedData) {
|
|
5558
|
+
encryptedData = await this.fetchAndCacheEncryptedShares({
|
|
5559
|
+
accountAddress,
|
|
5560
|
+
signedSessionId,
|
|
5561
|
+
mfaToken
|
|
5562
|
+
});
|
|
5563
|
+
}
|
|
5454
5564
|
if (!encryptedData) {
|
|
5455
5565
|
throw new Error('No encrypted shares found for wallet');
|
|
5456
5566
|
}
|
|
@@ -5464,7 +5574,17 @@ class DynamicWalletClient {
|
|
|
5464
5574
|
const otherEncryptedWallets = Object.entries(this.walletMap).filter(([addr, w])=>addr !== normalizedAccountAddress && w.walletReadyState !== WalletReadyState.READY && isWalletPasswordEncrypted(w));
|
|
5465
5575
|
const results = await Promise.allSettled(otherEncryptedWallets.map(async ([otherAddr])=>{
|
|
5466
5576
|
const otherEncryptedStorageKey = `${otherAddr}${ENCRYPTED_SHARES_STORAGE_SUFFIX}`;
|
|
5467
|
-
|
|
5577
|
+
let otherEncryptedData = await this.storage.getItem(otherEncryptedStorageKey);
|
|
5578
|
+
// Same cache-eviction scenario as the main wallet above: on RN, the
|
|
5579
|
+
// encrypted-shares blob can be gone while client shares survive in
|
|
5580
|
+
// secureStorage. Re-fetch from the server before giving up.
|
|
5581
|
+
if (!otherEncryptedData) {
|
|
5582
|
+
otherEncryptedData = await this.fetchAndCacheEncryptedShares({
|
|
5583
|
+
accountAddress: otherAddr,
|
|
5584
|
+
signedSessionId,
|
|
5585
|
+
mfaToken
|
|
5586
|
+
});
|
|
5587
|
+
}
|
|
5468
5588
|
if (!otherEncryptedData) {
|
|
5469
5589
|
return;
|
|
5470
5590
|
}
|
|
@@ -5853,4 +5973,4 @@ DynamicWalletClient.rooms = {};
|
|
|
5853
5973
|
DynamicWalletClient.roomsInitializing = {};
|
|
5854
5974
|
DynamicWalletClient.roomsPersistChain = Promise.resolve();
|
|
5855
5975
|
|
|
5856
|
-
export { BACKUP_FILENAME, CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX, DynamicWalletClient, ENVIRONMENT_SETTINGS_STORAGE_KEY, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_PASSCODE_REQUIRED, ERROR_PASSWORD_MISMATCH, ERROR_PASSWORD_REQUIRED_FOR_ENCRYPTED_WALLET, ERROR_PUBLIC_KEY_MISMATCH, ERROR_REFRESH_NOT_SUPPORTED_FOR_TWO_OF_THREE, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, HEAVY_QUEUE_OPERATIONS, RECOVER_QUEUE_OPERATIONS, ROOM_CACHE_COUNT, ROOM_EXPIRATION_TIME, SIGNED_SESSION_ID_MIN_VERSION, SIGNED_SESSION_ID_MIN_VERSION_BY_NAMESPACE, SIGN_QUEUE_OPERATIONS, STORAGE_KEY, 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, isNonRetryableCeremonyError, isPublicKeyMismatchError, isRecoverQueueOperation, isSignQueueOperation, listICloudBackups, markCeremonyErrorNonRetryable,
|
|
5976
|
+
export { BACKUP_FILENAME, CLIENT_KEYSHARE_EXPORT_FILENAME_PREFIX, DynamicWalletClient, ENVIRONMENT_SETTINGS_STORAGE_KEY, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_CREATE_WALLET_ACCOUNT, ERROR_EXPORT_PRIVATE_KEY, ERROR_IMPORT_PRIVATE_KEY, ERROR_KEYGEN_FAILED, ERROR_PASSCODE_REQUIRED, ERROR_PASSWORD_MISMATCH, ERROR_PASSWORD_REQUIRED_FOR_ENCRYPTED_WALLET, ERROR_PUBLIC_KEY_MISMATCH, ERROR_REFRESH_NOT_SUPPORTED_FOR_TWO_OF_THREE, ERROR_SIGN_MESSAGE, ERROR_SIGN_TYPED_DATA, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, HEAVY_QUEUE_OPERATIONS, RECOVER_QUEUE_OPERATIONS, ROOM_CACHE_COUNT, ROOM_EXPIRATION_TIME, SIGNED_SESSION_ID_MIN_VERSION, SIGNED_SESSION_ID_MIN_VERSION_BY_NAMESPACE, SIGN_QUEUE_OPERATIONS, STORAGE_KEY, StaleLocalSharesError, 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, isNonRetryableCeremonyError, isPublicKeyMismatchError, isRecoverQueueOperation, isSignQueueOperation, listICloudBackups, markCeremonyErrorNonRetryable, readEnvironmentSettings, retryPromise, shouldReshareToSameBackups, timeoutPromise };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs-wallet/browser",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.341",
|
|
4
4
|
"license": "Licensed under the Dynamic Labs, Inc. Terms Of Service (https://www.dynamic.xyz/terms-conditions)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@dynamic-labs-wallet/core": "0.0.
|
|
7
|
+
"@dynamic-labs-wallet/core": "0.0.341",
|
|
8
8
|
"@dynamic-labs-wallet/forward-mpc-client": "0.9.0",
|
|
9
|
-
"@dynamic-labs-wallet/primitives": "0.0.
|
|
9
|
+
"@dynamic-labs-wallet/primitives": "0.0.341",
|
|
10
10
|
"@dynamic-labs/sdk-api-core": "^0.0.964",
|
|
11
11
|
"argon2id": "1.0.1",
|
|
12
12
|
"axios": "1.15.2",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class StaleLocalSharesError extends Error {
|
|
2
|
+
readonly accountAddress: string;
|
|
3
|
+
readonly localKeygenIds: string[];
|
|
4
|
+
readonly recordedKeygenIds: string[];
|
|
5
|
+
readonly localSharesRefreshed: boolean;
|
|
6
|
+
constructor(args: {
|
|
7
|
+
accountAddress: string;
|
|
8
|
+
localKeygenIds: string[];
|
|
9
|
+
recordedKeygenIds: string[];
|
|
10
|
+
localSharesRefreshed: boolean;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/backup/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAgB,cAAc,EAAE,MAAM,CAAC;IACvC,SAAgB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzC,SAAgB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5C,SAAgB,oBAAoB,EAAE,OAAO,CAAC;gBAElC,IAAI,EAAE;QAChB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,EAAE,CAAC;QACzB,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAC5B,oBAAoB,EAAE,OAAO,CAAC;KAC/B;CAYF"}
|
package/src/client.d.ts
CHANGED
|
@@ -297,6 +297,7 @@ export declare class DynamicWalletClient {
|
|
|
297
297
|
*/
|
|
298
298
|
private getBitcoinConfigForChain;
|
|
299
299
|
private internalRefreshWalletAccountShares;
|
|
300
|
+
private getKeygenIdForShare;
|
|
300
301
|
getExportId({ chainName, clientKeyShare, bitcoinConfig, }: {
|
|
301
302
|
chainName: string;
|
|
302
303
|
clientKeyShare: ClientKeyShare;
|
|
@@ -420,20 +421,21 @@ export declare class DynamicWalletClient {
|
|
|
420
421
|
* Uses secureStorage when available (mobile), otherwise falls back to localStorage (browser).
|
|
421
422
|
*/
|
|
422
423
|
/**
|
|
423
|
-
*
|
|
424
|
-
* (overwrite) or merging with existing shares (merge), and emits a
|
|
425
|
-
* persistence-info log so we can audit storage operations from traces.
|
|
424
|
+
* Emits a persistence-info log so storage writes are auditable from traces.
|
|
426
425
|
*/
|
|
427
|
-
private
|
|
426
|
+
private logSharePersistence;
|
|
428
427
|
private setClientKeySharesToLocalStorage;
|
|
429
428
|
/**
|
|
430
|
-
*
|
|
429
|
+
* Replaces the client key shares stored for `accountAddress` with `clientKeyShares`.
|
|
431
430
|
* Uses secureStorage when available (mobile), otherwise falls back to localStorage (browser).
|
|
431
|
+
*
|
|
432
|
+
* A WaaS wallet holds exactly one client share per device (see share-distribution
|
|
433
|
+
* factories in types.ts), so writes are always a full replacement — there is no
|
|
434
|
+
* merge mode. Callers that need to clear local shares pass `clientKeyShares: []`.
|
|
432
435
|
*/
|
|
433
|
-
setClientKeySharesToStorage({ accountAddress, clientKeyShares,
|
|
436
|
+
setClientKeySharesToStorage({ accountAddress, clientKeyShares, }: {
|
|
434
437
|
accountAddress: string;
|
|
435
438
|
clientKeyShares: ClientKeyShare[];
|
|
436
|
-
overwriteOrMerge?: 'overwrite' | 'merge';
|
|
437
439
|
}): Promise<void>;
|
|
438
440
|
/**
|
|
439
441
|
* Ensures that client key shares exist for the given account address.
|
|
@@ -477,6 +479,7 @@ export declare class DynamicWalletClient {
|
|
|
477
479
|
externalKeyShareId?: string;
|
|
478
480
|
}[];
|
|
479
481
|
}>;
|
|
482
|
+
private ensureLocalSharesAreFresh;
|
|
480
483
|
/**
|
|
481
484
|
* Central backup orchestrator that encrypts and stores wallet key shares.
|
|
482
485
|
*
|
|
@@ -737,9 +740,11 @@ export declare class DynamicWalletClient {
|
|
|
737
740
|
accountAddress: string;
|
|
738
741
|
walletOperation?: WalletOperation;
|
|
739
742
|
}): Promise<boolean>;
|
|
743
|
+
private fetchWalletBackupInfoFromServer;
|
|
740
744
|
getWalletClientKeyShareBackupInfo({ accountAddress }: {
|
|
741
745
|
accountAddress: string;
|
|
742
746
|
}): Promise<KeyShareBackupInfo>;
|
|
747
|
+
private fetchFreshWalletBackupInfo;
|
|
743
748
|
getWallet({ accountAddress, walletOperation, shareCount, password, signedSessionId, }: {
|
|
744
749
|
accountAddress: string;
|
|
745
750
|
walletOperation?: WalletOperation;
|
|
@@ -768,6 +773,13 @@ export declare class DynamicWalletClient {
|
|
|
768
773
|
signedSessionId?: string;
|
|
769
774
|
password?: string;
|
|
770
775
|
}): Promise<WalletRecoveryState>;
|
|
776
|
+
/**
|
|
777
|
+
* Fetches the encrypted-shares blob from the server and caches it in this.storage.
|
|
778
|
+
* Returns the serialized blob, or null if the wallet is unknown or the API returns
|
|
779
|
+
* no data. Does not decrypt — callers (e.g. unlockWallet, getWallet eager-load)
|
|
780
|
+
* own the decrypt step.
|
|
781
|
+
*/
|
|
782
|
+
private fetchAndCacheEncryptedShares;
|
|
771
783
|
/**
|
|
772
784
|
* Unlocks a password-encrypted wallet by decrypting cached encrypted shares.
|
|
773
785
|
* This method should be called after getWalletRecoveryState returns LOCKED state.
|
|
@@ -777,10 +789,11 @@ export declare class DynamicWalletClient {
|
|
|
777
789
|
* @param signedSessionId - The signed session ID for authentication
|
|
778
790
|
* @returns The unlocked wallet properties
|
|
779
791
|
*/
|
|
780
|
-
unlockWallet({ accountAddress, password, signedSessionId, }: {
|
|
792
|
+
unlockWallet({ accountAddress, password, signedSessionId, mfaToken, }: {
|
|
781
793
|
accountAddress: string;
|
|
782
794
|
password: string;
|
|
783
795
|
signedSessionId: string;
|
|
796
|
+
mfaToken?: string;
|
|
784
797
|
}): Promise<WalletProperties>;
|
|
785
798
|
/**
|
|
786
799
|
* Decrypts cached encrypted key shares for a wallet and stores them locally.
|