@dynamic-labs-wallet/browser 1.1.42 → 1.1.43
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 +148 -136
- package/index.esm.js +148 -136
- package/package.json +3 -3
- package/src/backup/password/errors.d.ts.map +1 -1
- package/src/client.d.ts +36 -30
- package/src/client.d.ts.map +1 -1
- package/src/services/signedSession.d.ts +5 -3
- package/src/services/signedSession.d.ts.map +1 -1
package/index.esm.js
CHANGED
|
@@ -588,6 +588,10 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
588
588
|
'invalid_nonce_signature',
|
|
589
589
|
'invalid_session_signature'
|
|
590
590
|
]);
|
|
591
|
+
/** Reasons a fresh nonce fixes, so they may refresh on every attempt, not just once. */ const REFRESHABLE_NONCE_FAILURE_REASONS = new Set([
|
|
592
|
+
'nonce_already_used',
|
|
593
|
+
'nonce_not_found_or_expired'
|
|
594
|
+
]);
|
|
591
595
|
/** Best-effort extraction of the server error `code`/`message` from a 4xx body. */ const readErrorBody = (error)=>{
|
|
592
596
|
var _error_response;
|
|
593
597
|
if (!(error instanceof AxiosError)) {
|
|
@@ -618,6 +622,9 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
618
622
|
};
|
|
619
623
|
/** Maps the server error `code`/`message` onto a {@link NonceFailureReason}. */ const classifyNonceFailure = ({ code, message })=>{
|
|
620
624
|
const haystack = `${code != null ? code : ''} ${message != null ? message : ''}`.toLowerCase();
|
|
625
|
+
if (haystack.includes('not_found_or_expired') || haystack.includes('not found or expired')) {
|
|
626
|
+
return 'nonce_not_found_or_expired';
|
|
627
|
+
}
|
|
621
628
|
if (haystack.includes('already_used') || haystack.includes('already used') || haystack.includes('consumption failed')) {
|
|
622
629
|
return 'nonce_already_used';
|
|
623
630
|
}
|
|
@@ -662,7 +669,8 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
662
669
|
}
|
|
663
670
|
/**
|
|
664
671
|
* Builds a `retryPromise` `shouldRetry` predicate that refreshes a consumed
|
|
665
|
-
* single-use nonce via the reverse channel on a replay 400 and retries once
|
|
672
|
+
* single-use nonce via the reverse channel on a replay 400 and retries once
|
|
673
|
+
* (on every attempt when the body says the nonce was consumed or expired).
|
|
666
674
|
* The signed session carries a single-use replay nonce: it is valid on its
|
|
667
675
|
* first use, but once consumed (e.g. by a preceding operation) the server
|
|
668
676
|
* rejects with a 400, at which point a fresh session is fetched and the call
|
|
@@ -674,18 +682,24 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
674
682
|
// triggered an `alsoRetry`), so the replay 400 can land on any attempt, not
|
|
675
683
|
// just the first. Refresh on the first such 400 seen, whichever attempt
|
|
676
684
|
// it's on, and never again — preserves the single-refresh-per-operation limit.
|
|
685
|
+
// A consumed or expired nonce is the exception: each fresh session may still
|
|
686
|
+
// pop a stale pooled nonce, so it refreshes on every attempt (bounded by the
|
|
687
|
+
// caller's maxAttempts).
|
|
677
688
|
let hasRefreshed = false;
|
|
678
689
|
return async (error, attempt)=>{
|
|
679
690
|
const status = getHttpStatus(error);
|
|
691
|
+
const reason = status === 400 || status === 403 ? classifyNonceFailure(readErrorBody(error)) : undefined;
|
|
680
692
|
// A 400 is a refreshable replay nonce; a 403 is a session-key
|
|
681
693
|
// mismatch the server returns to break the refresh loop (DYNT-1282) — logged only
|
|
682
694
|
// when the body classifies as a genuine session-signature failure so unrelated 403s
|
|
683
695
|
// (e.g. an exhausted add-signer grant) don't pollute the DYNT-1599 metric.
|
|
684
696
|
// Only the 400 can actually refresh.
|
|
685
|
-
const isSessionSignatureFailure = status === 403 && SESSION_SIGNATURE_FAILURE_REASONS.has(
|
|
697
|
+
const isSessionSignatureFailure = status === 403 && reason !== undefined && SESSION_SIGNATURE_FAILURE_REASONS.has(reason);
|
|
698
|
+
const isRefreshableNonceFailure = status === 400 && reason !== undefined && REFRESHABLE_NONCE_FAILURE_REASONS.has(reason);
|
|
699
|
+
const canRefreshNow = isRefreshableNonceFailure || !hasRefreshed;
|
|
686
700
|
const shouldInstrument = status === 400 || isSessionSignatureFailure;
|
|
687
701
|
if (shouldInstrument) {
|
|
688
|
-
const willRefresh = status === 400 &&
|
|
702
|
+
const willRefresh = status === 400 && canRefreshNow && this.canRefresh && this.supportsReverseChannel();
|
|
689
703
|
this.instrumentNonceFailure({
|
|
690
704
|
error,
|
|
691
705
|
attempt,
|
|
@@ -695,7 +709,7 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
695
709
|
logContext
|
|
696
710
|
});
|
|
697
711
|
}
|
|
698
|
-
if (status === 400 &&
|
|
712
|
+
if (status === 400 && canRefreshNow && this.canRefresh) {
|
|
699
713
|
// canRefresh only proves a local callback exists, not that the host answers it.
|
|
700
714
|
if (!this.supportsReverseChannel()) {
|
|
701
715
|
this.logger.warn(`[${operationName}] host SDK version predates the signed-session reverse channel, declining nonce refresh`, _extends({
|
|
@@ -902,6 +916,8 @@ const SESSION_KEY_MISMATCH_REASONS = new Set([
|
|
|
902
916
|
]);
|
|
903
917
|
const NONCE_REASON_TO_BACKUP_REASON = {
|
|
904
918
|
nonce_already_used: 'nonce_already_used',
|
|
919
|
+
// Same dashboard bucket as before CKS split this code out of nonce_already_used.
|
|
920
|
+
nonce_not_found_or_expired: 'nonce_already_used',
|
|
905
921
|
invalid_nonce_signature: 'invalid_nonce_signature',
|
|
906
922
|
invalid_session_signature: 'invalid_session_signature',
|
|
907
923
|
other: 'signed_session'
|
|
@@ -4088,13 +4104,7 @@ class DynamicWalletClient {
|
|
|
4088
4104
|
this.restoreWallets()
|
|
4089
4105
|
];
|
|
4090
4106
|
if (this.featureFlags[FEATURE_FLAGS.ENABLE_ROOM_CACHE_FLAG] === true) {
|
|
4091
|
-
|
|
4092
|
-
initializePromises.push(this.restoreRooms().then(()=>// assume two of two threshold signature scheme
|
|
4093
|
-
this.createRooms({
|
|
4094
|
-
roomType: RoomTypeEnum.Threshold,
|
|
4095
|
-
thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
|
|
4096
|
-
roomCount: ROOM_CACHE_COUNT
|
|
4097
|
-
})));
|
|
4107
|
+
initializePromises.push(this.prefetchRoomPool());
|
|
4098
4108
|
}
|
|
4099
4109
|
await Promise.all(initializePromises);
|
|
4100
4110
|
return {
|
|
@@ -4108,6 +4118,25 @@ class DynamicWalletClient {
|
|
|
4108
4118
|
};
|
|
4109
4119
|
}
|
|
4110
4120
|
}
|
|
4121
|
+
/**
|
|
4122
|
+
* Restores the cached rooms, then creates more if the pool is low. Best effort: the pool is only a
|
|
4123
|
+
* prefetch, and takeRooms() falls back and refills it later, so a failure (e.g. a dropped request)
|
|
4124
|
+
* must not fail init. createRooms already logs its own error.
|
|
4125
|
+
*/ async prefetchRoomPool() {
|
|
4126
|
+
try {
|
|
4127
|
+
await this.restoreRooms();
|
|
4128
|
+
// assume two of two threshold signature scheme
|
|
4129
|
+
await this.createRooms({
|
|
4130
|
+
roomType: RoomTypeEnum.Threshold,
|
|
4131
|
+
thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
|
|
4132
|
+
roomCount: ROOM_CACHE_COUNT
|
|
4133
|
+
});
|
|
4134
|
+
} catch (error) {
|
|
4135
|
+
this.logger.debug('[DynamicWaasWalletClient] Room cache prefetch failed during init', {
|
|
4136
|
+
error
|
|
4137
|
+
});
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4111
4140
|
async serverInitializeKeyGen({ chainName, clientKeygenIds, dynamicRequestId, thresholdSignatureScheme, bitcoinConfig, businessAccountId, idempotencyKey, traceContext, onError, onCeremonyComplete }) {
|
|
4112
4141
|
// Initialize keygen, create room, and create the wallet account on the server.
|
|
4113
4142
|
// HTTP transport (flag on + idempotency key present) uses a single JSON
|
|
@@ -4832,14 +4861,14 @@ class DynamicWalletClient {
|
|
|
4832
4861
|
return data;
|
|
4833
4862
|
}
|
|
4834
4863
|
/**
|
|
4835
|
-
* The server half of `signAndBroadcast`: asks Dynamic to open the signing
|
|
4864
|
+
* The server half of `signAndBroadcast`: asks Dynamic to open the signing room and returns the transaction record.
|
|
4836
4865
|
*
|
|
4837
4866
|
* @param params.walletId - The wallet that signs.
|
|
4838
4867
|
* @param params.shareSetId - Which of the wallet's key sets to use. Defaults to its active one.
|
|
4839
|
-
* @param params.
|
|
4840
|
-
* @param params.
|
|
4868
|
+
* @param params.message - The transaction as the bytes to sign (RLP hex on EVM, message bytes on Solana). Not hashed.
|
|
4869
|
+
* @param params.context - What the message does in readable form (chain, recipient, amount). Dynamic checks it against the wallet and its policies.
|
|
4841
4870
|
* @param params.broadcastOptions - Solana send settings: other required signers' signatures, and skipping the pre-send dry run.
|
|
4842
|
-
* @param params.
|
|
4871
|
+
* @param params.roomId - A signing room you already opened; Dynamic opens one when omitted.
|
|
4843
4872
|
* @param params.requestedCertainty - How sure to be before calling it confirmed: `fast`, `safe` or `final`, or `chain:` plus a chain-specific value. Defaults to `safe`.
|
|
4844
4873
|
* @param params.externalReference - Your own label, returned on every event. Not unique.
|
|
4845
4874
|
* @param params.idempotencyKey - Makes a retry safe: the same key replays the stored answer instead of sending twice.
|
|
@@ -4849,15 +4878,15 @@ class DynamicWalletClient {
|
|
|
4849
4878
|
* @param params.onError - Called with the failure's message, never the raw error.
|
|
4850
4879
|
* @param params.dynamicRequestId - Id of this one request, to match client and server logs.
|
|
4851
4880
|
* @param params.traceContext - Ties this call's logs to the server's, across retries. Created when omitted.
|
|
4852
|
-
*/ async serverSignAndBroadcast({ walletId, shareSetId,
|
|
4853
|
-
// Opens
|
|
4881
|
+
*/ async serverSignAndBroadcast({ walletId, shareSetId, context, broadcastOptions, message, roomId, requestedCertainty, externalReference, idempotencyKey, mfaToken, elevatedAccessToken, approvalQuorum, onError, dynamicRequestId, traceContext }) {
|
|
4882
|
+
// Opens a signing room, unless `roomId` names one, and returns the transaction record.
|
|
4854
4883
|
return await this.apiClient.signAndBroadcast({
|
|
4855
4884
|
walletId,
|
|
4856
4885
|
shareSetId,
|
|
4857
|
-
|
|
4886
|
+
context: toJsonSafeContext(context),
|
|
4858
4887
|
broadcastOptions,
|
|
4859
|
-
|
|
4860
|
-
|
|
4888
|
+
message,
|
|
4889
|
+
roomId,
|
|
4861
4890
|
isFormatted: false,
|
|
4862
4891
|
requestedCertainty,
|
|
4863
4892
|
externalReference,
|
|
@@ -5315,12 +5344,12 @@ class DynamicWalletClient {
|
|
|
5315
5344
|
}));
|
|
5316
5345
|
}
|
|
5317
5346
|
/**
|
|
5318
|
-
* Signs a transaction for Dynamic to broadcast
|
|
5347
|
+
* Signs a transaction for Dynamic to broadcast. The signature stays on the server.
|
|
5319
5348
|
*
|
|
5320
5349
|
* @param params.accountAddress - The wallet's address.
|
|
5321
5350
|
* @param params.chainName - The wallet's chain, e.g. `EVM`.
|
|
5322
|
-
* @param params.
|
|
5323
|
-
* @param params.
|
|
5351
|
+
* @param params.message - The transaction as the bytes to sign (RLP hex on EVM, message bytes on Solana). Not hashed.
|
|
5352
|
+
* @param params.context - What the message does in readable form (chain, recipient, amount). Dynamic checks it against the wallet and its policies.
|
|
5324
5353
|
* @param params.broadcastOptions - Solana send settings: other required signers' signatures, and skipping the pre-send dry run.
|
|
5325
5354
|
* @param params.password - The wallet's backup password, if it has one.
|
|
5326
5355
|
* @param params.signedSessionId - The signed-in session, used to unlock the wallet's key share.
|
|
@@ -5333,18 +5362,13 @@ class DynamicWalletClient {
|
|
|
5333
5362
|
* @param params.onError - Called with the failure's message, never the raw error.
|
|
5334
5363
|
* @param params.traceContext - Ties this call's logs to the server's, across retries. Created when omitted.
|
|
5335
5364
|
* @returns The transaction record. A finished one comes back without signing again.
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
// Each fee attempt is one message and one context; a mismatch is the caller's, so it is named here, before any ceremony.
|
|
5339
|
-
if (messages.length === 0 || contexts.length !== messages.length) {
|
|
5340
|
-
throw new Error(`Sign and broadcast needs one signing context per message, got ${contexts.length} contexts for ${messages.length} messages`);
|
|
5341
|
-
}
|
|
5342
|
-
return this.queueSign(accountAddress, ({ reportPhase, readingShares })=>this.internalSignAndBroadcast({
|
|
5365
|
+
*/ async signAndBroadcast({ accountAddress, chainName, context, broadcastOptions, message, password = undefined, signedSessionId, mfaToken, elevatedAccessToken, approvalQuorum, requestedCertainty, externalReference, idempotencyKey = v4(), onError, traceContext }) {
|
|
5366
|
+
return this.queueSign(accountAddress, ({ reportPhase, reportDetails, readingShares })=>this.internalSignAndBroadcast({
|
|
5343
5367
|
accountAddress,
|
|
5344
5368
|
chainName,
|
|
5345
|
-
|
|
5369
|
+
context,
|
|
5346
5370
|
broadcastOptions,
|
|
5347
|
-
|
|
5371
|
+
message,
|
|
5348
5372
|
password,
|
|
5349
5373
|
signedSessionId,
|
|
5350
5374
|
mfaToken,
|
|
@@ -5356,17 +5380,21 @@ class DynamicWalletClient {
|
|
|
5356
5380
|
onError,
|
|
5357
5381
|
traceContext,
|
|
5358
5382
|
onSignPhase: reportPhase,
|
|
5383
|
+
onSignDetails: reportDetails,
|
|
5359
5384
|
onReadingShares: readingShares
|
|
5360
5385
|
}));
|
|
5361
5386
|
}
|
|
5362
5387
|
/**
|
|
5363
|
-
* Does the work of `signAndBroadcast` once it has its turn in the wallet queue: loads the wallet, asks Dynamic to open the
|
|
5388
|
+
* Does the work of `signAndBroadcast` once it has its turn in the wallet queue: loads the wallet, asks Dynamic to open the room, then signs in it.
|
|
5364
5389
|
*
|
|
5365
5390
|
* Takes the same fields as `signAndBroadcast`.
|
|
5366
5391
|
*
|
|
5367
5392
|
* @param params.onSignPhase - Reports which step it is on, so the queue can tell which step used up the time.
|
|
5368
|
-
*/ async internalSignAndBroadcast({ accountAddress, chainName,
|
|
5393
|
+
*/ async internalSignAndBroadcast({ accountAddress, chainName, context, broadcastOptions, message, password, signedSessionId, mfaToken, elevatedAccessToken, approvalQuorum, requestedCertainty, externalReference, idempotencyKey, onError, traceContext, onSignPhase, onSignDetails, onReadingShares, staleShareRetry = false, roomIdRetry = false }) {
|
|
5369
5394
|
const dynamicRequestId = v4();
|
|
5395
|
+
onSignDetails == null ? void 0 : onSignDetails({
|
|
5396
|
+
dynamicRequestId
|
|
5397
|
+
});
|
|
5370
5398
|
traceContext = withTraceId(traceContext);
|
|
5371
5399
|
let stage = 'prepareWallet';
|
|
5372
5400
|
let roomIdSource;
|
|
@@ -5376,7 +5404,6 @@ class DynamicWalletClient {
|
|
|
5376
5404
|
accountAddress,
|
|
5377
5405
|
chainName,
|
|
5378
5406
|
dynamicRequestId,
|
|
5379
|
-
attemptCount: messages.length,
|
|
5380
5407
|
staleShareRetry,
|
|
5381
5408
|
roomIdRetry
|
|
5382
5409
|
}, this.getTraceContext(traceContext)));
|
|
@@ -5390,8 +5417,15 @@ class DynamicWalletClient {
|
|
|
5390
5417
|
});
|
|
5391
5418
|
stage = 'resolveCachedRoom';
|
|
5392
5419
|
onSignPhase == null ? void 0 : onSignPhase('resolveCachedRoom');
|
|
5393
|
-
const
|
|
5394
|
-
|
|
5420
|
+
const cachedRoomId = roomIdRetry ? undefined : await this.getCachedRoom({
|
|
5421
|
+
wallet,
|
|
5422
|
+
accountAddress,
|
|
5423
|
+
chainName,
|
|
5424
|
+
key: 'signAndBroadcast',
|
|
5425
|
+
dynamicRequestId,
|
|
5426
|
+
traceContext
|
|
5427
|
+
});
|
|
5428
|
+
roomIdSource = cachedRoomId ? 'cache' : 'server';
|
|
5395
5429
|
const roomsResolvedAt = Date.now();
|
|
5396
5430
|
this.logSlowSignPreflight({
|
|
5397
5431
|
accountAddress,
|
|
@@ -5409,10 +5443,10 @@ class DynamicWalletClient {
|
|
|
5409
5443
|
const record = await this.serverSignAndBroadcast({
|
|
5410
5444
|
walletId: wallet.walletId,
|
|
5411
5445
|
shareSetId: wallet.shareSetId,
|
|
5412
|
-
|
|
5446
|
+
context,
|
|
5413
5447
|
broadcastOptions,
|
|
5414
|
-
|
|
5415
|
-
|
|
5448
|
+
message,
|
|
5449
|
+
roomId: cachedRoomId,
|
|
5416
5450
|
requestedCertainty,
|
|
5417
5451
|
externalReference,
|
|
5418
5452
|
idempotencyKey,
|
|
@@ -5423,9 +5457,7 @@ class DynamicWalletClient {
|
|
|
5423
5457
|
dynamicRequestId,
|
|
5424
5458
|
traceContext
|
|
5425
5459
|
});
|
|
5426
|
-
|
|
5427
|
-
const rooms = (_record_rooms = record.rooms) != null ? _record_rooms : [];
|
|
5428
|
-
var _roomIds_length;
|
|
5460
|
+
const room = record.room;
|
|
5429
5461
|
this.logger.info('[DynamicWaasWalletClient] Sign and broadcast accepted', _extends({
|
|
5430
5462
|
key: 'signAndBroadcast',
|
|
5431
5463
|
operation: 'accepted',
|
|
@@ -5434,38 +5466,30 @@ class DynamicWalletClient {
|
|
|
5434
5466
|
dynamicRequestId,
|
|
5435
5467
|
transactionId: record.transactionId,
|
|
5436
5468
|
transactionStatus: record.status,
|
|
5437
|
-
|
|
5438
|
-
roomIdSource,
|
|
5439
|
-
roomsSent: (_roomIds_length = roomIds == null ? void 0 : roomIds.length) != null ? _roomIds_length : 0,
|
|
5440
|
-
roomsReturned: rooms.length,
|
|
5441
|
-
// A retry of an existing row names that row's rooms, not the ones just sent.
|
|
5442
|
-
returnedRoomsMatchSent: roomIds ? roomIds.every((roomId, index)=>{
|
|
5443
|
-
var _rooms_index;
|
|
5444
|
-
return ((_rooms_index = rooms[index]) == null ? void 0 : _rooms_index.roomId) === roomId;
|
|
5445
|
-
}) : undefined
|
|
5469
|
+
roomIdSource
|
|
5446
5470
|
}, this.getTraceContext(traceContext)));
|
|
5447
|
-
// A retry of a finished request returns the record without
|
|
5471
|
+
// A retry of a finished request returns the record without a room.
|
|
5448
5472
|
if (record.status !== 'pending_signature') {
|
|
5449
5473
|
return record;
|
|
5450
5474
|
}
|
|
5451
|
-
if (
|
|
5452
|
-
throw new Error(`Expected
|
|
5475
|
+
if (!room) {
|
|
5476
|
+
throw new Error(`Expected a signing room for transaction ${record.transactionId}`);
|
|
5453
5477
|
}
|
|
5454
5478
|
stage = 'ceremony';
|
|
5455
5479
|
onSignPhase == null ? void 0 : onSignPhase('ceremony');
|
|
5456
|
-
const ceremonies =
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5480
|
+
const ceremonies = this.clientSign({
|
|
5481
|
+
chainName,
|
|
5482
|
+
message,
|
|
5483
|
+
roomId: room.roomId,
|
|
5484
|
+
keyShare: clientKeyShares[0],
|
|
5485
|
+
derivationPath,
|
|
5486
|
+
isFormatted: false,
|
|
5487
|
+
dynamicRequestId,
|
|
5488
|
+
traceContext,
|
|
5489
|
+
variant: wallet.variant,
|
|
5490
|
+
// Nothing mints a replacement room for a broadcast.
|
|
5491
|
+
skipForwardMPC: true
|
|
5492
|
+
});
|
|
5469
5493
|
const getClientSignMismatch = this.trackClientSignMismatch(ceremonies, {
|
|
5470
5494
|
accountAddress,
|
|
5471
5495
|
chainName,
|
|
@@ -5496,7 +5520,7 @@ class DynamicWalletClient {
|
|
|
5496
5520
|
this.logger.info('[DynamicWaasWalletClient] Client sign completed', _extends({
|
|
5497
5521
|
accountAddress,
|
|
5498
5522
|
chainName,
|
|
5499
|
-
|
|
5523
|
+
roomId: room.roomId,
|
|
5500
5524
|
dynamicRequestId,
|
|
5501
5525
|
key: 'signAndBroadcast',
|
|
5502
5526
|
operation: 'clientSign'
|
|
@@ -5506,9 +5530,9 @@ class DynamicWalletClient {
|
|
|
5506
5530
|
const retryParams = {
|
|
5507
5531
|
accountAddress,
|
|
5508
5532
|
chainName,
|
|
5509
|
-
|
|
5533
|
+
context,
|
|
5510
5534
|
broadcastOptions,
|
|
5511
|
-
|
|
5535
|
+
message,
|
|
5512
5536
|
password,
|
|
5513
5537
|
signedSessionId,
|
|
5514
5538
|
mfaToken,
|
|
@@ -5520,6 +5544,7 @@ class DynamicWalletClient {
|
|
|
5520
5544
|
onError,
|
|
5521
5545
|
traceContext,
|
|
5522
5546
|
onSignPhase,
|
|
5547
|
+
onSignDetails,
|
|
5523
5548
|
onReadingShares
|
|
5524
5549
|
};
|
|
5525
5550
|
// The server rejected the local shares as stale before opening any room. Heal them
|
|
@@ -5551,9 +5576,9 @@ class DynamicWalletClient {
|
|
|
5551
5576
|
}));
|
|
5552
5577
|
}
|
|
5553
5578
|
// A cached room was already claimed by an earlier ceremony. Retry once with the same
|
|
5554
|
-
// idempotency key and no cached
|
|
5579
|
+
// idempotency key and no cached room, so the server opens a fresh one.
|
|
5555
5580
|
if (!roomIdRetry && isRoomIdAlreadyUsedError(error)) {
|
|
5556
|
-
this.logger.info('[RoomCache] retrying sign and broadcast without cached
|
|
5581
|
+
this.logger.info('[RoomCache] retrying sign and broadcast without a cached room after 409 ROOM_ID_ALREADY_USED', _extends({
|
|
5557
5582
|
key: 'signAndBroadcast',
|
|
5558
5583
|
operation: 'roomIdRetry',
|
|
5559
5584
|
accountAddress,
|
|
@@ -5576,7 +5601,6 @@ class DynamicWalletClient {
|
|
|
5576
5601
|
operation: 'signAndBroadcast',
|
|
5577
5602
|
details: {
|
|
5578
5603
|
stage,
|
|
5579
|
-
attemptCount: messages.length,
|
|
5580
5604
|
roomIdSource,
|
|
5581
5605
|
staleShareRetry,
|
|
5582
5606
|
roomIdRetry
|
|
@@ -5614,95 +5638,67 @@ class DynamicWalletClient {
|
|
|
5614
5638
|
}, this.getTraceContext(traceContext)));
|
|
5615
5639
|
}
|
|
5616
5640
|
/**
|
|
5617
|
-
* Takes one already-opened room
|
|
5641
|
+
* Takes one already-opened room from the local pool for a sign or a sign-and-broadcast, or none when the cache is off or empty, so the server opens one instead.
|
|
5618
5642
|
*
|
|
5619
|
-
* @param wallet - The wallet signing; its key scheme decides which
|
|
5620
|
-
* @param accountAddress - The wallet's address, for logs.
|
|
5621
|
-
* @param chainName - The wallet's chain, for logs.
|
|
5622
|
-
* @param
|
|
5623
|
-
* @param dynamicRequestId - Id of this request, so the pool's logs join the
|
|
5624
|
-
* @param traceContext - Trace ids for the same reason.
|
|
5625
|
-
* @returns The room
|
|
5626
|
-
*/ async
|
|
5643
|
+
* @param params.wallet - The wallet signing; its key scheme decides which room fits.
|
|
5644
|
+
* @param params.accountAddress - The wallet's address, for logs.
|
|
5645
|
+
* @param params.chainName - The wallet's chain, for logs.
|
|
5646
|
+
* @param params.key - Which operation is asking, for logs.
|
|
5647
|
+
* @param params.dynamicRequestId - Id of this request, so the pool's logs join the operation's.
|
|
5648
|
+
* @param params.traceContext - Trace ids for the same reason.
|
|
5649
|
+
* @returns The room id, or `undefined`.
|
|
5650
|
+
*/ async getCachedRoom({ wallet, accountAddress, chainName, key, dynamicRequestId, traceContext }) {
|
|
5627
5651
|
const logContext = _extends({
|
|
5628
5652
|
dynamicRequestId
|
|
5629
5653
|
}, this.getTraceContext(traceContext));
|
|
5630
5654
|
if (this.featureFlags[FEATURE_FLAGS.ENABLE_ROOM_CACHE_FLAG] !== true) {
|
|
5631
|
-
this.
|
|
5655
|
+
this.logCachedRoomUnavailable({
|
|
5632
5656
|
reason: 'cache_disabled',
|
|
5657
|
+
key,
|
|
5633
5658
|
accountAddress,
|
|
5634
5659
|
chainName,
|
|
5635
|
-
count,
|
|
5636
5660
|
logContext
|
|
5637
5661
|
});
|
|
5638
5662
|
return undefined;
|
|
5639
5663
|
}
|
|
5640
|
-
const
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
});
|
|
5646
|
-
if (rooms.length === 0) {
|
|
5647
|
-
this.logCachedRoomsUnavailable({
|
|
5648
|
-
reason: 'pool_too_small',
|
|
5664
|
+
const room = await this.getRoom(RoomTypeEnum.Threshold, wallet.thresholdSignatureScheme, logContext);
|
|
5665
|
+
if (!room) {
|
|
5666
|
+
this.logCachedRoomUnavailable({
|
|
5667
|
+
reason: 'pool_empty',
|
|
5668
|
+
key,
|
|
5649
5669
|
accountAddress,
|
|
5650
5670
|
chainName,
|
|
5651
|
-
count,
|
|
5652
5671
|
logContext
|
|
5653
5672
|
});
|
|
5654
5673
|
return undefined;
|
|
5655
5674
|
}
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
operation: 'cachedRoomsFound',
|
|
5675
|
+
this.logger.info('[DynamicWaasWalletClient] Using cached room', _extends({
|
|
5676
|
+
key,
|
|
5677
|
+
operation: 'cachedRoomFound',
|
|
5660
5678
|
accountAddress,
|
|
5661
5679
|
chainName,
|
|
5662
5680
|
walletId: wallet.walletId,
|
|
5663
|
-
|
|
5681
|
+
roomId: room.roomId
|
|
5664
5682
|
}, logContext));
|
|
5665
|
-
return
|
|
5683
|
+
return room.roomId;
|
|
5666
5684
|
}
|
|
5667
5685
|
/**
|
|
5668
|
-
* Logs why no cached
|
|
5686
|
+
* Logs why no cached room was sent, so how often the cache covers a request can be measured.
|
|
5669
5687
|
*
|
|
5670
|
-
* @param params.reason - `cache_disabled` or `
|
|
5688
|
+
* @param params.reason - `cache_disabled` or `pool_empty`.
|
|
5689
|
+
* @param params.key - Which operation asked.
|
|
5671
5690
|
* @param params.accountAddress - The wallet's address.
|
|
5672
5691
|
* @param params.chainName - The wallet's chain.
|
|
5673
|
-
* @param params.count - How many rooms were needed.
|
|
5674
5692
|
* @param params.logContext - Request and trace ids to attach.
|
|
5675
|
-
*/
|
|
5676
|
-
this.logger.info('[DynamicWaasWalletClient] Cached
|
|
5677
|
-
key
|
|
5678
|
-
operation: '
|
|
5693
|
+
*/ logCachedRoomUnavailable({ reason, key, accountAddress, chainName, logContext }) {
|
|
5694
|
+
this.logger.info('[DynamicWaasWalletClient] Cached room unavailable; the server opens one', _extends({
|
|
5695
|
+
key,
|
|
5696
|
+
operation: 'cachedRoomUnavailable',
|
|
5679
5697
|
reason,
|
|
5680
5698
|
accountAddress,
|
|
5681
|
-
chainName
|
|
5682
|
-
requestedCount: count
|
|
5699
|
+
chainName
|
|
5683
5700
|
}, logContext));
|
|
5684
5701
|
}
|
|
5685
|
-
async getCachedRoomForSign(wallet, accountAddress, chainName, dynamicRequestId, traceContext) {
|
|
5686
|
-
if (this.featureFlags[FEATURE_FLAGS.ENABLE_ROOM_CACHE_FLAG] !== true) {
|
|
5687
|
-
return undefined;
|
|
5688
|
-
}
|
|
5689
|
-
const logContext = _extends({
|
|
5690
|
-
dynamicRequestId
|
|
5691
|
-
}, this.getTraceContext(traceContext));
|
|
5692
|
-
const room = await this.getRoom(RoomTypeEnum.Threshold, wallet.thresholdSignatureScheme, logContext);
|
|
5693
|
-
const roomId = room == null ? void 0 : room.roomId;
|
|
5694
|
-
if (roomId) {
|
|
5695
|
-
this.logger.info('[DynamicWaasWalletClient] Using cached room', _extends({
|
|
5696
|
-
key: 'sign',
|
|
5697
|
-
operation: 'cachedRoomFound',
|
|
5698
|
-
accountAddress,
|
|
5699
|
-
chainName,
|
|
5700
|
-
walletId: wallet.walletId,
|
|
5701
|
-
roomId
|
|
5702
|
-
}, logContext));
|
|
5703
|
-
}
|
|
5704
|
-
return roomId;
|
|
5705
|
-
}
|
|
5706
5702
|
// Without a cached room the client depends on the server reporting one
|
|
5707
5703
|
// before it can join the ceremony.
|
|
5708
5704
|
async resolveSignRoom({ cachedRoomId, serverSignPromise, accountAddress, chainName, walletId, dynamicRequestId, traceContext }) {
|
|
@@ -5892,7 +5888,14 @@ class DynamicWalletClient {
|
|
|
5892
5888
|
onReadingShares
|
|
5893
5889
|
});
|
|
5894
5890
|
onSignPhase == null ? void 0 : onSignPhase('resolveCachedRoom');
|
|
5895
|
-
const cachedRoomId = roomIdRetry ? undefined : await this.
|
|
5891
|
+
const cachedRoomId = roomIdRetry ? undefined : await this.getCachedRoom({
|
|
5892
|
+
wallet,
|
|
5893
|
+
accountAddress,
|
|
5894
|
+
chainName,
|
|
5895
|
+
key: 'sign',
|
|
5896
|
+
dynamicRequestId,
|
|
5897
|
+
traceContext
|
|
5898
|
+
});
|
|
5896
5899
|
const roomResolvedAt = Date.now();
|
|
5897
5900
|
this.logSlowSignPreflight({
|
|
5898
5901
|
accountAddress,
|
|
@@ -11209,6 +11212,17 @@ class DynamicWalletClient {
|
|
|
11209
11212
|
}
|
|
11210
11213
|
this.apiClient.syncAuthToken(authToken);
|
|
11211
11214
|
}
|
|
11215
|
+
/** Reads the persisted room pool. A corrupt value counts as empty, so the next write replaces it. */ async readStoredRooms() {
|
|
11216
|
+
const rawData = await this.storage.getItem(`${this.storageKey}-rooms`);
|
|
11217
|
+
if (!rawData) {
|
|
11218
|
+
return {};
|
|
11219
|
+
}
|
|
11220
|
+
try {
|
|
11221
|
+
return JSON.parse(rawData);
|
|
11222
|
+
} catch (e) {
|
|
11223
|
+
return {};
|
|
11224
|
+
}
|
|
11225
|
+
}
|
|
11212
11226
|
async createRooms({ roomType, thresholdSignatureScheme, roomCount = 5 }) {
|
|
11213
11227
|
const numberOfParties = this.getNumberOfParties(roomType, thresholdSignatureScheme);
|
|
11214
11228
|
const generation = DynamicWalletClient.roomsGeneration;
|
|
@@ -11250,8 +11264,7 @@ class DynamicWalletClient {
|
|
|
11250
11264
|
// Re-read from storage under the lock so we don't clobber rooms another
|
|
11251
11265
|
// tab just wrote, and so we don't lose rooms consumed by getRoom() calls
|
|
11252
11266
|
// that ran while our API request was in-flight.
|
|
11253
|
-
const
|
|
11254
|
-
const storedRooms = rawData ? JSON.parse(rawData) : {};
|
|
11267
|
+
const storedRooms = await this.readStoredRooms();
|
|
11255
11268
|
const currentRooms = storedRooms[numberOfParties] || [];
|
|
11256
11269
|
await this.setRooms(numberOfParties, [
|
|
11257
11270
|
...newRooms,
|
|
@@ -11368,8 +11381,7 @@ class DynamicWalletClient {
|
|
|
11368
11381
|
// already consumed by other tabs. Combined with the Web Locks API below, this
|
|
11369
11382
|
// makes room consumption atomic across tabs and prevents two tabs from both
|
|
11370
11383
|
// receiving the same cached room.
|
|
11371
|
-
const
|
|
11372
|
-
const storedRooms = rawData ? JSON.parse(rawData) : {};
|
|
11384
|
+
const storedRooms = await this.readStoredRooms();
|
|
11373
11385
|
const now = Date.now();
|
|
11374
11386
|
const schemeRooms = storedRooms[numberOfParties] || [];
|
|
11375
11387
|
// Filter out expired rooms and remove the next available room.
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs-wallet/browser",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.43",
|
|
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": "1.1.
|
|
7
|
+
"@dynamic-labs-wallet/core": "1.1.43",
|
|
8
8
|
"@dynamic-labs-wallet/forward-mpc-client": "2.0.2",
|
|
9
|
-
"@dynamic-labs-wallet/primitives": "1.1.
|
|
9
|
+
"@dynamic-labs-wallet/primitives": "1.1.43",
|
|
10
10
|
"@dynamic-labs/redaction": "^1.1.0",
|
|
11
11
|
"@dynamic-labs/sdk-api-core": "0.50.0",
|
|
12
12
|
"@noble/curves": "1.8.0",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/backup/password/errors.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAElF;;;;;;;GAOG;AACH,MAAM,MAAM,yBAAyB,GACjC,gBAAgB,GAChB,oBAAoB,GACpB,oBAAoB,GACpB,yBAAyB,GACzB,2BAA2B,GAC3B,gBAAgB,GAChB,oBAAoB,GACpB,oBAAoB,GACpB,gBAAgB,GAChB,oBAAoB,GACpB,mBAAmB,GACnB,2BAA2B,GAC3B,kBAAkB,GAClB,oBAAoB,GACpB,SAAS,GACT,SAAS,CAAC;AAEd;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6CAA6C;;;;;;;;;;;;;;;;;CAiBK,CAAC;AAEhE,eAAO,MAAM,yCAAyC,WAAY,yBAAyB,KAAG,OACvC,CAAC;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/backup/password/errors.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAElF;;;;;;;GAOG;AACH,MAAM,MAAM,yBAAyB,GACjC,gBAAgB,GAChB,oBAAoB,GACpB,oBAAoB,GACpB,yBAAyB,GACzB,2BAA2B,GAC3B,gBAAgB,GAChB,oBAAoB,GACpB,oBAAoB,GACpB,gBAAgB,GAChB,oBAAoB,GACpB,mBAAmB,GACnB,2BAA2B,GAC3B,kBAAkB,GAClB,oBAAoB,GACpB,SAAS,GACT,SAAS,CAAC;AAEd;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6CAA6C;;;;;;;;;;;;;;;;;CAiBK,CAAC;AAEhE,eAAO,MAAM,yCAAyC,WAAY,yBAAyB,KAAG,OACvC,CAAC;AA0KxD;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,UAAW,OAAO,KAAG,yBAM5D,CAAC"}
|