@dynamic-labs-wallet/browser 1.0.59 → 1.0.60
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 +86 -11
- package/index.esm.js +86 -11
- package/package.json +3 -3
- package/src/services/signedSession.d.ts +26 -0
- package/src/services/signedSession.d.ts.map +1 -1
package/index.cjs
CHANGED
|
@@ -2590,6 +2590,48 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2590
2590
|
}, logger);
|
|
2591
2591
|
}
|
|
2592
2592
|
|
|
2593
|
+
/**
|
|
2594
|
+
* Datadog event name emitted on every signed-session nonce rejection (a 400
|
|
2595
|
+
* from the backup/recovery endpoints). Log-only: lets us split the failure
|
|
2596
|
+
* bucket by `reason` and measure the true per-wallet unrecoverable rate
|
|
2597
|
+
* without changing any retry behavior. See DYNT-1599.
|
|
2598
|
+
*/ const NONCE_FAILURE_EVENT = 'waas.signed_session.nonce_failure';
|
|
2599
|
+
/** Best-effort extraction of the server error `code`/`message` from a 400 body. */ const readErrorBody = (error)=>{
|
|
2600
|
+
var _error_response;
|
|
2601
|
+
if (!(error instanceof axios.AxiosError)) {
|
|
2602
|
+
return {};
|
|
2603
|
+
}
|
|
2604
|
+
const data = (_error_response = error.response) == null ? void 0 : _error_response.data;
|
|
2605
|
+
if (typeof data === 'string') {
|
|
2606
|
+
return {
|
|
2607
|
+
message: data
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
if (!data || typeof data !== 'object') {
|
|
2611
|
+
return {};
|
|
2612
|
+
}
|
|
2613
|
+
const record = data;
|
|
2614
|
+
// Prefer a string `code`, else fall back to a string `error`.
|
|
2615
|
+
const code = [
|
|
2616
|
+
record['code'],
|
|
2617
|
+
record['error']
|
|
2618
|
+
].find((value)=>typeof value === 'string');
|
|
2619
|
+
const message = typeof record['message'] === 'string' ? record['message'] : undefined;
|
|
2620
|
+
return {
|
|
2621
|
+
code,
|
|
2622
|
+
message
|
|
2623
|
+
};
|
|
2624
|
+
};
|
|
2625
|
+
/** Maps the server error `code`/`message` onto a {@link NonceFailureReason}. */ const classifyNonceFailure = ({ code, message })=>{
|
|
2626
|
+
const haystack = `${code != null ? code : ''} ${message != null ? message : ''}`.toLowerCase();
|
|
2627
|
+
if (haystack.includes('already_used') || haystack.includes('already used') || haystack.includes('consumption failed')) {
|
|
2628
|
+
return 'nonce_already_used';
|
|
2629
|
+
}
|
|
2630
|
+
if (haystack.includes('invalid_nonce_signature') || haystack.includes('invalid nonce signature')) {
|
|
2631
|
+
return 'invalid_nonce_signature';
|
|
2632
|
+
}
|
|
2633
|
+
return 'other';
|
|
2634
|
+
};
|
|
2593
2635
|
/**
|
|
2594
2636
|
* Owns everything about signed sessions and their single-use replay nonces:
|
|
2595
2637
|
* resolving a session from an explicit value or the host reverse channel,
|
|
@@ -2632,26 +2674,57 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2632
2674
|
*/ refreshShouldRetry({ onRefreshed, operationName, logContext, alsoRetry }) {
|
|
2633
2675
|
return async (error, attempt)=>{
|
|
2634
2676
|
const status = getHttpStatus(error);
|
|
2635
|
-
if (status === 400
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2677
|
+
if (status === 400) {
|
|
2678
|
+
const willRefresh = attempt === 1 && this.canRefresh && this.supportsReverseChannel();
|
|
2679
|
+
this.instrumentNonceFailure({
|
|
2680
|
+
error,
|
|
2681
|
+
attempt,
|
|
2682
|
+
operationName,
|
|
2683
|
+
willRefresh,
|
|
2684
|
+
logContext
|
|
2685
|
+
});
|
|
2686
|
+
if (attempt === 1 && this.canRefresh) {
|
|
2687
|
+
// canRefresh only proves a local callback exists, not that the host answers it.
|
|
2688
|
+
if (!this.supportsReverseChannel()) {
|
|
2689
|
+
this.logger.warn(`[${operationName}] host SDK version predates the signed-session reverse channel, declining nonce refresh`, _extends({
|
|
2690
|
+
status
|
|
2691
|
+
}, logContext));
|
|
2692
|
+
return alsoRetry ? alsoRetry(status) : false;
|
|
2693
|
+
}
|
|
2694
|
+
onRefreshed(await this.resolve());
|
|
2695
|
+
this.logger.info(`[${operationName}] refreshed signed session, retrying`, _extends({
|
|
2639
2696
|
status
|
|
2640
2697
|
}, logContext));
|
|
2641
|
-
return
|
|
2698
|
+
return true;
|
|
2642
2699
|
}
|
|
2643
|
-
onRefreshed(await this.resolve());
|
|
2644
|
-
this.logger.info(`[${operationName}] refreshed signed session, retrying`, _extends({
|
|
2645
|
-
status
|
|
2646
|
-
}, logContext));
|
|
2647
|
-
return true;
|
|
2648
2700
|
}
|
|
2649
2701
|
return alsoRetry ? alsoRetry(status) : false;
|
|
2650
2702
|
};
|
|
2651
2703
|
}
|
|
2704
|
+
/**
|
|
2705
|
+
* Emits a single Datadog log per signed-session nonce rejection so the
|
|
2706
|
+
* failure bucket can be split by `reason` and correlated per wallet/request.
|
|
2707
|
+
* Purely observational — it never alters the retry decision.
|
|
2708
|
+
*/ instrumentNonceFailure({ error, attempt, operationName, willRefresh, logContext }) {
|
|
2709
|
+
const body = readErrorBody(error);
|
|
2710
|
+
this.logger.info(NONCE_FAILURE_EVENT, _extends({}, logContext, {
|
|
2711
|
+
operationName,
|
|
2712
|
+
attempt,
|
|
2713
|
+
status: 400,
|
|
2714
|
+
reason: classifyNonceFailure(body),
|
|
2715
|
+
errorCode: body.code,
|
|
2716
|
+
errorMessage: body.message,
|
|
2717
|
+
willRefresh,
|
|
2718
|
+
canRefresh: this.canRefresh,
|
|
2719
|
+
reverseChannelSupported: this.reverseChannelSupported,
|
|
2720
|
+
required: this.required,
|
|
2721
|
+
sdkVersion: this.sdkVersion
|
|
2722
|
+
}));
|
|
2723
|
+
}
|
|
2652
2724
|
constructor({ logger, sdkVersion, getCallback }){
|
|
2653
2725
|
this.logger = logger;
|
|
2654
2726
|
this.getCallback = getCallback;
|
|
2727
|
+
this.sdkVersion = sdkVersion;
|
|
2655
2728
|
this.required = isRequiresSignedSessionId(sdkVersion, this.logger);
|
|
2656
2729
|
this.reverseChannelSupported = isReverseChannelSupported(sdkVersion, this.logger);
|
|
2657
2730
|
}
|
|
@@ -6368,7 +6441,9 @@ class DynamicWalletClient {
|
|
|
6368
6441
|
},
|
|
6369
6442
|
operationName,
|
|
6370
6443
|
logContext: {
|
|
6371
|
-
dynamicRequestId
|
|
6444
|
+
dynamicRequestId,
|
|
6445
|
+
walletId: walletData.walletId,
|
|
6446
|
+
sessionPublicKey
|
|
6372
6447
|
},
|
|
6373
6448
|
alsoRetry: (status)=>status === undefined || status === 429 || status !== undefined && status >= 500
|
|
6374
6449
|
})
|
package/index.esm.js
CHANGED
|
@@ -2591,6 +2591,48 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2591
2591
|
}, logger);
|
|
2592
2592
|
}
|
|
2593
2593
|
|
|
2594
|
+
/**
|
|
2595
|
+
* Datadog event name emitted on every signed-session nonce rejection (a 400
|
|
2596
|
+
* from the backup/recovery endpoints). Log-only: lets us split the failure
|
|
2597
|
+
* bucket by `reason` and measure the true per-wallet unrecoverable rate
|
|
2598
|
+
* without changing any retry behavior. See DYNT-1599.
|
|
2599
|
+
*/ const NONCE_FAILURE_EVENT = 'waas.signed_session.nonce_failure';
|
|
2600
|
+
/** Best-effort extraction of the server error `code`/`message` from a 400 body. */ const readErrorBody = (error)=>{
|
|
2601
|
+
var _error_response;
|
|
2602
|
+
if (!(error instanceof AxiosError)) {
|
|
2603
|
+
return {};
|
|
2604
|
+
}
|
|
2605
|
+
const data = (_error_response = error.response) == null ? void 0 : _error_response.data;
|
|
2606
|
+
if (typeof data === 'string') {
|
|
2607
|
+
return {
|
|
2608
|
+
message: data
|
|
2609
|
+
};
|
|
2610
|
+
}
|
|
2611
|
+
if (!data || typeof data !== 'object') {
|
|
2612
|
+
return {};
|
|
2613
|
+
}
|
|
2614
|
+
const record = data;
|
|
2615
|
+
// Prefer a string `code`, else fall back to a string `error`.
|
|
2616
|
+
const code = [
|
|
2617
|
+
record['code'],
|
|
2618
|
+
record['error']
|
|
2619
|
+
].find((value)=>typeof value === 'string');
|
|
2620
|
+
const message = typeof record['message'] === 'string' ? record['message'] : undefined;
|
|
2621
|
+
return {
|
|
2622
|
+
code,
|
|
2623
|
+
message
|
|
2624
|
+
};
|
|
2625
|
+
};
|
|
2626
|
+
/** Maps the server error `code`/`message` onto a {@link NonceFailureReason}. */ const classifyNonceFailure = ({ code, message })=>{
|
|
2627
|
+
const haystack = `${code != null ? code : ''} ${message != null ? message : ''}`.toLowerCase();
|
|
2628
|
+
if (haystack.includes('already_used') || haystack.includes('already used') || haystack.includes('consumption failed')) {
|
|
2629
|
+
return 'nonce_already_used';
|
|
2630
|
+
}
|
|
2631
|
+
if (haystack.includes('invalid_nonce_signature') || haystack.includes('invalid nonce signature')) {
|
|
2632
|
+
return 'invalid_nonce_signature';
|
|
2633
|
+
}
|
|
2634
|
+
return 'other';
|
|
2635
|
+
};
|
|
2594
2636
|
/**
|
|
2595
2637
|
* Owns everything about signed sessions and their single-use replay nonces:
|
|
2596
2638
|
* resolving a session from an explicit value or the host reverse channel,
|
|
@@ -2633,26 +2675,57 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2633
2675
|
*/ refreshShouldRetry({ onRefreshed, operationName, logContext, alsoRetry }) {
|
|
2634
2676
|
return async (error, attempt)=>{
|
|
2635
2677
|
const status = getHttpStatus(error);
|
|
2636
|
-
if (status === 400
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2678
|
+
if (status === 400) {
|
|
2679
|
+
const willRefresh = attempt === 1 && this.canRefresh && this.supportsReverseChannel();
|
|
2680
|
+
this.instrumentNonceFailure({
|
|
2681
|
+
error,
|
|
2682
|
+
attempt,
|
|
2683
|
+
operationName,
|
|
2684
|
+
willRefresh,
|
|
2685
|
+
logContext
|
|
2686
|
+
});
|
|
2687
|
+
if (attempt === 1 && this.canRefresh) {
|
|
2688
|
+
// canRefresh only proves a local callback exists, not that the host answers it.
|
|
2689
|
+
if (!this.supportsReverseChannel()) {
|
|
2690
|
+
this.logger.warn(`[${operationName}] host SDK version predates the signed-session reverse channel, declining nonce refresh`, _extends({
|
|
2691
|
+
status
|
|
2692
|
+
}, logContext));
|
|
2693
|
+
return alsoRetry ? alsoRetry(status) : false;
|
|
2694
|
+
}
|
|
2695
|
+
onRefreshed(await this.resolve());
|
|
2696
|
+
this.logger.info(`[${operationName}] refreshed signed session, retrying`, _extends({
|
|
2640
2697
|
status
|
|
2641
2698
|
}, logContext));
|
|
2642
|
-
return
|
|
2699
|
+
return true;
|
|
2643
2700
|
}
|
|
2644
|
-
onRefreshed(await this.resolve());
|
|
2645
|
-
this.logger.info(`[${operationName}] refreshed signed session, retrying`, _extends({
|
|
2646
|
-
status
|
|
2647
|
-
}, logContext));
|
|
2648
|
-
return true;
|
|
2649
2701
|
}
|
|
2650
2702
|
return alsoRetry ? alsoRetry(status) : false;
|
|
2651
2703
|
};
|
|
2652
2704
|
}
|
|
2705
|
+
/**
|
|
2706
|
+
* Emits a single Datadog log per signed-session nonce rejection so the
|
|
2707
|
+
* failure bucket can be split by `reason` and correlated per wallet/request.
|
|
2708
|
+
* Purely observational — it never alters the retry decision.
|
|
2709
|
+
*/ instrumentNonceFailure({ error, attempt, operationName, willRefresh, logContext }) {
|
|
2710
|
+
const body = readErrorBody(error);
|
|
2711
|
+
this.logger.info(NONCE_FAILURE_EVENT, _extends({}, logContext, {
|
|
2712
|
+
operationName,
|
|
2713
|
+
attempt,
|
|
2714
|
+
status: 400,
|
|
2715
|
+
reason: classifyNonceFailure(body),
|
|
2716
|
+
errorCode: body.code,
|
|
2717
|
+
errorMessage: body.message,
|
|
2718
|
+
willRefresh,
|
|
2719
|
+
canRefresh: this.canRefresh,
|
|
2720
|
+
reverseChannelSupported: this.reverseChannelSupported,
|
|
2721
|
+
required: this.required,
|
|
2722
|
+
sdkVersion: this.sdkVersion
|
|
2723
|
+
}));
|
|
2724
|
+
}
|
|
2653
2725
|
constructor({ logger, sdkVersion, getCallback }){
|
|
2654
2726
|
this.logger = logger;
|
|
2655
2727
|
this.getCallback = getCallback;
|
|
2728
|
+
this.sdkVersion = sdkVersion;
|
|
2656
2729
|
this.required = isRequiresSignedSessionId(sdkVersion, this.logger);
|
|
2657
2730
|
this.reverseChannelSupported = isReverseChannelSupported(sdkVersion, this.logger);
|
|
2658
2731
|
}
|
|
@@ -6369,7 +6442,9 @@ class DynamicWalletClient {
|
|
|
6369
6442
|
},
|
|
6370
6443
|
operationName,
|
|
6371
6444
|
logContext: {
|
|
6372
|
-
dynamicRequestId
|
|
6445
|
+
dynamicRequestId,
|
|
6446
|
+
walletId: walletData.walletId,
|
|
6447
|
+
sessionPublicKey
|
|
6373
6448
|
},
|
|
6374
6449
|
alsoRetry: (status)=>status === undefined || status === 429 || status !== undefined && status >= 500
|
|
6375
6450
|
})
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs-wallet/browser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.60",
|
|
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.0.
|
|
7
|
+
"@dynamic-labs-wallet/core": "1.0.60",
|
|
8
8
|
"@dynamic-labs-wallet/forward-mpc-client": "1.0.1",
|
|
9
|
-
"@dynamic-labs-wallet/primitives": "1.0.
|
|
9
|
+
"@dynamic-labs-wallet/primitives": "1.0.60",
|
|
10
10
|
"@dynamic-labs/sdk-api-core": "^0.0.984",
|
|
11
11
|
"argon2id": "1.0.1",
|
|
12
12
|
"axios": "1.16.0",
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import type { ILogger } from '@dynamic-labs-wallet/core';
|
|
2
2
|
/** Predicate matching `retryPromise`'s `shouldRetry` option. */
|
|
3
3
|
type ShouldRetry = (error: unknown, attempt: number) => boolean | Promise<boolean>;
|
|
4
|
+
/**
|
|
5
|
+
* Datadog event name emitted on every signed-session nonce rejection (a 400
|
|
6
|
+
* from the backup/recovery endpoints). Log-only: lets us split the failure
|
|
7
|
+
* bucket by `reason` and measure the true per-wallet unrecoverable rate
|
|
8
|
+
* without changing any retry behavior. See DYNT-1599.
|
|
9
|
+
*/
|
|
10
|
+
export declare const NONCE_FAILURE_EVENT = "waas.signed_session.nonce_failure";
|
|
11
|
+
/**
|
|
12
|
+
* Coarse cause of a signed-session nonce rejection. `invalid_nonce_signature`
|
|
13
|
+
* (a bad/stale signing key) cannot be fixed by refreshing the nonce, whereas
|
|
14
|
+
* `nonce_already_used` (a consumed replay nonce) can — so splitting them tells
|
|
15
|
+
* us whether the refresh-and-retry path can actually help.
|
|
16
|
+
*/
|
|
17
|
+
export type NonceFailureReason = 'invalid_nonce_signature' | 'nonce_already_used' | 'other';
|
|
18
|
+
/** Maps the server error `code`/`message` onto a {@link NonceFailureReason}. */
|
|
19
|
+
export declare const classifyNonceFailure: ({ code, message }: {
|
|
20
|
+
code?: string;
|
|
21
|
+
message?: string;
|
|
22
|
+
}) => NonceFailureReason;
|
|
4
23
|
/**
|
|
5
24
|
* Owns everything about signed sessions and their single-use replay nonces:
|
|
6
25
|
* resolving a session from an explicit value or the host reverse channel,
|
|
@@ -15,6 +34,7 @@ type ShouldRetry = (error: unknown, attempt: number) => boolean | Promise<boolea
|
|
|
15
34
|
export declare class SignedSessionManager {
|
|
16
35
|
private readonly logger;
|
|
17
36
|
private readonly getCallback;
|
|
37
|
+
private readonly sdkVersion?;
|
|
18
38
|
private readonly required;
|
|
19
39
|
private readonly reverseChannelSupported;
|
|
20
40
|
constructor({ logger, sdkVersion, getCallback, }: {
|
|
@@ -48,6 +68,12 @@ export declare class SignedSessionManager {
|
|
|
48
68
|
logContext?: Record<string, unknown>;
|
|
49
69
|
alsoRetry?: (status: number | undefined) => boolean;
|
|
50
70
|
}): ShouldRetry;
|
|
71
|
+
/**
|
|
72
|
+
* Emits a single Datadog log per signed-session nonce rejection so the
|
|
73
|
+
* failure bucket can be split by `reason` and correlated per wallet/request.
|
|
74
|
+
* Purely observational — it never alters the retry decision.
|
|
75
|
+
*/
|
|
76
|
+
private instrumentNonceFailure;
|
|
51
77
|
}
|
|
52
78
|
export {};
|
|
53
79
|
//# sourceMappingURL=signedSession.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signedSession.d.ts","sourceRoot":"","sources":["../../src/services/signedSession.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"signedSession.d.ts","sourceRoot":"","sources":["../../src/services/signedSession.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAIzD,gEAAgE;AAChE,KAAK,WAAW,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEnF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,sCAAsC,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,yBAAyB,GAAG,oBAAoB,GAAG,OAAO,CAAC;AAqB5F,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,sBAAuB;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,kBAa7F,CAAC;AAEF;;;;;;;;;;GAUG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA4C;IAGxE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAU;gBAEtC,EACV,MAAM,EACN,UAAU,EACV,WAAW,GACZ,EAAE;QACD,MAAM,EAAE,OAAO,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;KACxD;IAQD,+EAA+E;IAC/E,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;;OAGG;IACG,OAAO,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAaxD,uEAAuE;IACvE,UAAU,IAAI,OAAO;IAIrB,2GAA2G;IAC3G,sBAAsB,IAAI,OAAO;IAIjC;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EACjB,WAAW,EACX,aAAa,EACb,UAAU,EACV,SAAS,GACV,EAAE;QACD,WAAW,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/C,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC;KACrD,GAAG,WAAW;IAyBf;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;CA6B/B"}
|