@meshconnect/uwc-core 2.5.16 → 2.7.0-snapshot.c392d3d
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/README.md +11 -2
- package/dist/events.d.ts +20 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/observability/instrument-handoff.d.ts +5 -0
- package/dist/observability/instrument-handoff.d.ts.map +1 -1
- package/dist/observability/instrument-handoff.js +12 -0
- package/dist/observability/instrument-handoff.js.map +1 -1
- package/dist/observability/telemetry.d.ts +4 -0
- package/dist/observability/telemetry.d.ts.map +1 -1
- package/dist/observability/telemetry.js +31 -10
- package/dist/observability/telemetry.js.map +1 -1
- package/dist/services/connection-service.d.ts +11 -2
- package/dist/services/connection-service.d.ts.map +1 -1
- package/dist/services/connection-service.js +39 -12
- package/dist/services/connection-service.js.map +1 -1
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/network-selection.d.ts +34 -0
- package/dist/services/network-selection.d.ts.map +1 -0
- package/dist/services/network-selection.js +54 -0
- package/dist/services/network-selection.js.map +1 -0
- package/dist/universal-wallet-connector.d.ts +15 -1
- package/dist/universal-wallet-connector.d.ts.map +1 -1
- package/dist/universal-wallet-connector.js +5 -3
- package/dist/universal-wallet-connector.js.map +1 -1
- package/dist/utils/to-wallet-error.d.ts.map +1 -1
- package/dist/utils/to-wallet-error.js +13 -2
- package/dist/utils/to-wallet-error.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +5 -5
- package/src/events.ts +20 -0
- package/src/index.ts +4 -0
- package/src/observability/instrument-handoff.ts +17 -0
- package/src/observability/telemetry.test.ts +83 -0
- package/src/observability/telemetry.ts +36 -10
- package/src/services/connection-service.test.ts +159 -7
- package/src/services/connection-service.ts +57 -14
- package/src/services/index.ts +1 -0
- package/src/services/network-selection.test.ts +152 -0
- package/src/services/network-selection.ts +86 -0
- package/src/services/transaction-service.test.ts +25 -0
- package/src/universal-wallet-connector.ts +20 -3
- package/src/utils/to-wallet-error.test.ts +29 -1
- package/src/utils/to-wallet-error.ts +12 -1
- package/src/version.ts +1 -1
package/dist/version.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB,UAAU,CAAA"}
|
package/dist/version.js
CHANGED
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,gBAAgB,GAAG,
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meshconnect/uwc-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0-snapshot.c392d3d",
|
|
4
4
|
"description": "Core functionality for Universal Wallet Connector",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@meshconnect/uwc-injected-connector": "0.
|
|
20
|
-
"@meshconnect/uwc-ton-connector": "0.9.
|
|
21
|
-
"@meshconnect/uwc-types": "0.
|
|
22
|
-
"@meshconnect/uwc-wallet-connect-connector": "0.14.
|
|
19
|
+
"@meshconnect/uwc-injected-connector": "0.27.1-snapshot.c392d3d",
|
|
20
|
+
"@meshconnect/uwc-ton-connector": "0.9.11-snapshot.c392d3d",
|
|
21
|
+
"@meshconnect/uwc-types": "0.32.0-snapshot.c392d3d",
|
|
22
|
+
"@meshconnect/uwc-wallet-connect-connector": "0.14.21-snapshot.c392d3d"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"typescript": "^7.0.2"
|
package/src/events.ts
CHANGED
|
@@ -251,6 +251,10 @@ export interface UWCEventMap {
|
|
|
251
251
|
connectAttemptId?: string | undefined
|
|
252
252
|
/** switchNetwork only: target chain already the active one (no-op switch). */
|
|
253
253
|
targetAlreadyActive?: boolean | undefined
|
|
254
|
+
/** connect only: a `silentOnly` attempt (no wallet UI allowed). */
|
|
255
|
+
silentOnly?: boolean | undefined
|
|
256
|
+
/** connect only: how the connect network was chosen. */
|
|
257
|
+
networkSelection?: 'explicit' | 'detected' | 'catalogOrder' | undefined
|
|
254
258
|
}
|
|
255
259
|
|
|
256
260
|
/** The wallet resolved the op successfully. Closes a handoff. */
|
|
@@ -270,6 +274,10 @@ export interface UWCEventMap {
|
|
|
270
274
|
connectAttemptId?: string | undefined
|
|
271
275
|
/** switchNetwork only: target chain already the active one (no-op switch). */
|
|
272
276
|
targetAlreadyActive?: boolean | undefined
|
|
277
|
+
/** connect only: a `silentOnly` attempt (no wallet UI allowed). */
|
|
278
|
+
silentOnly?: boolean | undefined
|
|
279
|
+
/** connect only: how the connect network was chosen. */
|
|
280
|
+
networkSelection?: 'explicit' | 'detected' | 'catalogOrder' | undefined
|
|
273
281
|
/** Backgrounding during this handoff: cumulative hidden time (ms) + transition count. */
|
|
274
282
|
backgroundedMs?: number | undefined
|
|
275
283
|
backgroundCount?: number | undefined
|
|
@@ -303,6 +311,10 @@ export interface UWCEventMap {
|
|
|
303
311
|
/** Per-connect identifier shared by all events in this connect flow. */
|
|
304
312
|
connectAttemptId?: string | undefined
|
|
305
313
|
targetAlreadyActive?: boolean | undefined
|
|
314
|
+
/** connect only: a `silentOnly` attempt (no wallet UI allowed). */
|
|
315
|
+
silentOnly?: boolean | undefined
|
|
316
|
+
/** connect only: how the connect network was chosen. */
|
|
317
|
+
networkSelection?: 'explicit' | 'detected' | 'catalogOrder' | undefined
|
|
306
318
|
/** Stringified error code (EIP-1193 numeric / string), when present. */
|
|
307
319
|
errorCode?: string | undefined
|
|
308
320
|
failureKind?: FailureKind | undefined
|
|
@@ -328,6 +340,10 @@ export interface UWCEventMap {
|
|
|
328
340
|
/** Per-connect identifier shared by all events in this connect flow. */
|
|
329
341
|
connectAttemptId?: string | undefined
|
|
330
342
|
targetAlreadyActive?: boolean | undefined
|
|
343
|
+
/** connect only: a `silentOnly` attempt (no wallet UI allowed). */
|
|
344
|
+
silentOnly?: boolean | undefined
|
|
345
|
+
/** connect only: how the connect network was chosen. */
|
|
346
|
+
networkSelection?: 'explicit' | 'detected' | 'catalogOrder' | undefined
|
|
331
347
|
/** Stringified error code (EIP-1193 numeric / string), when present. */
|
|
332
348
|
errorCode?: string | undefined
|
|
333
349
|
failureKind?: FailureKind | undefined
|
|
@@ -358,6 +374,10 @@ export interface UWCEventMap {
|
|
|
358
374
|
connectAttemptId?: string | undefined
|
|
359
375
|
/** switchNetwork only: target chain already the active one (no-op switch). */
|
|
360
376
|
targetAlreadyActive?: boolean | undefined
|
|
377
|
+
/** connect only: a `silentOnly` attempt (no wallet UI allowed). */
|
|
378
|
+
silentOnly?: boolean | undefined
|
|
379
|
+
/** connect only: how the connect network was chosen. */
|
|
380
|
+
networkSelection?: 'explicit' | 'detected' | 'catalogOrder' | undefined
|
|
361
381
|
/** Backgrounding so far (marker fires mid-flight): hidden time (ms) + transition count. */
|
|
362
382
|
backgroundedMs?: number | undefined
|
|
363
383
|
backgroundCount?: number | undefined
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NetworkSelection } from '../services/network-selection'
|
|
1
2
|
import {
|
|
2
3
|
BatchConfirmedWithoutReceiptError,
|
|
3
4
|
type ConnectionMode,
|
|
@@ -29,6 +30,10 @@ export interface HandoffContext {
|
|
|
29
30
|
connectAttemptId?: string | undefined
|
|
30
31
|
/** switchNetwork only: target chain already the active one (no-op switch). */
|
|
31
32
|
targetAlreadyActive?: boolean | undefined
|
|
33
|
+
/** connect only: the attempt was `silentOnly` (see `ConnectOptions.silentOnly`). */
|
|
34
|
+
silentOnly?: boolean | undefined
|
|
35
|
+
/** connect only: how the connect network was chosen (see `NetworkSelection`). */
|
|
36
|
+
networkSelection?: NetworkSelection | undefined
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
/** Build handoff routing facets from the active session (no address). */
|
|
@@ -151,6 +156,12 @@ export async function instrumentHandoff<T>(
|
|
|
151
156
|
}),
|
|
152
157
|
...(context.targetAlreadyActive !== undefined && {
|
|
153
158
|
targetAlreadyActive: context.targetAlreadyActive
|
|
159
|
+
}),
|
|
160
|
+
...(context.silentOnly !== undefined && {
|
|
161
|
+
silentOnly: context.silentOnly
|
|
162
|
+
}),
|
|
163
|
+
...(context.networkSelection !== undefined && {
|
|
164
|
+
networkSelection: context.networkSelection
|
|
154
165
|
})
|
|
155
166
|
})
|
|
156
167
|
|
|
@@ -196,6 +207,12 @@ export async function instrumentHandoff<T>(
|
|
|
196
207
|
}),
|
|
197
208
|
...(context.targetAlreadyActive !== undefined && {
|
|
198
209
|
targetAlreadyActive: context.targetAlreadyActive
|
|
210
|
+
}),
|
|
211
|
+
...(context.silentOnly !== undefined && {
|
|
212
|
+
silentOnly: context.silentOnly
|
|
213
|
+
}),
|
|
214
|
+
...(context.networkSelection !== undefined && {
|
|
215
|
+
networkSelection: context.networkSelection
|
|
199
216
|
})
|
|
200
217
|
}
|
|
201
218
|
|
|
@@ -867,6 +867,89 @@ describe('normalizeEvent', () => {
|
|
|
867
867
|
expect(out.connectAttemptId).toBe('attempt-1')
|
|
868
868
|
})
|
|
869
869
|
|
|
870
|
+
it('forwards a scrubbed providerReason on walletFailed', () => {
|
|
871
|
+
const out = normalizeEvent(
|
|
872
|
+
'walletFailed',
|
|
873
|
+
{
|
|
874
|
+
handoffId: 'h-1',
|
|
875
|
+
operation: 'sendTransaction',
|
|
876
|
+
durationMs: 1,
|
|
877
|
+
error: {
|
|
878
|
+
type: 'unknown',
|
|
879
|
+
message: 'An unknown RPC error occurred.',
|
|
880
|
+
providerReason:
|
|
881
|
+
'cancelled for 0x1234567890123456789012345678901234567890'
|
|
882
|
+
},
|
|
883
|
+
connectionMode: 'injected',
|
|
884
|
+
timedOut: false
|
|
885
|
+
},
|
|
886
|
+
SDK_SESSION,
|
|
887
|
+
NOW
|
|
888
|
+
)
|
|
889
|
+
expect(out.error?.providerReason).toContain('cancelled for ')
|
|
890
|
+
expect(out.error?.providerReason).not.toContain(
|
|
891
|
+
'1234567890123456789012345678901234567890'
|
|
892
|
+
)
|
|
893
|
+
})
|
|
894
|
+
|
|
895
|
+
it('redacts an address in a long providerReason before capping it', () => {
|
|
896
|
+
const address = '0x1234567890123456789012345678901234567890'
|
|
897
|
+
const out = normalizeEvent(
|
|
898
|
+
'walletFailed',
|
|
899
|
+
{
|
|
900
|
+
handoffId: 'h-1',
|
|
901
|
+
operation: 'sendTransaction',
|
|
902
|
+
durationMs: 1,
|
|
903
|
+
error: {
|
|
904
|
+
type: 'unknown',
|
|
905
|
+
message: 'An unknown RPC error occurred.',
|
|
906
|
+
// The address straddles the 500-character cap.
|
|
907
|
+
providerReason: `${'x'.repeat(480)} ${address}`
|
|
908
|
+
},
|
|
909
|
+
connectionMode: 'injected',
|
|
910
|
+
timedOut: false
|
|
911
|
+
},
|
|
912
|
+
SDK_SESSION,
|
|
913
|
+
NOW
|
|
914
|
+
)
|
|
915
|
+
expect(out.error?.providerReason).not.toContain('0x1234567890')
|
|
916
|
+
expect(out.error?.providerReason).toContain('0x[redacted-hex]')
|
|
917
|
+
})
|
|
918
|
+
|
|
919
|
+
it('omits providerReason from the record when the error has none', () => {
|
|
920
|
+
const out = normalizeEvent(
|
|
921
|
+
'walletFailed',
|
|
922
|
+
{
|
|
923
|
+
handoffId: 'h-1',
|
|
924
|
+
operation: 'sendTransaction',
|
|
925
|
+
durationMs: 1,
|
|
926
|
+
error: { type: 'unknown', message: 'failed' },
|
|
927
|
+
connectionMode: 'injected',
|
|
928
|
+
timedOut: false
|
|
929
|
+
},
|
|
930
|
+
SDK_SESSION,
|
|
931
|
+
NOW
|
|
932
|
+
)
|
|
933
|
+
expect(out.error).toEqual({ type: 'unknown', message: 'failed' })
|
|
934
|
+
})
|
|
935
|
+
|
|
936
|
+
it('forwards providerReason on the error event too', () => {
|
|
937
|
+
const out = normalizeEvent(
|
|
938
|
+
'error',
|
|
939
|
+
{
|
|
940
|
+
error: {
|
|
941
|
+
type: 'unknown',
|
|
942
|
+
message: 'failed',
|
|
943
|
+
providerReason: 'cancelled'
|
|
944
|
+
},
|
|
945
|
+
operation: 'sendTransaction'
|
|
946
|
+
},
|
|
947
|
+
SDK_SESSION,
|
|
948
|
+
NOW
|
|
949
|
+
)
|
|
950
|
+
expect(out.error?.providerReason).toBe('cancelled')
|
|
951
|
+
})
|
|
952
|
+
|
|
870
953
|
it('does not expose a stray connectAttemptId on data-bearing non-connect events', () => {
|
|
871
954
|
const out = normalizeEvent(
|
|
872
955
|
'error',
|
|
@@ -52,6 +52,20 @@ function capNetworkMismatch(
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Egress shape for a wallet error. `providerReason` is wallet-controlled text, so
|
|
57
|
+
* it gets the same scrubber as `message` and is omitted when absent.
|
|
58
|
+
*/
|
|
59
|
+
function toTelemetryError(error: WalletError): WalletError {
|
|
60
|
+
return {
|
|
61
|
+
type: error.type,
|
|
62
|
+
message: scrubMessage(error.message),
|
|
63
|
+
...(error.providerReason
|
|
64
|
+
? { providerReason: scrubMessage(error.providerReason) }
|
|
65
|
+
: {})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
55
69
|
/**
|
|
56
70
|
* A normalized, PII-safe telemetry record derived from a `UWCEventMap` event.
|
|
57
71
|
*
|
|
@@ -107,6 +121,10 @@ export interface UWCTelemetryEvent {
|
|
|
107
121
|
timeoutMs?: number | undefined
|
|
108
122
|
/** switchNetwork only: target chain was already the active one (no-op switch). */
|
|
109
123
|
targetAlreadyActive?: boolean | undefined
|
|
124
|
+
/** connect only: a `silentOnly` attempt — tells a silent restore from a tapped connect. */
|
|
125
|
+
silentOnly?: boolean | undefined
|
|
126
|
+
/** connect only: how the connect network was chosen — explicit, detected, or catalog order. */
|
|
127
|
+
networkSelection?: 'explicit' | 'detected' | 'catalogOrder' | undefined
|
|
110
128
|
/** Non-success outcomes: stringified error code when reliably known. */
|
|
111
129
|
errorCode?: string | undefined
|
|
112
130
|
/** Non-success outcomes: coarse cause bucket. `unknown` when not classifiable. */
|
|
@@ -432,7 +450,7 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
432
450
|
return {
|
|
433
451
|
...base,
|
|
434
452
|
operation: d.operation,
|
|
435
|
-
error:
|
|
453
|
+
error: toTelemetryError(d.error),
|
|
436
454
|
// Telemetry-only classification — undefined for ordinary errors, set for
|
|
437
455
|
// pre-wallet validation failures (e.g. no_common_network) that never
|
|
438
456
|
// reach the handoff funnel.
|
|
@@ -456,7 +474,9 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
456
474
|
connectAttemptId: getConnectAttemptId(d.operation, d.connectAttemptId),
|
|
457
475
|
walletFlowType: d.walletFlowType,
|
|
458
476
|
platform: d.platform,
|
|
459
|
-
targetAlreadyActive: d.targetAlreadyActive
|
|
477
|
+
targetAlreadyActive: d.targetAlreadyActive,
|
|
478
|
+
silentOnly: d.silentOnly,
|
|
479
|
+
networkSelection: d.networkSelection
|
|
460
480
|
}
|
|
461
481
|
}
|
|
462
482
|
case 'walletSucceeded': {
|
|
@@ -474,6 +494,8 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
474
494
|
timedOut: d.timedOut,
|
|
475
495
|
timeoutMs: d.timeoutMs,
|
|
476
496
|
targetAlreadyActive: d.targetAlreadyActive,
|
|
497
|
+
silentOnly: d.silentOnly,
|
|
498
|
+
networkSelection: d.networkSelection,
|
|
477
499
|
backgroundedMs: d.backgroundedMs,
|
|
478
500
|
backgroundCount: d.backgroundCount,
|
|
479
501
|
relayDropCount: d.relayDropCount,
|
|
@@ -495,12 +517,12 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
495
517
|
timedOut: d.timedOut,
|
|
496
518
|
timeoutMs: d.timeoutMs,
|
|
497
519
|
targetAlreadyActive: d.targetAlreadyActive,
|
|
520
|
+
silentOnly: d.silentOnly,
|
|
521
|
+
networkSelection: d.networkSelection,
|
|
498
522
|
errorCode: d.errorCode,
|
|
499
523
|
failureKind: d.failureKind,
|
|
500
524
|
source: d.source,
|
|
501
|
-
error: d.error
|
|
502
|
-
? { type: d.error.type, message: scrubMessage(d.error.message) }
|
|
503
|
-
: undefined,
|
|
525
|
+
error: d.error ? toTelemetryError(d.error) : undefined,
|
|
504
526
|
backgroundedMs: d.backgroundedMs,
|
|
505
527
|
backgroundCount: d.backgroundCount,
|
|
506
528
|
relayDropCount: d.relayDropCount
|
|
@@ -520,6 +542,8 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
520
542
|
durationMs: d.durationMs,
|
|
521
543
|
timeoutMs: d.timeoutMs,
|
|
522
544
|
targetAlreadyActive: d.targetAlreadyActive,
|
|
545
|
+
silentOnly: d.silentOnly,
|
|
546
|
+
networkSelection: d.networkSelection,
|
|
523
547
|
backgroundedMs: d.backgroundedMs,
|
|
524
548
|
backgroundCount: d.backgroundCount,
|
|
525
549
|
relayDropCount: d.relayDropCount
|
|
@@ -555,10 +579,12 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
555
579
|
timedOut: d.timedOut,
|
|
556
580
|
timeoutMs: d.timeoutMs,
|
|
557
581
|
targetAlreadyActive: d.targetAlreadyActive,
|
|
582
|
+
silentOnly: d.silentOnly,
|
|
583
|
+
networkSelection: d.networkSelection,
|
|
558
584
|
errorCode: d.errorCode,
|
|
559
585
|
failureKind: d.failureKind,
|
|
560
586
|
source: d.source,
|
|
561
|
-
error:
|
|
587
|
+
error: toTelemetryError(d.error),
|
|
562
588
|
backgroundedMs: d.backgroundedMs,
|
|
563
589
|
backgroundCount: d.backgroundCount,
|
|
564
590
|
relayDropCount: d.relayDropCount
|
|
@@ -602,7 +628,7 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
602
628
|
...base,
|
|
603
629
|
connectionMode: d.connectionMode,
|
|
604
630
|
namespace: d.namespace,
|
|
605
|
-
error:
|
|
631
|
+
error: toTelemetryError(d.error)
|
|
606
632
|
}
|
|
607
633
|
}
|
|
608
634
|
case 'secondarySolanaConnectSkipped': {
|
|
@@ -644,7 +670,7 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
644
670
|
namespace: d.namespace,
|
|
645
671
|
source: 'bridge',
|
|
646
672
|
failureKind: 'bridge_error',
|
|
647
|
-
error:
|
|
673
|
+
error: toTelemetryError(d.error)
|
|
648
674
|
}
|
|
649
675
|
}
|
|
650
676
|
case 'relayError': {
|
|
@@ -656,7 +682,7 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
656
682
|
connectionMode: 'walletConnect',
|
|
657
683
|
source: 'provider',
|
|
658
684
|
failureKind: 'network_error',
|
|
659
|
-
error:
|
|
685
|
+
error: toTelemetryError(d.error)
|
|
660
686
|
}
|
|
661
687
|
}
|
|
662
688
|
case 'relayErrorCapped':
|
|
@@ -677,7 +703,7 @@ function buildTelemetryEvent<K extends UWCEventName>(
|
|
|
677
703
|
connectionMode: 'walletConnect',
|
|
678
704
|
source: 'provider',
|
|
679
705
|
failureKind: 'client_init_failed',
|
|
680
|
-
error:
|
|
706
|
+
error: toTelemetryError(d.error)
|
|
681
707
|
}
|
|
682
708
|
}
|
|
683
709
|
case 'walletAccountChanged':
|
|
@@ -12,7 +12,10 @@ import type {
|
|
|
12
12
|
ConnectionMode,
|
|
13
13
|
Connector
|
|
14
14
|
} from '@meshconnect/uwc-types'
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
NoCommonNetworkError,
|
|
17
|
+
WalletNotAuthorizedError
|
|
18
|
+
} from '@meshconnect/uwc-types'
|
|
16
19
|
import type { WalletLifecycleEvent } from '@meshconnect/uwc-types'
|
|
17
20
|
|
|
18
21
|
describe('ConnectionService', () => {
|
|
@@ -111,11 +114,11 @@ describe('ConnectionService', () => {
|
|
|
111
114
|
const mockProvider = {
|
|
112
115
|
supportedNetworkIds: ['eip155:1']
|
|
113
116
|
}
|
|
114
|
-
const network = connectionService[
|
|
115
|
-
'
|
|
116
|
-
|
|
117
|
-
)
|
|
117
|
+
const { network, selection } = connectionService[
|
|
118
|
+
'validateAndDetermineNetwork'
|
|
119
|
+
]('eip155:1', mockProvider as any, 'injected')
|
|
118
120
|
expect(network).toEqual(networks[0])
|
|
121
|
+
expect(selection).toBe('explicit')
|
|
119
122
|
})
|
|
120
123
|
|
|
121
124
|
it('should throw error for unknown network', () => {
|
|
@@ -125,7 +128,8 @@ describe('ConnectionService', () => {
|
|
|
125
128
|
expect(() => {
|
|
126
129
|
connectionService['validateAndDetermineNetwork'](
|
|
127
130
|
'eip155:8453',
|
|
128
|
-
mockProvider as any
|
|
131
|
+
mockProvider as any,
|
|
132
|
+
'injected'
|
|
129
133
|
)
|
|
130
134
|
}).toThrow('Network eip155:8453 is not in the configured networks')
|
|
131
135
|
})
|
|
@@ -140,7 +144,8 @@ describe('ConnectionService', () => {
|
|
|
140
144
|
try {
|
|
141
145
|
connectionService['validateAndDetermineNetwork'](
|
|
142
146
|
undefined,
|
|
143
|
-
mockProvider as any
|
|
147
|
+
mockProvider as any,
|
|
148
|
+
'injected'
|
|
144
149
|
)
|
|
145
150
|
} catch (e) {
|
|
146
151
|
thrown = e
|
|
@@ -211,6 +216,115 @@ describe('ConnectionService', () => {
|
|
|
211
216
|
expect(connectionService['activeConnector']).toBe(mockConnector)
|
|
212
217
|
})
|
|
213
218
|
|
|
219
|
+
// ONC-1975: silentOnly is a promise never to open wallet UI.
|
|
220
|
+
it('forwards silentOnly to the injected connector', async () => {
|
|
221
|
+
const provider = { supportedNetworkIds: ['eip155:1'] }
|
|
222
|
+
wallets[0].extensionInjectedProvider = provider as any
|
|
223
|
+
mockConnector.connect = vi.fn().mockResolvedValue({
|
|
224
|
+
networkId: 'eip155:1',
|
|
225
|
+
address: '0x1234',
|
|
226
|
+
availableAddresses: [{ address: '0x1234', networkId: 'eip155:1' }]
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
await connectionService.connect('injected', 'metamask', 'eip155:1', {
|
|
230
|
+
silentOnly: true
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
expect(mockConnector.connect).toHaveBeenCalledWith(
|
|
234
|
+
networks[0],
|
|
235
|
+
provider,
|
|
236
|
+
{
|
|
237
|
+
silentOnly: true
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('tags the connect funnel with silentOnly, so a silent restore is not counted as a tap', async () => {
|
|
243
|
+
const provider = { supportedNetworkIds: ['eip155:1'] }
|
|
244
|
+
wallets[0].extensionInjectedProvider = provider as any
|
|
245
|
+
mockConnector.connect = vi.fn().mockResolvedValue({
|
|
246
|
+
networkId: 'eip155:1',
|
|
247
|
+
address: '0x1234',
|
|
248
|
+
availableAddresses: [{ address: '0x1234', networkId: 'eip155:1' }]
|
|
249
|
+
})
|
|
250
|
+
const awaiting = vi.fn()
|
|
251
|
+
const succeeded = vi.fn()
|
|
252
|
+
eventManager.on('awaitingWallet', awaiting)
|
|
253
|
+
eventManager.on('walletSucceeded', succeeded)
|
|
254
|
+
|
|
255
|
+
await connectionService.connect('injected', 'metamask', 'eip155:1', {
|
|
256
|
+
silentOnly: true
|
|
257
|
+
})
|
|
258
|
+
await connectionService.connect('injected', 'metamask', 'eip155:1')
|
|
259
|
+
|
|
260
|
+
expect(awaiting.mock.calls.map(([e]) => e.silentOnly)).toEqual([
|
|
261
|
+
true,
|
|
262
|
+
false
|
|
263
|
+
])
|
|
264
|
+
expect(succeeded.mock.calls.map(([e]) => e.silentOnly)).toEqual([
|
|
265
|
+
true,
|
|
266
|
+
false
|
|
267
|
+
])
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it('passes no connect options when neither silentOnly nor requiredAddresses is set', async () => {
|
|
271
|
+
const provider = { supportedNetworkIds: ['eip155:1'] }
|
|
272
|
+
wallets[0].extensionInjectedProvider = provider as any
|
|
273
|
+
mockConnector.connect = vi.fn().mockResolvedValue({
|
|
274
|
+
networkId: 'eip155:1',
|
|
275
|
+
address: '0x1234',
|
|
276
|
+
availableAddresses: [{ address: '0x1234', networkId: 'eip155:1' }]
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
await connectionService.connect('injected', 'metamask', 'eip155:1', {
|
|
280
|
+
silentOnly: false
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
expect(mockConnector.connect).toHaveBeenCalledWith(
|
|
284
|
+
networks[0],
|
|
285
|
+
provider,
|
|
286
|
+
undefined
|
|
287
|
+
)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('rejects silentOnly for a non-injected mode before the attempt starts', async () => {
|
|
291
|
+
const walletConnectConnector: Connector = {
|
|
292
|
+
connect: vi.fn(),
|
|
293
|
+
disconnect: vi.fn(),
|
|
294
|
+
signMessage: vi.fn(),
|
|
295
|
+
sendTransaction: vi.fn(),
|
|
296
|
+
switchNetwork: vi.fn()
|
|
297
|
+
}
|
|
298
|
+
connectors.set('walletConnect', walletConnectConnector)
|
|
299
|
+
wallets[0].walletConnectProvider = {
|
|
300
|
+
supportedNetworkIds: ['eip155:1']
|
|
301
|
+
} as any
|
|
302
|
+
const connectingListener = vi.fn()
|
|
303
|
+
const awaiting = vi.fn()
|
|
304
|
+
eventManager.on('connecting', connectingListener)
|
|
305
|
+
eventManager.on('awaitingWallet', awaiting)
|
|
306
|
+
|
|
307
|
+
await expect(
|
|
308
|
+
connectionService.connect('walletConnect', 'metamask', 'eip155:1', {
|
|
309
|
+
silentOnly: true
|
|
310
|
+
})
|
|
311
|
+
).rejects.toThrow(
|
|
312
|
+
"silentOnly is not supported for connection mode 'walletConnect'"
|
|
313
|
+
)
|
|
314
|
+
// A caller bug, not the expected `not_authorized` miss.
|
|
315
|
+
await expect(
|
|
316
|
+
connectionService.connect('walletConnect', 'metamask', 'eip155:1', {
|
|
317
|
+
silentOnly: true
|
|
318
|
+
})
|
|
319
|
+
).rejects.not.toBeInstanceOf(WalletNotAuthorizedError)
|
|
320
|
+
|
|
321
|
+
// A validation failure: never reaches the wallet, never enters the funnel.
|
|
322
|
+
expect(walletConnectConnector.connect).not.toHaveBeenCalled()
|
|
323
|
+
expect(connectingListener).not.toHaveBeenCalled()
|
|
324
|
+
expect(awaiting).not.toHaveBeenCalled()
|
|
325
|
+
expect(connectionService['activeConnector']).toBeNull()
|
|
326
|
+
})
|
|
327
|
+
|
|
214
328
|
it('does not emit "connecting" when validation throws (unknown wallet)', async () => {
|
|
215
329
|
const connectingListener = vi.fn()
|
|
216
330
|
eventManager.on('connecting', connectingListener)
|
|
@@ -264,6 +378,44 @@ describe('ConnectionService', () => {
|
|
|
264
378
|
expect(awaiting.namespace).toBe('eip155')
|
|
265
379
|
expect(awaiting.chainId).toBe('eip155:1')
|
|
266
380
|
expect(awaiting.handoffId).toBe(events[1].data.handoffId)
|
|
381
|
+
expect(awaiting.networkSelection).toBe('explicit')
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
it('auto-picks a detected namespace over an undetected first network (ONC-2110)', async () => {
|
|
385
|
+
networks.push({
|
|
386
|
+
...networks[0],
|
|
387
|
+
id: 'tron:0x2b6653dc',
|
|
388
|
+
internalId: 2,
|
|
389
|
+
name: 'Tron',
|
|
390
|
+
namespace: 'tron',
|
|
391
|
+
networkType: 'tron'
|
|
392
|
+
} as Network)
|
|
393
|
+
// Trust mobile: catalog lists Tron first, but only EVM was detected.
|
|
394
|
+
wallets[0].extensionInjectedProvider = {
|
|
395
|
+
supportedNetworkIds: ['tron:0x2b6653dc', 'eip155:1'],
|
|
396
|
+
namespaceMetaData: {
|
|
397
|
+
tron: { injectedId: 'trustWallet.tronLink', installed: false },
|
|
398
|
+
eip155: { eip155Name: 'Trust Wallet', installed: true }
|
|
399
|
+
}
|
|
400
|
+
} as any
|
|
401
|
+
const awaiting = vi.fn()
|
|
402
|
+
eventManager.on('awaitingWallet', awaiting)
|
|
403
|
+
mockConnector.connect = vi.fn().mockResolvedValue({
|
|
404
|
+
networkId: 'eip155:1',
|
|
405
|
+
address: '0x1234',
|
|
406
|
+
availableAddresses: [{ address: '0x1234', networkId: 'eip155:1' }]
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
await connectionService.connect('injected', 'metamask')
|
|
410
|
+
|
|
411
|
+
expect(awaiting).toHaveBeenCalledWith(
|
|
412
|
+
expect.objectContaining({
|
|
413
|
+
chainId: 'eip155:1',
|
|
414
|
+
namespace: 'eip155',
|
|
415
|
+
networkSelection: 'detected'
|
|
416
|
+
})
|
|
417
|
+
)
|
|
418
|
+
expect(sessionManager.getSession().activeNetwork?.id).toBe('eip155:1')
|
|
267
419
|
})
|
|
268
420
|
|
|
269
421
|
it('emits one stable attempt ID per connect and a different ID for the next connect', async () => {
|