@meshconnect/uwc-core 1.0.2 → 1.1.0-snapshot.86f6845
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 +247 -76
- package/dist/events.d.ts +241 -5
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +77 -1
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/logger/create-logger.d.ts +32 -0
- package/dist/logger/create-logger.d.ts.map +1 -0
- package/dist/logger/create-logger.js +79 -0
- package/dist/logger/create-logger.js.map +1 -0
- package/dist/managers/event-manager.d.ts.map +1 -1
- package/dist/managers/event-manager.js +5 -1
- package/dist/managers/event-manager.js.map +1 -1
- package/dist/observability/connect-observer.d.ts +33 -0
- package/dist/observability/connect-observer.d.ts.map +1 -0
- package/dist/observability/connect-observer.js +115 -0
- package/dist/observability/connect-observer.js.map +1 -0
- package/dist/observability/focus-tracker.d.ts +42 -0
- package/dist/observability/focus-tracker.d.ts.map +1 -0
- package/dist/observability/focus-tracker.js +57 -0
- package/dist/observability/focus-tracker.js.map +1 -0
- package/dist/observability/instrument-handoff.d.ts +54 -0
- package/dist/observability/instrument-handoff.d.ts.map +1 -0
- package/dist/observability/instrument-handoff.js +173 -0
- package/dist/observability/instrument-handoff.js.map +1 -0
- package/dist/observability/lifecycle-bridge.d.ts +69 -0
- package/dist/observability/lifecycle-bridge.d.ts.map +1 -0
- package/dist/observability/lifecycle-bridge.js +155 -0
- package/dist/observability/lifecycle-bridge.js.map +1 -0
- package/dist/observability/scrub.d.ts +31 -0
- package/dist/observability/scrub.d.ts.map +1 -0
- package/dist/observability/scrub.js +167 -0
- package/dist/observability/scrub.js.map +1 -0
- package/dist/observability/telemetry.d.ts +157 -0
- package/dist/observability/telemetry.d.ts.map +1 -0
- package/dist/observability/telemetry.js +373 -0
- package/dist/observability/telemetry.js.map +1 -0
- package/dist/services/connection-service.d.ts +23 -1
- package/dist/services/connection-service.d.ts.map +1 -1
- package/dist/services/connection-service.js +128 -19
- package/dist/services/connection-service.js.map +1 -1
- package/dist/services/network-switch-service.d.ts +2 -1
- package/dist/services/network-switch-service.d.ts.map +1 -1
- package/dist/services/network-switch-service.js +45 -16
- package/dist/services/network-switch-service.js.map +1 -1
- package/dist/services/signature-service.d.ts +14 -7
- package/dist/services/signature-service.d.ts.map +1 -1
- package/dist/services/signature-service.js +56 -58
- package/dist/services/signature-service.js.map +1 -1
- package/dist/services/transaction-service.d.ts +19 -5
- package/dist/services/transaction-service.d.ts.map +1 -1
- package/dist/services/transaction-service.js +55 -38
- package/dist/services/transaction-service.js.map +1 -1
- package/dist/universal-wallet-connector.d.ts +56 -1
- package/dist/universal-wallet-connector.d.ts.map +1 -1
- package/dist/universal-wallet-connector.js +207 -110
- package/dist/universal-wallet-connector.js.map +1 -1
- package/dist/utils/id.d.ts +2 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +41 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/resolve-session-provider.d.ts +10 -0
- package/dist/utils/resolve-session-provider.d.ts.map +1 -0
- package/dist/utils/resolve-session-provider.js +21 -0
- package/dist/utils/resolve-session-provider.js.map +1 -0
- package/dist/utils/to-wallet-error.d.ts +35 -0
- package/dist/utils/to-wallet-error.d.ts.map +1 -0
- package/dist/utils/to-wallet-error.js +130 -0
- package/dist/utils/to-wallet-error.js.map +1 -0
- package/dist/utils/validate-wallet-session.d.ts +15 -0
- package/dist/utils/validate-wallet-session.d.ts.map +1 -0
- package/dist/utils/validate-wallet-session.js +29 -0
- package/dist/utils/validate-wallet-session.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +6 -6
- package/src/events.ts +326 -14
- package/src/index.ts +10 -0
- package/src/logger/create-logger.test.ts +126 -0
- package/src/logger/create-logger.ts +112 -0
- package/src/managers/event-manager.test.ts +55 -0
- package/src/managers/event-manager.ts +5 -1
- package/src/observability/connect-observer.test.ts +214 -0
- package/src/observability/connect-observer.ts +128 -0
- package/src/observability/focus-tracker.test.ts +191 -0
- package/src/observability/focus-tracker.ts +96 -0
- package/src/observability/instrument-handoff.test.ts +397 -0
- package/src/observability/instrument-handoff.ts +231 -0
- package/src/observability/lifecycle-bridge.test.ts +398 -0
- package/src/observability/lifecycle-bridge.ts +216 -0
- package/src/observability/scrub.test.ts +183 -0
- package/src/observability/scrub.ts +174 -0
- package/src/observability/telemetry.test.ts +549 -0
- package/src/observability/telemetry.ts +546 -0
- package/src/services/connection-service.test.ts +261 -1
- package/src/services/connection-service.ts +167 -38
- package/src/services/network-switch-service.test.ts +51 -1
- package/src/services/network-switch-service.ts +56 -24
- package/src/services/signature-service.test.ts +181 -76
- package/src/services/signature-service.ts +75 -85
- package/src/services/transaction-service.test.ts +270 -69
- package/src/services/transaction-service.ts +74 -51
- package/src/universal-wallet-connector.instrumentation-guard.test.ts +199 -0
- package/src/universal-wallet-connector.observability.test.ts +610 -0
- package/src/universal-wallet-connector.test.ts +27 -146
- package/src/universal-wallet-connector.ts +279 -142
- package/src/utils/id.test.ts +15 -0
- package/src/utils/id.ts +48 -0
- package/src/utils/resolve-session-provider.test.ts +54 -0
- package/src/utils/resolve-session-provider.ts +25 -0
- package/src/utils/to-wallet-error.test.ts +181 -0
- package/src/utils/to-wallet-error.ts +152 -0
- package/src/utils/validate-wallet-session.test.ts +77 -0
- package/src/utils/validate-wallet-session.ts +40 -0
- package/src/version.test.ts +11 -0
- package/src/version.ts +2 -0
package/README.md
CHANGED
|
@@ -121,11 +121,11 @@ await connector.connect('injected', 'metamask', 'eip155:1')
|
|
|
121
121
|
|
|
122
122
|
Three mutually exclusive ways a user can connect:
|
|
123
123
|
|
|
124
|
-
| Mode | Description
|
|
125
|
-
| --------------- |
|
|
124
|
+
| Mode | Description | Typical wallets |
|
|
125
|
+
| --------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------- |
|
|
126
126
|
| `injected` | Browser-extension or in-app injected provider (EIP-6963, Solana Wallet Standard, Tron, TON JS Bridge) | MetaMask, Phantom, Tonkeeper extension |
|
|
127
|
-
| `walletConnect` | WalletConnect v2 relay + QR code / deeplink
|
|
128
|
-
| `tonConnect` | TON Connect JS Bridge + QR code / universal link
|
|
127
|
+
| `walletConnect` | WalletConnect v2 relay + QR code / deeplink | Any WC-compatible mobile wallet |
|
|
128
|
+
| `tonConnect` | TON Connect JS Bridge + QR code / universal link | Tonkeeper, OKX TON Wallet |
|
|
129
129
|
|
|
130
130
|
A wallet can support multiple modes. `isConnectionModeAvailable(mode, walletId)`
|
|
131
131
|
tells you which ones are actually viable in the current runtime.
|
|
@@ -141,7 +141,7 @@ interface Session {
|
|
|
141
141
|
activeNetwork: Network | null
|
|
142
142
|
activeAddress: string | null
|
|
143
143
|
publicKey: string | null
|
|
144
|
-
availableNetworks: Network[]
|
|
144
|
+
availableNetworks: Network[] // networks the current wallet supports
|
|
145
145
|
availableAddresses: AvailableAddress[] // one per chain the wallet exposed
|
|
146
146
|
activeWalletCapabilities: Record<string, EVMCapabilities> | null
|
|
147
147
|
activeNetworkWalletCapabilities: EVMCapabilities | null
|
|
@@ -153,33 +153,44 @@ a `sessionChanged` event fires.
|
|
|
153
153
|
|
|
154
154
|
### Observer / event model
|
|
155
155
|
|
|
156
|
-
The connector is an event emitter.
|
|
157
|
-
|
|
158
|
-
| Event
|
|
159
|
-
|
|
|
160
|
-
| `ready`
|
|
161
|
-
| `walletsDetected`
|
|
162
|
-
| `connecting`
|
|
163
|
-
| `connectionUri`
|
|
164
|
-
| `connected`
|
|
165
|
-
| `disconnected`
|
|
166
|
-
| `sessionChanged`
|
|
167
|
-
| `networkSwitching`
|
|
168
|
-
| `networkSwitched`
|
|
169
|
-
| `capabilitiesUpdated`
|
|
170
|
-
| `error`
|
|
171
|
-
| `change`
|
|
172
|
-
|
|
173
|
-
|
|
156
|
+
The connector is an event emitter. The state-change events are:
|
|
157
|
+
|
|
158
|
+
| Event | When it fires |
|
|
159
|
+
| --------------------- | -------------------------------------------------------- |
|
|
160
|
+
| `ready` | Initial wallet detection has completed |
|
|
161
|
+
| `walletsDetected` | Detection completed, payload is the enriched wallet list |
|
|
162
|
+
| `connecting` | `connect()` started |
|
|
163
|
+
| `connectionUri` | A WC/TonConnect pairing URI is now available |
|
|
164
|
+
| `connected` | `connect()` finished successfully |
|
|
165
|
+
| `disconnected` | `disconnect()` finished (session cleared) |
|
|
166
|
+
| `sessionChanged` | Any session field changed |
|
|
167
|
+
| `networkSwitching` | Network switch started or ended |
|
|
168
|
+
| `networkSwitched` | Network switch finished successfully |
|
|
169
|
+
| `capabilitiesUpdated` | `getWalletCapabilities()` refreshed data |
|
|
170
|
+
| `error` | A user-initiated op threw |
|
|
171
|
+
| `change` | Catch-all — fires after every other state-change event |
|
|
172
|
+
|
|
173
|
+
Plus a set of **telemetry-only** events used for logging and wallet-handoff
|
|
174
|
+
visibility (see [Logging & observability](#logging--observability)). These
|
|
175
|
+
deliberately do **not** cascade to `change` (so they never trigger re-renders):
|
|
176
|
+
`log`, `awaitingWallet`, `walletSucceeded`, `walletRejected`, `walletFailed`,
|
|
177
|
+
`walletTimedOut`, `walletDiscoveryFailed`, `bridgeError`, `relayError`,
|
|
178
|
+
`walletAccountChanged`, `walletChainChanged`, `walletDisconnected`,
|
|
179
|
+
`walletSessionExpired`, `lifecycleTelemetryUnavailable`,
|
|
180
|
+
`pageHiddenDuringHandoff`, `pageVisibleDuringHandoff` — the full list is the
|
|
181
|
+
`TELEMETRY_ONLY_EVENTS` export in `events.ts`.
|
|
182
|
+
|
|
183
|
+
Use `on(eventName, listener)` for targeted updates. Every state-change event also
|
|
174
184
|
cascades to `change`, so the legacy `subscribe(listener)` pattern still works.
|
|
175
185
|
|
|
176
186
|
### AbortSignal
|
|
177
187
|
|
|
178
188
|
Every async operation accepts an optional `{ signal }`. If the signal aborts,
|
|
179
|
-
the operation rejects with
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
the operation rejects with the signal's `reason` — a `DOMException` named
|
|
190
|
+
`AbortError` by default, or whatever you passed to `controller.abort(reason)` —
|
|
191
|
+
and no session mutations happen after the abort, even if the wallet prompt has
|
|
192
|
+
already returned. Wallet prompts themselves cannot usually be cancelled — the
|
|
193
|
+
signal protects your app from acting on stale results.
|
|
183
194
|
|
|
184
195
|
---
|
|
185
196
|
|
|
@@ -296,8 +307,8 @@ const connector = new UniversalWalletConnector(
|
|
|
296
307
|
)
|
|
297
308
|
```
|
|
298
309
|
|
|
299
|
-
Constructing a **second** instance logs
|
|
300
|
-
almost always a mistake. For tests and full-logout flows, call
|
|
310
|
+
Constructing a **second** instance logs an error through the configured logger
|
|
311
|
+
— it's almost always a mistake. For tests and full-logout flows, call
|
|
301
312
|
`UniversalWalletConnector.resetInstance()` to clear the cached reference.
|
|
302
313
|
|
|
303
314
|
---
|
|
@@ -307,15 +318,15 @@ almost always a mistake. For tests and full-logout flows, call
|
|
|
307
318
|
All reads are synchronous and cheap. Call them any time.
|
|
308
319
|
|
|
309
320
|
```ts
|
|
310
|
-
connector.getSession()
|
|
311
|
-
connector.getState()
|
|
312
|
-
connector.isReady()
|
|
313
|
-
connector.getWallets()
|
|
314
|
-
connector.getNetworks()
|
|
321
|
+
connector.getSession() // current Session
|
|
322
|
+
connector.getState() // { session } — parity with older API
|
|
323
|
+
connector.isReady() // true once detection completed
|
|
324
|
+
connector.getWallets() // wallets with `installed` flags set
|
|
325
|
+
connector.getNetworks() // the configured networks array
|
|
315
326
|
connector.isConnectionModeAvailable('walletConnect', 'metamask')
|
|
316
|
-
connector.getConnectionURI()
|
|
327
|
+
connector.getConnectionURI() // WC/TonConnect pairing URI, if any
|
|
317
328
|
connector.getNetworkSwitchLoadingState()
|
|
318
|
-
|
|
329
|
+
// { isLoading, isWaitingForUserApproval }
|
|
319
330
|
connector.getActiveWalletCapabilities() // Record<NetworkId, EVMCapabilities> | null
|
|
320
331
|
```
|
|
321
332
|
|
|
@@ -363,18 +374,21 @@ connector.off('connectionUri', handler)
|
|
|
363
374
|
import type { UWCEventMap } from '@meshconnect/uwc-core'
|
|
364
375
|
|
|
365
376
|
// Narrowed per event:
|
|
366
|
-
connector.on('connecting',
|
|
367
|
-
connector.on('connected',
|
|
368
|
-
connector.on('disconnected',
|
|
377
|
+
connector.on('connecting', ({ connectionMode, walletId }) => {})
|
|
378
|
+
connector.on('connected', ({ session }) => {})
|
|
379
|
+
connector.on('disconnected', () => {})
|
|
369
380
|
connector.on('connectionUri', ({ uri, connectionMode }) => {})
|
|
370
|
-
connector.on(
|
|
371
|
-
|
|
372
|
-
|
|
381
|
+
connector.on(
|
|
382
|
+
'networkSwitching',
|
|
383
|
+
({ isLoading, isWaitingForUserApproval }) => {}
|
|
384
|
+
)
|
|
385
|
+
connector.on('networkSwitched', ({ network }) => {})
|
|
386
|
+
connector.on('sessionChanged', ({ session }) => {})
|
|
373
387
|
connector.on('capabilitiesUpdated', ({ capabilities }) => {})
|
|
374
|
-
connector.on('walletsDetected',
|
|
388
|
+
connector.on('walletsDetected', ({ wallets }) => {})
|
|
375
389
|
connector.on('ready', () => {})
|
|
376
390
|
connector.on('error', ({ error, operation }) => {})
|
|
377
|
-
connector.on('change', () => {})
|
|
391
|
+
connector.on('change', () => {}) // catch-all; no payload
|
|
378
392
|
```
|
|
379
393
|
|
|
380
394
|
### Legacy `subscribe()`
|
|
@@ -473,8 +487,8 @@ await connector.switchNetwork('eip155:8453') // Base
|
|
|
473
487
|
// Track loading for UI
|
|
474
488
|
connector.on('networkSwitching', ({ isLoading, isWaitingForUserApproval }) => {
|
|
475
489
|
if (isWaitingForUserApproval) showBanner('Approve in your wallet…')
|
|
476
|
-
else if (isLoading)
|
|
477
|
-
else
|
|
490
|
+
else if (isLoading) showBanner('Switching network…')
|
|
491
|
+
else hideBanner()
|
|
478
492
|
})
|
|
479
493
|
```
|
|
480
494
|
|
|
@@ -614,9 +628,10 @@ connectButton.addEventListener('click', () => {
|
|
|
614
628
|
cancelButton.addEventListener('click', () => controller.abort())
|
|
615
629
|
```
|
|
616
630
|
|
|
617
|
-
When the signal aborts, the in-flight promise rejects with
|
|
618
|
-
|
|
619
|
-
|
|
631
|
+
When the signal aborts, the in-flight promise rejects with the signal's
|
|
632
|
+
`reason` (an `AbortError` unless you aborted with a custom one). Session state
|
|
633
|
+
is **not** mutated after the abort, even if the wallet prompt later returns a
|
|
634
|
+
result. This is especially useful in component lifecycles:
|
|
620
635
|
|
|
621
636
|
```ts
|
|
622
637
|
// Single-page app: abort on route change
|
|
@@ -647,8 +662,8 @@ interface WalletError {
|
|
|
647
662
|
```
|
|
648
663
|
|
|
649
664
|
- `rejected` — user dismissed the prompt.
|
|
650
|
-
- `expired`
|
|
651
|
-
- `unknown`
|
|
665
|
+
- `expired` — WalletConnect pairing proposal timed out.
|
|
666
|
+
- `unknown` — everything else (network errors, bad wallet state, …).
|
|
652
667
|
|
|
653
668
|
### Per-call handling
|
|
654
669
|
|
|
@@ -670,26 +685,29 @@ try {
|
|
|
670
685
|
```ts
|
|
671
686
|
import type { UWCOperation } from '@meshconnect/uwc-core'
|
|
672
687
|
|
|
673
|
-
connector.on(
|
|
674
|
-
error
|
|
675
|
-
operation: UWCOperation
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
}
|
|
682
|
-
|
|
688
|
+
connector.on(
|
|
689
|
+
'error',
|
|
690
|
+
({ error, operation }: { error: WalletError; operation: UWCOperation }) => {
|
|
691
|
+
analytics.track('wallet.error', {
|
|
692
|
+
operation,
|
|
693
|
+
type: error.type,
|
|
694
|
+
message: error.message
|
|
695
|
+
})
|
|
696
|
+
}
|
|
697
|
+
)
|
|
683
698
|
```
|
|
684
699
|
|
|
685
700
|
`operation` is one of:
|
|
686
|
-
`'connect' | 'disconnect' | 'switchNetwork' | 'signMessage' | 'sendTransaction' | 'getWalletCapabilities' | 'initialize'`.
|
|
701
|
+
`'connect' | 'disconnect' | 'switchNetwork' | 'signMessage' | 'signTypedData' | 'sendTransaction' | 'signSolanaTransaction' | 'getWalletCapabilities' | 'initialize'`.
|
|
687
702
|
|
|
688
703
|
### AbortError
|
|
689
704
|
|
|
690
|
-
Aborted operations reject with a `DOMException` whose
|
|
691
|
-
|
|
692
|
-
|
|
705
|
+
Aborted operations reject with the signal's `reason`: a `DOMException` whose
|
|
706
|
+
`.name === 'AbortError'` by default, or your own value when you called
|
|
707
|
+
`controller.abort(customReason)`. Either way they are **not** routed through
|
|
708
|
+
the `error` event — UWC consults the signal itself (not just the error name),
|
|
709
|
+
so even a custom-reason abort is treated as a cancellation, not a wallet
|
|
710
|
+
failure.
|
|
693
711
|
|
|
694
712
|
```ts
|
|
695
713
|
try {
|
|
@@ -705,6 +723,139 @@ try {
|
|
|
705
723
|
|
|
706
724
|
---
|
|
707
725
|
|
|
726
|
+
## Logging & observability
|
|
727
|
+
|
|
728
|
+
UWC ships **no** third-party telemetry SDK. Instead it exposes two opt-in seams
|
|
729
|
+
you wire to whatever stack you run (Datadog, Amplitude, Segment, a plain
|
|
730
|
+
console). With neither configured, there is **zero external egress** — the
|
|
731
|
+
security-first default for a sign-only wallet layer.
|
|
732
|
+
|
|
733
|
+
### Logging
|
|
734
|
+
|
|
735
|
+
A pluggable, level-gated logger backs UWC's internal logging. Configure it via
|
|
736
|
+
`UWCConfig`:
|
|
737
|
+
|
|
738
|
+
```ts
|
|
739
|
+
const connector = new UniversalWalletConnector({
|
|
740
|
+
networks,
|
|
741
|
+
wallets,
|
|
742
|
+
logLevel: 'info', // console threshold; default 'warn'
|
|
743
|
+
logger: myAppLogger // optional: redirect output into your own logger
|
|
744
|
+
})
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
`logger` (any object with `debug`/`info`/`warn`/`error`) receives the raw
|
|
748
|
+
`(message, ...args)`; omit it and a console sink is used with an ISO/level prefix.
|
|
749
|
+
|
|
750
|
+
Tune the threshold at runtime — useful when triaging in a wallet's in-app
|
|
751
|
+
browser where you can't redeploy:
|
|
752
|
+
|
|
753
|
+
```ts
|
|
754
|
+
connector.setLogLevel('debug') // programmatic
|
|
755
|
+
window.UWC_DEBUG = true // or from the console: surfaces everything
|
|
756
|
+
window.UWC_DEBUG = 'info' // or a specific level (only ever lowers it)
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### Observer (telemetry)
|
|
760
|
+
|
|
761
|
+
Implement `UWCObserver` to receive a normalized, PII-safe event stream plus all
|
|
762
|
+
log lines. The vendor code lives in **your** app, never in UWC:
|
|
763
|
+
|
|
764
|
+
```ts
|
|
765
|
+
import type { UWCObserver } from '@meshconnect/uwc-core'
|
|
766
|
+
|
|
767
|
+
const observer: UWCObserver = {
|
|
768
|
+
onEvent: e => datadogRum.addAction(`uwc.${e.name}`, e), // or amplitude.track / …
|
|
769
|
+
onLog: (level, message, args) =>
|
|
770
|
+
myAppLogger[level](`[uwc] ${message}`, ...args)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const connector = new UniversalWalletConnector({ networks, wallets, observer })
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
> **Keep `onEvent` / `onLog` non-blocking.** They are called synchronously on the
|
|
777
|
+
> wallet-operation path — a terminal event fires between the wallet returning a
|
|
778
|
+
> result and that result reaching your `await`, so a slow handler adds latency to
|
|
779
|
+
> connect / sign / sendTransaction. Queue-backed sinks (Datadog `addAction`,
|
|
780
|
+
> Segment `track`) are already non-blocking; for anything heavy, defer it yourself
|
|
781
|
+
> (`onEvent: e => queueMicrotask(() => …)`).
|
|
782
|
+
|
|
783
|
+
Every `onEvent` record is a `UWCTelemetryEvent` — only routing facets
|
|
784
|
+
(`operation`, `connectionMode`, `namespace`, `chainId`, `walletId`, `durationMs`,
|
|
785
|
+
outcome…) plus a `sdkSessionId` correlation id. By default `sdkSessionId` is a
|
|
786
|
+
generated per-instance id; pass `correlationId` (a string or getter) to align it
|
|
787
|
+
to your own session key (e.g. `link_sess_id`) so UWC events join your existing
|
|
788
|
+
Datadog/RUM telemetry without a separate mapping:
|
|
789
|
+
|
|
790
|
+
```ts
|
|
791
|
+
new UniversalWalletConnector({
|
|
792
|
+
...config,
|
|
793
|
+
observer,
|
|
794
|
+
correlationId: () => myLinkSession.id // resolved per-event; getter may be lazy/dynamic
|
|
795
|
+
})
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
**Structured fields never carry a signing payload or wallet address** — the shape has no slot for
|
|
799
|
+
them. The two free-form surfaces (`error.message` and `onLog` args) additionally
|
|
800
|
+
pass through a key + content scrubber that redacts sensitive keys, EVM hex blobs
|
|
801
|
+
(addresses / signatures / keys), secret URL params, bearer tokens, and Solana
|
|
802
|
+
base58 / TON (`EQ…`/`UQ…`) addresses. This last pass is best-effort and biased
|
|
803
|
+
toward **over**-redaction (some non-secret base58-looking ids/hashes get redacted
|
|
804
|
+
by design). One gap remains: BARE (non-`0x`) hex — a raw hex string is far more
|
|
805
|
+
often a legitimate id/hash than a secret, so only `0x`-prefixed hex is redacted.
|
|
806
|
+
Structured fields never carry addresses regardless.
|
|
807
|
+
|
|
808
|
+
### Wallet-handoff funnel
|
|
809
|
+
|
|
810
|
+
The moment UWC hands control to a wallet (extension popup, WC deeplink, TON
|
|
811
|
+
sheet) it emits `awaitingWallet`; when the wallet answers it emits exactly one
|
|
812
|
+
terminal — all sharing a `handoffId` and carrying `durationMs`:
|
|
813
|
+
|
|
814
|
+
```
|
|
815
|
+
awaitingWallet ─┬─ walletSucceeded success
|
|
816
|
+
├─ walletRejected user said no (4001 / rejected)
|
|
817
|
+
└─ walletFailed any other error
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
A non-cancelling `walletTimedOut` marker also fires if a non-TON handoff is still
|
|
821
|
+
pending past `walletResponseTimeoutMs` (default 60s, `0` disables). It is purely
|
|
822
|
+
observational — it never rejects, aborts, or mutates state, and is cleared the
|
|
823
|
+
instant the op settles, so a fast op never false-fires it. This turns the
|
|
824
|
+
previously-blind connect/sign/approve window into a per-(chain × wallet × flow)
|
|
825
|
+
timed funnel.
|
|
826
|
+
|
|
827
|
+
> A failed or rejected op emits **both** a handoff terminal
|
|
828
|
+
> (`walletFailed` / `walletRejected`) **and** the back-compat `error` event. The
|
|
829
|
+
> duplicate `error` record carries `handoffCovered: true`, so failure metrics
|
|
830
|
+
> are: **terminals + `error` where NOT `handoffCovered`** (the uncovered ones
|
|
831
|
+
> are pre-wallet validation failures, which have no terminal). Counting
|
|
832
|
+
> terminals and all `error` records double-counts each wallet failure.
|
|
833
|
+
> One documented edge: a wallet that rejects with a bare string/number (not an
|
|
834
|
+
> object) can't be marked — a WeakSet can't hold primitives — so that rare
|
|
835
|
+
> failure double-counts. EIP-1193 requires object errors, and wrapping the
|
|
836
|
+
> thrown value to make it markable would mutate what the caller receives.
|
|
837
|
+
|
|
838
|
+
> **Volume.** A single connect can emit several records (`awaitingWallet`, a
|
|
839
|
+
> terminal, plus any focus / provider-lifecycle events), and
|
|
840
|
+
> `pageHidden`/`pageVisibleDuringHandoff` fire on each visibility toggle while a
|
|
841
|
+
> handoff is pending. If you forward `onEvent` straight to a metered backend
|
|
842
|
+
> (Datadog RUM, etc.), sample or filter by `name` on your side — UWC emits every
|
|
843
|
+
> record and does not sample for you. Provider-lifecycle events are additionally
|
|
844
|
+
> capped at 100 per attachment (`MAX_LIFECYCLE_EVENTS_PER_ATTACHMENT`) so a
|
|
845
|
+
> storming provider cannot drive unbounded egress.
|
|
846
|
+
|
|
847
|
+
### Teardown
|
|
848
|
+
|
|
849
|
+
Call `dispose()` on full logout to release observer subscriptions, tear down any
|
|
850
|
+
live provider-lifecycle listeners on a connected session, and clear all
|
|
851
|
+
listeners:
|
|
852
|
+
|
|
853
|
+
```ts
|
|
854
|
+
connector.dispose()
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
---
|
|
858
|
+
|
|
708
859
|
## Testing
|
|
709
860
|
|
|
710
861
|
The shared singleton can leak state between tests. Reset it in `beforeEach`:
|
|
@@ -726,7 +877,9 @@ When unit-testing consumer code, mock the whole module:
|
|
|
726
877
|
vi.mock('@meshconnect/uwc-core', () => ({
|
|
727
878
|
UniversalWalletConnector: class {
|
|
728
879
|
static getInstance = () => new this()
|
|
729
|
-
getSession = () => ({
|
|
880
|
+
getSession = () => ({
|
|
881
|
+
/* fixture */
|
|
882
|
+
})
|
|
730
883
|
on = () => () => {}
|
|
731
884
|
once = () => () => {}
|
|
732
885
|
off = () => {}
|
|
@@ -757,12 +910,12 @@ connector.on('sessionChanged', ({ session }) => {
|
|
|
757
910
|
session.activeAddress ?? 'not connected'
|
|
758
911
|
})
|
|
759
912
|
|
|
760
|
-
document
|
|
761
|
-
|
|
762
|
-
)
|
|
763
|
-
document
|
|
764
|
-
|
|
765
|
-
)
|
|
913
|
+
document
|
|
914
|
+
.querySelector('#connect')!
|
|
915
|
+
.addEventListener('click', () => connector.connect('injected', 'metamask'))
|
|
916
|
+
document
|
|
917
|
+
.querySelector('#disconnect')!
|
|
918
|
+
.addEventListener('click', () => connector.disconnect())
|
|
766
919
|
```
|
|
767
920
|
|
|
768
921
|
### Show QR and auto-close modal on pair
|
|
@@ -805,7 +958,9 @@ const controller = new AbortController()
|
|
|
805
958
|
window.addEventListener('beforeunload', () => controller.abort())
|
|
806
959
|
|
|
807
960
|
try {
|
|
808
|
-
const sig = await connector.signMessage('Sign in', {
|
|
961
|
+
const sig = await connector.signMessage('Sign in', {
|
|
962
|
+
signal: controller.signal
|
|
963
|
+
})
|
|
809
964
|
submitLogin(sig)
|
|
810
965
|
} catch (error) {
|
|
811
966
|
if ((error as { name?: string }).name === 'AbortError') return
|
|
@@ -828,6 +983,12 @@ export interface UWCConfig {
|
|
|
828
983
|
walletConnectConfig?: WalletConnectConfig
|
|
829
984
|
tonConnectConfig?: TonConnectConfig
|
|
830
985
|
tronConnectorConfig?: TronConnectorConfig
|
|
986
|
+
// Observability (all optional; defaults preserve current behaviour)
|
|
987
|
+
observer?: UWCObserver // vendor-agnostic telemetry sink; default = no egress
|
|
988
|
+
logger?: Logger // redirect internal logging; default = console
|
|
989
|
+
logLevel?: LogLevel // console threshold; default 'warn'
|
|
990
|
+
walletResponseTimeoutMs?: number // walletTimedOut deadline; default 60_000, 0 disables
|
|
991
|
+
correlationId?: string | (() => string) // align sdkSessionId to your session key
|
|
831
992
|
}
|
|
832
993
|
|
|
833
994
|
export interface OperationOptions {
|
|
@@ -836,9 +997,15 @@ export interface OperationOptions {
|
|
|
836
997
|
|
|
837
998
|
export type UWCEventName = keyof UWCEventMap
|
|
838
999
|
export type UWCOperation =
|
|
839
|
-
| 'connect'
|
|
840
|
-
| '
|
|
841
|
-
| '
|
|
1000
|
+
| 'connect'
|
|
1001
|
+
| 'disconnect'
|
|
1002
|
+
| 'switchNetwork'
|
|
1003
|
+
| 'signMessage'
|
|
1004
|
+
| 'signTypedData'
|
|
1005
|
+
| 'sendTransaction'
|
|
1006
|
+
| 'signSolanaTransaction'
|
|
1007
|
+
| 'getWalletCapabilities'
|
|
1008
|
+
| 'initialize'
|
|
842
1009
|
```
|
|
843
1010
|
|
|
844
1011
|
### Static methods
|
|
@@ -879,6 +1046,10 @@ getNetworkSwitchLoadingState(): {
|
|
|
879
1046
|
getActiveWalletCapabilities(): Record<string, EVMCapabilities> | null
|
|
880
1047
|
isConnectionModeAvailable(mode: ConnectionMode, walletId: string): boolean
|
|
881
1048
|
|
|
1049
|
+
// Observability
|
|
1050
|
+
setLogLevel(level: LogLevel): void // tune the console threshold at runtime
|
|
1051
|
+
dispose(): void // release observer subs + clear listeners
|
|
1052
|
+
|
|
882
1053
|
// Events
|
|
883
1054
|
on<K extends UWCEventName>(event: K, listener: UWCEventListener<K>): () => void
|
|
884
1055
|
once<K extends UWCEventName>(event: K, listener: UWCEventListener<K>): () => void
|