@dynamic-labs-wallet/browser 1.0.79 → 1.0.81
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 +40 -19
- package/index.esm.js +40 -19
- package/package.json +3 -3
- package/src/services/signedSession.d.ts +8 -6
- package/src/services/signedSession.d.ts.map +1 -1
package/index.cjs
CHANGED
|
@@ -2651,12 +2651,20 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2651
2651
|
}
|
|
2652
2652
|
|
|
2653
2653
|
/**
|
|
2654
|
-
* Datadog event name emitted on every signed-session nonce rejection (a 400
|
|
2655
|
-
* from the backup/recovery endpoints). Log-only: lets us split the failure
|
|
2654
|
+
* Datadog event name emitted on every signed-session nonce rejection (a 400 or
|
|
2655
|
+
* 401 from the backup/recovery endpoints). Log-only: lets us split the failure
|
|
2656
2656
|
* bucket by `reason` and measure the true per-wallet unrecoverable rate
|
|
2657
2657
|
* without changing any retry behavior. See DYNT-1599.
|
|
2658
2658
|
*/ const NONCE_FAILURE_EVENT = 'waas.signed_session.nonce_failure';
|
|
2659
|
-
/**
|
|
2659
|
+
/**
|
|
2660
|
+
* Reasons that represent a genuine session-key signature mismatch (the 401 the
|
|
2661
|
+
* server returns per DYNT-1282). Used to gate 401 instrumentation so callers
|
|
2662
|
+
* that reuse 401 for unrelated rejections don't pollute the failure metric.
|
|
2663
|
+
*/ const SESSION_SIGNATURE_FAILURE_REASONS = new Set([
|
|
2664
|
+
'invalid_nonce_signature',
|
|
2665
|
+
'invalid_session_signature'
|
|
2666
|
+
]);
|
|
2667
|
+
/** Best-effort extraction of the server error `code`/`message` from a 4xx body. */ const readErrorBody = (error)=>{
|
|
2660
2668
|
var _error_response;
|
|
2661
2669
|
if (!(error instanceof axios.AxiosError)) {
|
|
2662
2670
|
return {};
|
|
@@ -2671,9 +2679,11 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2671
2679
|
return {};
|
|
2672
2680
|
}
|
|
2673
2681
|
const record = data;
|
|
2674
|
-
// Prefer a string `code`, else fall back to
|
|
2682
|
+
// Prefer a string `code`, else fall back to `error_code` (the field the Key
|
|
2683
|
+
// Share Service emits, see KSS #271) or a bare `error`.
|
|
2675
2684
|
const code = [
|
|
2676
2685
|
record['code'],
|
|
2686
|
+
record['error_code'],
|
|
2677
2687
|
record['error']
|
|
2678
2688
|
].find((value)=>typeof value === 'string');
|
|
2679
2689
|
const message = typeof record['message'] === 'string' ? record['message'] : undefined;
|
|
@@ -2690,6 +2700,9 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2690
2700
|
if (haystack.includes('invalid_nonce_signature') || haystack.includes('invalid nonce signature')) {
|
|
2691
2701
|
return 'invalid_nonce_signature';
|
|
2692
2702
|
}
|
|
2703
|
+
if (haystack.includes('invalid_session_signature') || haystack.includes('invalid session public key') || haystack.includes('invalid session signature')) {
|
|
2704
|
+
return 'invalid_session_signature';
|
|
2705
|
+
}
|
|
2693
2706
|
return 'other';
|
|
2694
2707
|
};
|
|
2695
2708
|
/**
|
|
@@ -2734,29 +2747,37 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2734
2747
|
*/ refreshShouldRetry({ onRefreshed, operationName, logContext, alsoRetry }) {
|
|
2735
2748
|
return async (error, attempt)=>{
|
|
2736
2749
|
const status = getHttpStatus(error);
|
|
2737
|
-
|
|
2738
|
-
|
|
2750
|
+
// A 400 is a refreshable replay nonce; a 401 is a session-key mismatch the
|
|
2751
|
+
// server returns to break the refresh loop (DYNT-1282) — logged only when the
|
|
2752
|
+
// body classifies as a genuine session-signature failure so unrelated 401s
|
|
2753
|
+
// (e.g. an exhausted add-signer grant) don't pollute the DYNT-1599 metric.
|
|
2754
|
+
// Only the 400 can actually refresh.
|
|
2755
|
+
const isSessionSignatureFailure = status === 401 && SESSION_SIGNATURE_FAILURE_REASONS.has(classifyNonceFailure(readErrorBody(error)));
|
|
2756
|
+
const shouldInstrument = status === 400 || isSessionSignatureFailure;
|
|
2757
|
+
if (shouldInstrument) {
|
|
2758
|
+
const willRefresh = status === 400 && attempt === 1 && this.canRefresh && this.supportsReverseChannel();
|
|
2739
2759
|
this.instrumentNonceFailure({
|
|
2740
2760
|
error,
|
|
2741
2761
|
attempt,
|
|
2762
|
+
status,
|
|
2742
2763
|
operationName,
|
|
2743
2764
|
willRefresh,
|
|
2744
2765
|
logContext
|
|
2745
2766
|
});
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
}, logContext));
|
|
2752
|
-
return alsoRetry ? alsoRetry(status) : false;
|
|
2753
|
-
}
|
|
2754
|
-
onRefreshed(await this.resolve());
|
|
2755
|
-
this.logger.info(`[${operationName}] refreshed signed session, retrying`, _extends({
|
|
2767
|
+
}
|
|
2768
|
+
if (status === 400 && attempt === 1 && this.canRefresh) {
|
|
2769
|
+
// canRefresh only proves a local callback exists, not that the host answers it.
|
|
2770
|
+
if (!this.supportsReverseChannel()) {
|
|
2771
|
+
this.logger.warn(`[${operationName}] host SDK version predates the signed-session reverse channel, declining nonce refresh`, _extends({
|
|
2756
2772
|
status
|
|
2757
2773
|
}, logContext));
|
|
2758
|
-
return
|
|
2774
|
+
return alsoRetry ? alsoRetry(status) : false;
|
|
2759
2775
|
}
|
|
2776
|
+
onRefreshed(await this.resolve());
|
|
2777
|
+
this.logger.info(`[${operationName}] refreshed signed session, retrying`, _extends({
|
|
2778
|
+
status
|
|
2779
|
+
}, logContext));
|
|
2780
|
+
return true;
|
|
2760
2781
|
}
|
|
2761
2782
|
return alsoRetry ? alsoRetry(status) : false;
|
|
2762
2783
|
};
|
|
@@ -2765,12 +2786,12 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2765
2786
|
* Emits a single Datadog log per signed-session nonce rejection so the
|
|
2766
2787
|
* failure bucket can be split by `reason` and correlated per wallet/request.
|
|
2767
2788
|
* Purely observational — it never alters the retry decision.
|
|
2768
|
-
*/ instrumentNonceFailure({ error, attempt, operationName, willRefresh, logContext }) {
|
|
2789
|
+
*/ instrumentNonceFailure({ error, attempt, status, operationName, willRefresh, logContext }) {
|
|
2769
2790
|
const body = readErrorBody(error);
|
|
2770
2791
|
this.logger.info(NONCE_FAILURE_EVENT, _extends({}, logContext, {
|
|
2771
2792
|
operationName,
|
|
2772
2793
|
attempt,
|
|
2773
|
-
status
|
|
2794
|
+
status,
|
|
2774
2795
|
reason: classifyNonceFailure(body),
|
|
2775
2796
|
errorCode: body.code,
|
|
2776
2797
|
errorMessage: body.message,
|
package/index.esm.js
CHANGED
|
@@ -2652,12 +2652,20 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2652
2652
|
}
|
|
2653
2653
|
|
|
2654
2654
|
/**
|
|
2655
|
-
* Datadog event name emitted on every signed-session nonce rejection (a 400
|
|
2656
|
-
* from the backup/recovery endpoints). Log-only: lets us split the failure
|
|
2655
|
+
* Datadog event name emitted on every signed-session nonce rejection (a 400 or
|
|
2656
|
+
* 401 from the backup/recovery endpoints). Log-only: lets us split the failure
|
|
2657
2657
|
* bucket by `reason` and measure the true per-wallet unrecoverable rate
|
|
2658
2658
|
* without changing any retry behavior. See DYNT-1599.
|
|
2659
2659
|
*/ const NONCE_FAILURE_EVENT = 'waas.signed_session.nonce_failure';
|
|
2660
|
-
/**
|
|
2660
|
+
/**
|
|
2661
|
+
* Reasons that represent a genuine session-key signature mismatch (the 401 the
|
|
2662
|
+
* server returns per DYNT-1282). Used to gate 401 instrumentation so callers
|
|
2663
|
+
* that reuse 401 for unrelated rejections don't pollute the failure metric.
|
|
2664
|
+
*/ const SESSION_SIGNATURE_FAILURE_REASONS = new Set([
|
|
2665
|
+
'invalid_nonce_signature',
|
|
2666
|
+
'invalid_session_signature'
|
|
2667
|
+
]);
|
|
2668
|
+
/** Best-effort extraction of the server error `code`/`message` from a 4xx body. */ const readErrorBody = (error)=>{
|
|
2661
2669
|
var _error_response;
|
|
2662
2670
|
if (!(error instanceof AxiosError)) {
|
|
2663
2671
|
return {};
|
|
@@ -2672,9 +2680,11 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2672
2680
|
return {};
|
|
2673
2681
|
}
|
|
2674
2682
|
const record = data;
|
|
2675
|
-
// Prefer a string `code`, else fall back to
|
|
2683
|
+
// Prefer a string `code`, else fall back to `error_code` (the field the Key
|
|
2684
|
+
// Share Service emits, see KSS #271) or a bare `error`.
|
|
2676
2685
|
const code = [
|
|
2677
2686
|
record['code'],
|
|
2687
|
+
record['error_code'],
|
|
2678
2688
|
record['error']
|
|
2679
2689
|
].find((value)=>typeof value === 'string');
|
|
2680
2690
|
const message = typeof record['message'] === 'string' ? record['message'] : undefined;
|
|
@@ -2691,6 +2701,9 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2691
2701
|
if (haystack.includes('invalid_nonce_signature') || haystack.includes('invalid nonce signature')) {
|
|
2692
2702
|
return 'invalid_nonce_signature';
|
|
2693
2703
|
}
|
|
2704
|
+
if (haystack.includes('invalid_session_signature') || haystack.includes('invalid session public key') || haystack.includes('invalid session signature')) {
|
|
2705
|
+
return 'invalid_session_signature';
|
|
2706
|
+
}
|
|
2694
2707
|
return 'other';
|
|
2695
2708
|
};
|
|
2696
2709
|
/**
|
|
@@ -2735,29 +2748,37 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2735
2748
|
*/ refreshShouldRetry({ onRefreshed, operationName, logContext, alsoRetry }) {
|
|
2736
2749
|
return async (error, attempt)=>{
|
|
2737
2750
|
const status = getHttpStatus(error);
|
|
2738
|
-
|
|
2739
|
-
|
|
2751
|
+
// A 400 is a refreshable replay nonce; a 401 is a session-key mismatch the
|
|
2752
|
+
// server returns to break the refresh loop (DYNT-1282) — logged only when the
|
|
2753
|
+
// body classifies as a genuine session-signature failure so unrelated 401s
|
|
2754
|
+
// (e.g. an exhausted add-signer grant) don't pollute the DYNT-1599 metric.
|
|
2755
|
+
// Only the 400 can actually refresh.
|
|
2756
|
+
const isSessionSignatureFailure = status === 401 && SESSION_SIGNATURE_FAILURE_REASONS.has(classifyNonceFailure(readErrorBody(error)));
|
|
2757
|
+
const shouldInstrument = status === 400 || isSessionSignatureFailure;
|
|
2758
|
+
if (shouldInstrument) {
|
|
2759
|
+
const willRefresh = status === 400 && attempt === 1 && this.canRefresh && this.supportsReverseChannel();
|
|
2740
2760
|
this.instrumentNonceFailure({
|
|
2741
2761
|
error,
|
|
2742
2762
|
attempt,
|
|
2763
|
+
status,
|
|
2743
2764
|
operationName,
|
|
2744
2765
|
willRefresh,
|
|
2745
2766
|
logContext
|
|
2746
2767
|
});
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
}, logContext));
|
|
2753
|
-
return alsoRetry ? alsoRetry(status) : false;
|
|
2754
|
-
}
|
|
2755
|
-
onRefreshed(await this.resolve());
|
|
2756
|
-
this.logger.info(`[${operationName}] refreshed signed session, retrying`, _extends({
|
|
2768
|
+
}
|
|
2769
|
+
if (status === 400 && attempt === 1 && this.canRefresh) {
|
|
2770
|
+
// canRefresh only proves a local callback exists, not that the host answers it.
|
|
2771
|
+
if (!this.supportsReverseChannel()) {
|
|
2772
|
+
this.logger.warn(`[${operationName}] host SDK version predates the signed-session reverse channel, declining nonce refresh`, _extends({
|
|
2757
2773
|
status
|
|
2758
2774
|
}, logContext));
|
|
2759
|
-
return
|
|
2775
|
+
return alsoRetry ? alsoRetry(status) : false;
|
|
2760
2776
|
}
|
|
2777
|
+
onRefreshed(await this.resolve());
|
|
2778
|
+
this.logger.info(`[${operationName}] refreshed signed session, retrying`, _extends({
|
|
2779
|
+
status
|
|
2780
|
+
}, logContext));
|
|
2781
|
+
return true;
|
|
2761
2782
|
}
|
|
2762
2783
|
return alsoRetry ? alsoRetry(status) : false;
|
|
2763
2784
|
};
|
|
@@ -2766,12 +2787,12 @@ function meetsMinVersion(sdkVersion, { minVersionByNamespace, fallbackMinVersion
|
|
|
2766
2787
|
* Emits a single Datadog log per signed-session nonce rejection so the
|
|
2767
2788
|
* failure bucket can be split by `reason` and correlated per wallet/request.
|
|
2768
2789
|
* Purely observational — it never alters the retry decision.
|
|
2769
|
-
*/ instrumentNonceFailure({ error, attempt, operationName, willRefresh, logContext }) {
|
|
2790
|
+
*/ instrumentNonceFailure({ error, attempt, status, operationName, willRefresh, logContext }) {
|
|
2770
2791
|
const body = readErrorBody(error);
|
|
2771
2792
|
this.logger.info(NONCE_FAILURE_EVENT, _extends({}, logContext, {
|
|
2772
2793
|
operationName,
|
|
2773
2794
|
attempt,
|
|
2774
|
-
status
|
|
2795
|
+
status,
|
|
2775
2796
|
reason: classifyNonceFailure(body),
|
|
2776
2797
|
errorCode: body.code,
|
|
2777
2798
|
errorMessage: body.message,
|
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.81",
|
|
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.81",
|
|
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.81",
|
|
10
10
|
"@dynamic-labs/sdk-api-core": "^0.0.1083",
|
|
11
11
|
"argon2id": "1.0.1",
|
|
12
12
|
"axios": "1.16.0",
|
|
@@ -2,19 +2,21 @@ 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
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
|
|
5
|
+
* Datadog event name emitted on every signed-session nonce rejection (a 400 or
|
|
6
|
+
* 401 from the backup/recovery endpoints). Log-only: lets us split the failure
|
|
7
7
|
* bucket by `reason` and measure the true per-wallet unrecoverable rate
|
|
8
8
|
* without changing any retry behavior. See DYNT-1599.
|
|
9
9
|
*/
|
|
10
10
|
export declare const NONCE_FAILURE_EVENT = "waas.signed_session.nonce_failure";
|
|
11
11
|
/**
|
|
12
12
|
* Coarse cause of a signed-session nonce rejection. `invalid_nonce_signature`
|
|
13
|
-
* (a bad/stale signing key
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* and `invalid_session_signature` (a bad/stale signing key that diverged from
|
|
14
|
+
* the JWT-embedded session key — surfaced as a 401 since DYNT-1282) cannot be
|
|
15
|
+
* fixed by refreshing the nonce, whereas `nonce_already_used` (a consumed
|
|
16
|
+
* replay nonce, a 400) can — so splitting them tells us whether the
|
|
17
|
+
* refresh-and-retry path can actually help.
|
|
16
18
|
*/
|
|
17
|
-
export type NonceFailureReason = 'invalid_nonce_signature' | 'nonce_already_used' | 'other';
|
|
19
|
+
export type NonceFailureReason = 'invalid_nonce_signature' | 'invalid_session_signature' | 'nonce_already_used' | 'other';
|
|
18
20
|
/** Maps the server error `code`/`message` onto a {@link NonceFailureReason}. */
|
|
19
21
|
export declare const classifyNonceFailure: ({ code, message }: {
|
|
20
22
|
code?: string;
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAC1B,yBAAyB,GACzB,2BAA2B,GAC3B,oBAAoB,GACpB,OAAO,CAAC;AAkCZ,gFAAgF;AAChF,eAAO,MAAM,oBAAoB,sBAAuB;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,kBAoB7F,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;IAiCf;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;CA+B/B"}
|