@meshconnect/uwc-wallet-connect-connector 0.14.0 → 0.14.2
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/dist/services/walletconnect-client.d.ts +9 -2
- package/dist/services/walletconnect-client.d.ts.map +1 -1
- package/dist/services/walletconnect-client.js +33 -3
- package/dist/services/walletconnect-client.js.map +1 -1
- package/package.json +2 -2
- package/src/services/walletconnect-client.test.ts +71 -5
- package/src/services/walletconnect-client.ts +41 -5
|
@@ -5,6 +5,7 @@ import type { WalletConnectConfig } from '@meshconnect/uwc-types';
|
|
|
5
5
|
* guard, not a permanent ledger — bounding it keeps memory flat on a long-lived
|
|
6
6
|
* session even if messages are high-cardinality (timestamps / urls).
|
|
7
7
|
*/
|
|
8
|
+
export declare const MAX_RELAY_ERROR_DEDUP_KEYS = 100;
|
|
8
9
|
export declare const MAX_REPORTED_RELAY_ERRORS = 100;
|
|
9
10
|
/**
|
|
10
11
|
* Cap on relay transport-lifecycle emissions PER EVENT TYPE (disconnect and
|
|
@@ -27,10 +28,12 @@ export declare const MAX_REPORTED_RELAY_LIFECYCLE = 50;
|
|
|
27
28
|
export interface WalletConnectTelemetryObserver {
|
|
28
29
|
/**
|
|
29
30
|
* Relay-socket failure: the relayer re-emits an abnormal WebSocket close under
|
|
30
|
-
* `RELAYER_EVENTS.error` (e.g. code 3000
|
|
31
|
-
*
|
|
31
|
+
* `RELAYER_EVENTS.error` (e.g. code 3000 JWT validation error on clock skew),
|
|
32
|
+
* detached from the connect() promise. Deduped by a stable error-family key.
|
|
32
33
|
*/
|
|
33
34
|
onRelayError?: (error: unknown) => void;
|
|
35
|
+
/** Relay-error telemetry reached its per-client emission budget. */
|
|
36
|
+
onRelayErrorCapped?: () => void;
|
|
34
37
|
/**
|
|
35
38
|
* `SignClient.init` failure (storage/crypto, NOT relay). Telemetry-only — the
|
|
36
39
|
* init rejection is always rethrown so `getClient()` callers see it unchanged.
|
|
@@ -67,6 +70,10 @@ export declare class WalletConnectClient {
|
|
|
67
70
|
private initPromise;
|
|
68
71
|
/** Dedupe relay-error reports so a reconnect storm surfaces once, not per tick. */
|
|
69
72
|
private readonly relayErrorDedup;
|
|
73
|
+
/** Hard limit for relay-error telemetry when messages are high-cardinality. */
|
|
74
|
+
private relayErrorCount;
|
|
75
|
+
/** Ensures the telemetry-cap signal is emitted once per client. */
|
|
76
|
+
private relayErrorCapped;
|
|
70
77
|
/** Independent bounded counters so drop/recover telemetry can never starve each other. */
|
|
71
78
|
private relayDisconnectCount;
|
|
72
79
|
private relayReconnectCount;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"walletconnect-client.d.ts","sourceRoot":"","sources":["../../src/services/walletconnect-client.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,4BAA4B,CAAA;AAGnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAEjE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"walletconnect-client.d.ts","sourceRoot":"","sources":["../../src/services/walletconnect-client.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,4BAA4B,CAAA;AAGnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAEjE;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAA;AAC7C,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAa5C;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,KAAK,CAAA;AAE9C;;;;;;;GAOG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACvC,oEAAoE;IACpE,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC/B;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAA;IAC9B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,sBAAsB,CAAC,EACnB,8BAA8B,GAC9B,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,EAC9B,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GACrC,8BAA8B,CAsBhC;AAED;;GAEG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,WAAW,CAAmC;IACtD,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAE/B;IACD,+EAA+E;IAC/E,OAAO,CAAC,eAAe,CAAI;IAC3B,mEAAmE;IACnE,OAAO,CAAC,gBAAgB,CAAQ;IAChC,0FAA0F;IAC1F,OAAO,CAAC,oBAAoB,CAAI;IAChC,OAAO,CAAC,mBAAmB,CAAI;IAC/B,sFAAsF;IACtF,OAAO,CAAC,oBAAoB,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAgC;IAElE;;;;OAIG;gBAED,MAAM,EAAE,mBAAmB,EAC3B,iBAAiB,CAAC,EAAE,8BAA8B;IAEpD;;;;OAIG;gBAED,MAAM,EAAE,mBAAmB,EAC3B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EACvC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;IAgBxC;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IAmBtC;;OAEG;YACW,UAAU;IAiDxB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,wBAAwB;IAoChC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,6BAA6B;IA6CrC;;;;OAIG;IACH,iBAAiB,IAAI,UAAU,GAAG,IAAI;IAItC;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBpC;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAW/C"}
|
|
@@ -6,7 +6,15 @@ import { createBoundedDedup, safeErrorMessage } from '@meshconnect/uwc-types';
|
|
|
6
6
|
* guard, not a permanent ledger — bounding it keeps memory flat on a long-lived
|
|
7
7
|
* session even if messages are high-cardinality (timestamps / urls).
|
|
8
8
|
*/
|
|
9
|
+
export const MAX_RELAY_ERROR_DEDUP_KEYS = 100;
|
|
9
10
|
export const MAX_REPORTED_RELAY_ERRORS = 100;
|
|
11
|
+
const JWT_VALIDATION_RELAY_ERROR = /^WebSocket connection closed abnormally with code: 3000 \(JWT validation error: .+\)$/;
|
|
12
|
+
const JWT_VALIDATION_RELAY_ERROR_KEY = 'walletconnect:jwt-validation:code-3000';
|
|
13
|
+
function relayErrorDedupKey(message) {
|
|
14
|
+
return JWT_VALIDATION_RELAY_ERROR.test(message)
|
|
15
|
+
? JWT_VALIDATION_RELAY_ERROR_KEY
|
|
16
|
+
: message;
|
|
17
|
+
}
|
|
10
18
|
/**
|
|
11
19
|
* Cap on relay transport-lifecycle emissions PER EVENT TYPE (disconnect and
|
|
12
20
|
* reconnect budgeted independently) per client. A flapping socket IS the signal
|
|
@@ -54,7 +62,11 @@ export class WalletConnectClient {
|
|
|
54
62
|
client = null;
|
|
55
63
|
initPromise = null;
|
|
56
64
|
/** Dedupe relay-error reports so a reconnect storm surfaces once, not per tick. */
|
|
57
|
-
relayErrorDedup = createBoundedDedup(
|
|
65
|
+
relayErrorDedup = createBoundedDedup(MAX_RELAY_ERROR_DEDUP_KEYS);
|
|
66
|
+
/** Hard limit for relay-error telemetry when messages are high-cardinality. */
|
|
67
|
+
relayErrorCount = 0;
|
|
68
|
+
/** Ensures the telemetry-cap signal is emitted once per client. */
|
|
69
|
+
relayErrorCapped = false;
|
|
58
70
|
/** Independent bounded counters so drop/recover telemetry can never starve each other. */
|
|
59
71
|
relayDisconnectCount = 0;
|
|
60
72
|
relayReconnectCount = 0;
|
|
@@ -144,15 +156,33 @@ export class WalletConnectClient {
|
|
|
144
156
|
* drops the socket without emitting an `error`). Telemetry-only; never changes behaviour.
|
|
145
157
|
*/
|
|
146
158
|
attachRelayErrorListener(client) {
|
|
147
|
-
if (!this.telemetryObserver.onRelayError
|
|
159
|
+
if (!this.telemetryObserver.onRelayError &&
|
|
160
|
+
!this.telemetryObserver.onRelayErrorCapped) {
|
|
148
161
|
return;
|
|
162
|
+
}
|
|
149
163
|
client.core?.relayer?.on(RELAYER_EVENTS.error, (error) => {
|
|
164
|
+
if (this.relayErrorCount >= MAX_REPORTED_RELAY_ERRORS) {
|
|
165
|
+
if (this.relayErrorCapped)
|
|
166
|
+
return;
|
|
167
|
+
this.relayErrorCapped = true;
|
|
168
|
+
try {
|
|
169
|
+
this.telemetryObserver.onRelayErrorCapped?.();
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Telemetry must never break the relay.
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
150
176
|
// safeErrorMessage: this runs INSIDE the SDK's relayer dispatch, before
|
|
151
177
|
// the sink guard below — a throwing toString() here would break the
|
|
152
178
|
// emitter just like a throwing sink; the dedup key stays bounded too.
|
|
153
179
|
const message = safeErrorMessage(error);
|
|
154
|
-
|
|
180
|
+
const dedupKey = relayErrorDedupKey(message);
|
|
181
|
+
if (!this.relayErrorDedup.firstSeen(dedupKey))
|
|
182
|
+
return;
|
|
183
|
+
if (this.relayErrorCount >= MAX_REPORTED_RELAY_ERRORS)
|
|
155
184
|
return;
|
|
185
|
+
this.relayErrorCount++;
|
|
156
186
|
try {
|
|
157
187
|
this.telemetryObserver.onRelayError?.(error);
|
|
158
188
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"walletconnect-client.js","sourceRoot":"","sources":["../../src/services/walletconnect-client.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,4BAA4B,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAG7E;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAA;AAE5C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"walletconnect-client.js","sourceRoot":"","sources":["../../src/services/walletconnect-client.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,4BAA4B,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAG7E;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAA;AAC7C,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAA;AAE5C,MAAM,0BAA0B,GAC9B,uFAAuF,CAAA;AAEzF,MAAM,8BAA8B,GAAG,wCAAwC,CAAA;AAE/E,SAAS,kBAAkB,CAAC,OAAe;IACzC,OAAO,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC;QAC7C,CAAC,CAAC,8BAA8B;QAChC,CAAC,CAAC,OAAO,CAAA;AACb,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAA;AAqC9C;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,sBAE8B,EAC9B,WAAsC;IAEtC,MAAM,YAAY,GAChB,OAAO,sBAAsB,KAAK,UAAU;QAC1C,CAAC,CAAC,sBAAsB;QACxB,CAAC,CAAC,SAAS,CAAA;IAEf,2EAA2E;IAC3E,4EAA4E;IAC5E,uEAAuE;IACvE,+EAA+E;IAC/E,IAAI,YAAY,IAAI,WAAW,EAAE,CAAC;QAChC,OAAO;YACL,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxC,CAAA;IACH,CAAC;IAED,6CAA6C;IAC7C,OAAO,OAAO,sBAAsB,KAAK,QAAQ;QAC/C,sBAAsB,KAAK,IAAI;QAC/B,CAAC,CAAC,sBAAsB;QACxB,CAAC,CAAC,EAAE,CAAA;AACR,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,mBAAmB;IACtB,MAAM,GAAsB,IAAI,CAAA;IAChC,WAAW,GAA+B,IAAI,CAAA;IACtD,mFAAmF;IAClE,eAAe,GAAG,kBAAkB,CACnD,0BAA0B,CAC3B,CAAA;IACD,+EAA+E;IACvE,eAAe,GAAG,CAAC,CAAA;IAC3B,mEAAmE;IAC3D,gBAAgB,GAAG,KAAK,CAAA;IAChC,0FAA0F;IAClF,oBAAoB,GAAG,CAAC,CAAA;IACxB,mBAAmB,GAAG,CAAC,CAAA;IAC/B,sFAAsF;IAC9E,oBAAoB,GAAG,KAAK,CAAA;IACnB,MAAM,CAAqB;IAC3B,iBAAiB,CAAgC;IAqBlE,YACE,MAA2B,EAC3B,sBAE8B,EAC9B,WAAsC;QAEtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,iBAAiB,GAAG,mBAAmB,CAC1C,sBAAsB,EACtB,WAAW,CACZ,CAAA;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS;QACb,4CAA4C;QAC5C,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC,MAAM,CAAA;QACpB,CAAC;QAED,gDAAgD;QAChD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,WAAW,CAAA;QACzB,CAAC;QAED,uBAAuB;QACvB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,CAAA;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAA;QACpC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QAEvB,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,UAAU;QACtB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;QACxD,CAAC;QAED,IAAI,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAA;QACzC,IAAI,CAAC,GAAG,IAAI,OAAO,UAAU,KAAK,WAAW,EAAE,CAAC;YAC9C,8DAA8D;YAC9D,MAAM,MAAM,GAAG,UAAiB,CAAA;YAChC,IAAI,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;gBAC5B,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAA;YAC9B,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG;YACf,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,IAAI,4BAA4B;YAChE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,IAAI,qBAAqB;YACvE,GAAG;YACH,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;SACzC,CAAA;QAED,IAAI,MAAkB,CAAA;QACtB,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC;gBAC7B,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;gBAChC,QAAQ;aACT,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,kFAAkF;YAClF,oFAAoF;YACpF,iFAAiF;YACjF,oFAAoF;YACpF,mFAAmF;YACnF,mFAAmF;YACnF,2DAA2D;YAC3D,IAAI,CAAC;gBACH,IAAI,CAAC,iBAAiB,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAA;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACP,mDAAmD;YACrD,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;QAED,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAA;QACrC,IAAI,CAAC,6BAA6B,CAAC,MAAM,CAAC,CAAA;QAE1C,OAAO,MAAM,CAAA;IACf,CAAC;IAED;;;;;;;;;;OAUG;IACK,wBAAwB,CAAC,MAAkB;QACjD,IACE,CAAC,IAAI,CAAC,iBAAiB,CAAC,YAAY;YACpC,CAAC,IAAI,CAAC,iBAAiB,CAAC,kBAAkB,EAC1C,CAAC;YACD,OAAM;QACR,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,KAAc,EAAE,EAAE;YAChE,IAAI,IAAI,CAAC,eAAe,IAAI,yBAAyB,EAAE,CAAC;gBACtD,IAAI,IAAI,CAAC,gBAAgB;oBAAE,OAAM;gBACjC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAA;gBAC5B,IAAI,CAAC;oBACH,IAAI,CAAC,iBAAiB,CAAC,kBAAkB,EAAE,EAAE,CAAA;gBAC/C,CAAC;gBAAC,MAAM,CAAC;oBACP,wCAAwC;gBAC1C,CAAC;gBACD,OAAM;YACR,CAAC;YACD,wEAAwE;YACxE,oEAAoE;YACpE,sEAAsE;YACtE,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAA;YACvC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAA;YAC5C,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,QAAQ,CAAC;gBAAE,OAAM;YACrD,IAAI,IAAI,CAAC,eAAe,IAAI,yBAAyB;gBAAE,OAAM;YAC7D,IAAI,CAAC,eAAe,EAAE,CAAA;YACtB,IAAI,CAAC;gBACH,IAAI,CAAC,iBAAiB,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,CAAA;YAC9C,CAAC;YAAC,MAAM,CAAC;gBACP,oEAAoE;gBACpE,sEAAsE;gBACtE,sDAAsD;YACxD,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;;;;;;;;;;;OAaG;IACK,6BAA6B,CAAC,MAAkB;QACtD,IACE,CAAC,IAAI,CAAC,iBAAiB,CAAC,iBAAiB;YACzC,CAAC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB;YAExC,OAAM;QACR,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAA;QACpC,IAAI,CAAC,OAAO;YAAE,OAAM;QAEpB,+EAA+E;QAC/E,0EAA0E;QAC1E,4EAA4E;QAC5E,gFAAgF;QAChF,MAAM,MAAM,GAAG,CACb,IAA8B,EAC9B,OAAuD,EACjD,EAAE;YACR,IAAI,CAAC,IAAI;gBAAE,OAAM;YACjB,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,4BAA4B;gBAAE,OAAM;YACzD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAA;YACf,IAAI,CAAC;gBACH,IAAI,EAAE,CAAA;YACR,CAAC;YAAC,MAAM,CAAC;gBACP,+DAA+D;YACjE,CAAC;QACH,CAAC,CAAA;QAED,OAAO,CAAC,EAAE,CAAC,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE;YACzC,6EAA6E;YAC7E,8EAA8E;YAC9E,8EAA8E;YAC9E,kEAAkE;YAClE,IAAI,IAAI,CAAC,oBAAoB;gBAAE,OAAM;YACrC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAA;YAChC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,sBAAsB,CAAC,CAAA;QAC1E,CAAC,CAAC,CAAA;QACF,OAAO,CAAC,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE;YACtC,8EAA8E;YAC9E,yEAAyE;YACzE,IAAI,CAAC,IAAI,CAAC,oBAAoB;gBAAE,OAAM;YACtC,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAA;YACjC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,CAAA;QACxE,CAAC,CAAC,CAAA;IACJ,CAAC;IAED;;;;OAIG;IACH,iBAAiB;QACf,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa;QACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAA;QACrC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAA;QAExC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,UAAU,CAAC;oBACtB,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,MAAM,EAAE;wBACN,IAAI,EAAE,IAAI;wBACV,OAAO,EAAE,mBAAmB;qBAC7B;iBACF,CAAC,CAAA;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,oCAAoC;YACtC,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,KAAa;QAC5B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAA;QAErC,MAAM,MAAM,CAAC,UAAU,CAAC;YACtB,KAAK;YACL,MAAM,EAAE;gBACN,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,mBAAmB;aAC7B;SACF,CAAC,CAAA;IACJ,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meshconnect/uwc-wallet-connect-connector",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "WalletConnect connector for Universal Wallet Connector",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"bs58": "6.0.0",
|
|
26
26
|
"buffer": "^6.0.3",
|
|
27
27
|
"viem": "^2.54.6",
|
|
28
|
-
"@meshconnect/uwc-types": "0.26.
|
|
28
|
+
"@meshconnect/uwc-types": "0.26.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"typescript": "^5.9.3"
|
|
@@ -71,6 +71,36 @@ describe('WalletConnectClient relay-error surfacing', () => {
|
|
|
71
71
|
expect(errors).toHaveLength(1)
|
|
72
72
|
})
|
|
73
73
|
|
|
74
|
+
it('dedupes dynamic code-3000 JWT validation errors', async () => {
|
|
75
|
+
const errors: unknown[] = []
|
|
76
|
+
const client = new WalletConnectClient(CONFIG, {
|
|
77
|
+
onRelayError: e => errors.push(e)
|
|
78
|
+
})
|
|
79
|
+
await client.getClient()
|
|
80
|
+
|
|
81
|
+
const fire = (issuedAt: number, relayNow: number) =>
|
|
82
|
+
relayerHandlers[RELAYER_EVENTS.error]?.(
|
|
83
|
+
new Error(
|
|
84
|
+
`WebSocket connection closed abnormally with code: 3000 (JWT validation error: JWT Token is not yet valid: basic.iat: ${issuedAt}, now + time_leeway: ${relayNow}, time_leeway: 120)`
|
|
85
|
+
)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
fire(1784405170, 1784405120)
|
|
89
|
+
relayerHandlers[RELAYER_EVENTS.error]?.(
|
|
90
|
+
new Error(
|
|
91
|
+
'WebSocket connection closed abnormally with code: 3000 (JWT validation error: JWT Token is expired: Some(1784227017))'
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
relayerHandlers[RELAYER_EVENTS.error]?.(
|
|
95
|
+
new Error(
|
|
96
|
+
'WebSocket connection closed abnormally with code: 3000 (JWT validation error: JWT Token is not yet valid: basic.iat: 1784405173, now + time_leeway: 1784405123, time_leeway: 90)'
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
expect(errors).toHaveLength(1)
|
|
101
|
+
expect((errors[0] as Error).message).toContain('basic.iat: 1784405170')
|
|
102
|
+
})
|
|
103
|
+
|
|
74
104
|
it('swallows a throwing onRelayError sink — must not break the relayer emitter', async () => {
|
|
75
105
|
const client = new WalletConnectClient(CONFIG, {
|
|
76
106
|
onRelayError: () => {
|
|
@@ -110,7 +140,7 @@ describe('WalletConnectClient relay-error surfacing', () => {
|
|
|
110
140
|
expect(relayerHandlers[RELAYER_EVENTS.error]).toBeUndefined()
|
|
111
141
|
})
|
|
112
142
|
|
|
113
|
-
it('
|
|
143
|
+
it('does not emit after the relay-error telemetry cap is reached', async () => {
|
|
114
144
|
const errors: unknown[] = []
|
|
115
145
|
const client = new WalletConnectClient(CONFIG, {
|
|
116
146
|
onRelayError: e => errors.push(e)
|
|
@@ -127,13 +157,49 @@ describe('WalletConnectClient relay-error surfacing', () => {
|
|
|
127
157
|
fire('err-0')
|
|
128
158
|
expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS)
|
|
129
159
|
|
|
130
|
-
//
|
|
160
|
+
// Distinct messages can cycle the bounded dedup cache, but must not bypass
|
|
161
|
+
// the hard telemetry cap.
|
|
131
162
|
fire('over-cap')
|
|
132
|
-
expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS
|
|
163
|
+
expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS)
|
|
133
164
|
|
|
134
|
-
//
|
|
165
|
+
// A previously evicted key must remain capped too.
|
|
135
166
|
fire('err-0')
|
|
136
|
-
expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS
|
|
167
|
+
expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('caps relay-error telemetry when every message is distinct', async () => {
|
|
171
|
+
const errors: unknown[] = []
|
|
172
|
+
let capped = 0
|
|
173
|
+
const client = new WalletConnectClient(CONFIG, {
|
|
174
|
+
onRelayError: e => errors.push(e),
|
|
175
|
+
onRelayErrorCapped: () => {
|
|
176
|
+
capped++
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
await client.getClient()
|
|
180
|
+
|
|
181
|
+
for (let i = 0; i <= MAX_REPORTED_RELAY_ERRORS; i++) {
|
|
182
|
+
relayerHandlers[RELAYER_EVENTS.error]?.(new Error(`err-${i}`))
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
expect(errors).toHaveLength(MAX_REPORTED_RELAY_ERRORS)
|
|
186
|
+
expect(capped).toBe(1)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('emits the cap signal when it is the only relay-error sink', async () => {
|
|
190
|
+
let capped = 0
|
|
191
|
+
const client = new WalletConnectClient(CONFIG, {
|
|
192
|
+
onRelayErrorCapped: () => {
|
|
193
|
+
capped++
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
await client.getClient()
|
|
197
|
+
|
|
198
|
+
for (let i = 0; i <= MAX_REPORTED_RELAY_ERRORS; i++) {
|
|
199
|
+
relayerHandlers[RELAYER_EVENTS.error]?.(new Error(`err-${i}`))
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
expect(capped).toBe(1)
|
|
137
203
|
})
|
|
138
204
|
})
|
|
139
205
|
|
|
@@ -8,8 +8,20 @@ import type { WalletConnectConfig } from '@meshconnect/uwc-types'
|
|
|
8
8
|
* guard, not a permanent ledger — bounding it keeps memory flat on a long-lived
|
|
9
9
|
* session even if messages are high-cardinality (timestamps / urls).
|
|
10
10
|
*/
|
|
11
|
+
export const MAX_RELAY_ERROR_DEDUP_KEYS = 100
|
|
11
12
|
export const MAX_REPORTED_RELAY_ERRORS = 100
|
|
12
13
|
|
|
14
|
+
const JWT_VALIDATION_RELAY_ERROR =
|
|
15
|
+
/^WebSocket connection closed abnormally with code: 3000 \(JWT validation error: .+\)$/
|
|
16
|
+
|
|
17
|
+
const JWT_VALIDATION_RELAY_ERROR_KEY = 'walletconnect:jwt-validation:code-3000'
|
|
18
|
+
|
|
19
|
+
function relayErrorDedupKey(message: string): string {
|
|
20
|
+
return JWT_VALIDATION_RELAY_ERROR.test(message)
|
|
21
|
+
? JWT_VALIDATION_RELAY_ERROR_KEY
|
|
22
|
+
: message
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
/**
|
|
14
26
|
* Cap on relay transport-lifecycle emissions PER EVENT TYPE (disconnect and
|
|
15
27
|
* reconnect budgeted independently) per client. A flapping socket IS the signal
|
|
@@ -32,10 +44,12 @@ export const MAX_REPORTED_RELAY_LIFECYCLE = 50
|
|
|
32
44
|
export interface WalletConnectTelemetryObserver {
|
|
33
45
|
/**
|
|
34
46
|
* Relay-socket failure: the relayer re-emits an abnormal WebSocket close under
|
|
35
|
-
* `RELAYER_EVENTS.error` (e.g. code 3000
|
|
36
|
-
*
|
|
47
|
+
* `RELAYER_EVENTS.error` (e.g. code 3000 JWT validation error on clock skew),
|
|
48
|
+
* detached from the connect() promise. Deduped by a stable error-family key.
|
|
37
49
|
*/
|
|
38
50
|
onRelayError?: (error: unknown) => void
|
|
51
|
+
/** Relay-error telemetry reached its per-client emission budget. */
|
|
52
|
+
onRelayErrorCapped?: () => void
|
|
39
53
|
/**
|
|
40
54
|
* `SignClient.init` failure (storage/crypto, NOT relay). Telemetry-only — the
|
|
41
55
|
* init rejection is always rethrown so `getClient()` callers see it unchanged.
|
|
@@ -101,8 +115,12 @@ export class WalletConnectClient {
|
|
|
101
115
|
private initPromise: Promise<SignClient> | null = null
|
|
102
116
|
/** Dedupe relay-error reports so a reconnect storm surfaces once, not per tick. */
|
|
103
117
|
private readonly relayErrorDedup = createBoundedDedup(
|
|
104
|
-
|
|
118
|
+
MAX_RELAY_ERROR_DEDUP_KEYS
|
|
105
119
|
)
|
|
120
|
+
/** Hard limit for relay-error telemetry when messages are high-cardinality. */
|
|
121
|
+
private relayErrorCount = 0
|
|
122
|
+
/** Ensures the telemetry-cap signal is emitted once per client. */
|
|
123
|
+
private relayErrorCapped = false
|
|
106
124
|
/** Independent bounded counters so drop/recover telemetry can never starve each other. */
|
|
107
125
|
private relayDisconnectCount = 0
|
|
108
126
|
private relayReconnectCount = 0
|
|
@@ -230,13 +248,31 @@ export class WalletConnectClient {
|
|
|
230
248
|
* drops the socket without emitting an `error`). Telemetry-only; never changes behaviour.
|
|
231
249
|
*/
|
|
232
250
|
private attachRelayErrorListener(client: SignClient): void {
|
|
233
|
-
if (
|
|
251
|
+
if (
|
|
252
|
+
!this.telemetryObserver.onRelayError &&
|
|
253
|
+
!this.telemetryObserver.onRelayErrorCapped
|
|
254
|
+
) {
|
|
255
|
+
return
|
|
256
|
+
}
|
|
234
257
|
client.core?.relayer?.on(RELAYER_EVENTS.error, (error: unknown) => {
|
|
258
|
+
if (this.relayErrorCount >= MAX_REPORTED_RELAY_ERRORS) {
|
|
259
|
+
if (this.relayErrorCapped) return
|
|
260
|
+
this.relayErrorCapped = true
|
|
261
|
+
try {
|
|
262
|
+
this.telemetryObserver.onRelayErrorCapped?.()
|
|
263
|
+
} catch {
|
|
264
|
+
// Telemetry must never break the relay.
|
|
265
|
+
}
|
|
266
|
+
return
|
|
267
|
+
}
|
|
235
268
|
// safeErrorMessage: this runs INSIDE the SDK's relayer dispatch, before
|
|
236
269
|
// the sink guard below — a throwing toString() here would break the
|
|
237
270
|
// emitter just like a throwing sink; the dedup key stays bounded too.
|
|
238
271
|
const message = safeErrorMessage(error)
|
|
239
|
-
|
|
272
|
+
const dedupKey = relayErrorDedupKey(message)
|
|
273
|
+
if (!this.relayErrorDedup.firstSeen(dedupKey)) return
|
|
274
|
+
if (this.relayErrorCount >= MAX_REPORTED_RELAY_ERRORS) return
|
|
275
|
+
this.relayErrorCount++
|
|
240
276
|
try {
|
|
241
277
|
this.telemetryObserver.onRelayError?.(error)
|
|
242
278
|
} catch {
|