@meshconnect/uwc-core 1.3.0 → 1.4.0-snapshot.103d25f

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.
Files changed (37) hide show
  1. package/README.md +5 -0
  2. package/dist/events.d.ts +50 -0
  3. package/dist/events.d.ts.map +1 -1
  4. package/dist/events.js +6 -0
  5. package/dist/events.js.map +1 -1
  6. package/dist/observability/instrument-handoff.d.ts +6 -0
  7. package/dist/observability/instrument-handoff.d.ts.map +1 -1
  8. package/dist/observability/instrument-handoff.js +6 -3
  9. package/dist/observability/instrument-handoff.js.map +1 -1
  10. package/dist/observability/telemetry.d.ts +13 -0
  11. package/dist/observability/telemetry.d.ts.map +1 -1
  12. package/dist/observability/telemetry.js +55 -6
  13. package/dist/observability/telemetry.js.map +1 -1
  14. package/dist/services/connection-service.d.ts.map +1 -1
  15. package/dist/services/connection-service.js +12 -4
  16. package/dist/services/connection-service.js.map +1 -1
  17. package/dist/services/network-switch-service.d.ts.map +1 -1
  18. package/dist/services/network-switch-service.js +2 -1
  19. package/dist/services/network-switch-service.js.map +1 -1
  20. package/dist/universal-wallet-connector.d.ts +11 -0
  21. package/dist/universal-wallet-connector.d.ts.map +1 -1
  22. package/dist/universal-wallet-connector.js +40 -1
  23. package/dist/universal-wallet-connector.js.map +1 -1
  24. package/dist/version.d.ts +1 -1
  25. package/dist/version.js +1 -1
  26. package/package.json +5 -5
  27. package/src/events.ts +59 -0
  28. package/src/observability/instrument-handoff.test.ts +31 -0
  29. package/src/observability/instrument-handoff.ts +12 -3
  30. package/src/observability/telemetry.test.ts +71 -0
  31. package/src/observability/telemetry.ts +68 -6
  32. package/src/services/connection-service.test.ts +11 -5
  33. package/src/services/connection-service.ts +17 -4
  34. package/src/services/network-switch-service.ts +2 -1
  35. package/src/universal-wallet-connector.test.ts +78 -1
  36. package/src/universal-wallet-connector.ts +49 -1
  37. package/src/version.ts +1 -1
@@ -139,7 +139,8 @@ export class ConnectionService {
139
139
  network,
140
140
  provider,
141
141
  connectionMode,
142
- signal
142
+ signal,
143
+ wallet.name
143
144
  ),
144
145
  signal
145
146
  )
@@ -189,7 +190,11 @@ export class ConnectionService {
189
190
  availableNetworks: this.networks.filter(n =>
190
191
  provider.supportedNetworkIds.includes(n.id)
191
192
  ),
192
- availableAddresses: filteredAvailableAddresses
193
+ availableAddresses: filteredAvailableAddresses,
194
+ // Connection provenance for observability — set definitively on every
195
+ // connect so a fresh pairing clears any prior reused flag (MFS-642). Rides
196
+ // `connected` + every later operation handoff via sessionHandoffContext.
197
+ reused: result.reused === true
193
198
  })
194
199
 
195
200
  const session = this.sessionManager.getSession()
@@ -295,9 +300,17 @@ export class ConnectionService {
295
300
  network: Network,
296
301
  provider: SupportedProvider,
297
302
  connectionMode: ConnectionMode,
298
- signal?: AbortSignal
303
+ signal?: AbortSignal,
304
+ // Passed to the connector as ConnectOptions.walletName so the WalletConnect
305
+ // connector can confirm a reused session belongs to the selected wallet
306
+ // (MFS-642). Ignored by connectors without session reuse.
307
+ walletName?: string
299
308
  ): Promise<ConnectorResult> {
300
- const connectPromise = connector.connect(network, provider)
309
+ const connectPromise = connector.connect(
310
+ network,
311
+ provider,
312
+ walletName ? { walletName } : undefined
313
+ )
301
314
 
302
315
  let pollingStopped = false
303
316
  let emittedUri: string | undefined
@@ -145,7 +145,8 @@ export class NetworkSwitchService {
145
145
  walletId: currentWallet.id,
146
146
  namespace: network.namespace,
147
147
  chainId: network.id,
148
- targetAlreadyActive
148
+ targetAlreadyActive,
149
+ reused: session.reused
149
150
  },
150
151
  async () => {
151
152
  if (
@@ -14,7 +14,8 @@ const mockInjectedConnector = {
14
14
 
15
15
  const mockWalletConnectConnector = {
16
16
  getAvailableWallets: vi.fn().mockResolvedValue([]),
17
- signSolanaTransactionBytes: vi.fn()
17
+ signSolanaTransactionBytes: vi.fn(),
18
+ beginPairing: vi.fn()
18
19
  }
19
20
 
20
21
  // tonConnect deliberately omits signSolanaTransactionBytes so the router's
@@ -695,4 +696,80 @@ describe('UniversalWalletConnector', () => {
695
696
  ).rejects.toThrow('No active wallet')
696
697
  })
697
698
  })
699
+
700
+ describe('beginPairing (WalletConnect pre-warm)', () => {
701
+ // Wallets exposing a WalletConnect provider, so the union of their networks
702
+ // is non-empty and beginPairing has something to pair.
703
+ const walletsWithWc: WalletMetadata[] = [
704
+ {
705
+ id: 'rainbow',
706
+ name: 'Rainbow',
707
+ metadata: {},
708
+ walletConnectProvider: {
709
+ supportedNetworkIds: ['eip155:1', 'eip155:137'],
710
+ deeplinks: { universal: 'https://rnbwapp.com', native: 'rainbow://' }
711
+ }
712
+ },
713
+ {
714
+ id: 'trust',
715
+ name: 'Trust',
716
+ metadata: {},
717
+ walletConnectProvider: {
718
+ supportedNetworkIds: ['eip155:137', 'eip155:8453'],
719
+ deeplinks: {
720
+ universal: 'https://link.trustwallet.com',
721
+ native: 'trust://'
722
+ }
723
+ }
724
+ }
725
+ ]
726
+
727
+ it('returns undefined when WalletConnect is not configured', async () => {
728
+ const uwc = new UniversalWalletConnector(mockNetworks, walletsWithWc)
729
+ await expect(uwc.beginPairing()).resolves.toBeUndefined()
730
+ expect(mockWalletConnectConnector.beginPairing).not.toHaveBeenCalled()
731
+ })
732
+
733
+ it('returns undefined when no wallet exposes a WalletConnect provider', async () => {
734
+ // WC configured, but mockWallets carries only an injected provider — nothing to pair.
735
+ const uwc = new UniversalWalletConnector(
736
+ mockNetworks,
737
+ mockWallets,
738
+ false,
739
+ mockWalletConnectConfig
740
+ )
741
+ await expect(uwc.beginPairing()).resolves.toBeUndefined()
742
+ expect(mockWalletConnectConnector.beginPairing).not.toHaveBeenCalled()
743
+ })
744
+
745
+ it('delegates to the connector with the deduped union of wallet networks', async () => {
746
+ mockWalletConnectConnector.beginPairing.mockResolvedValueOnce(
747
+ 'wc:union@2'
748
+ )
749
+ const uwc = new UniversalWalletConnector(
750
+ mockNetworks,
751
+ walletsWithWc,
752
+ false,
753
+ mockWalletConnectConfig
754
+ )
755
+ const uri = await uwc.beginPairing()
756
+ expect(uri).toBe('wc:union@2')
757
+ expect(mockWalletConnectConnector.beginPairing).toHaveBeenCalledWith({
758
+ supportedNetworkIds: ['eip155:1', 'eip155:137', 'eip155:8453']
759
+ })
760
+ })
761
+
762
+ it('never throws: resolves undefined if the connector rejects', async () => {
763
+ mockWalletConnectConnector.beginPairing.mockRejectedValueOnce(
764
+ new Error('relay down')
765
+ )
766
+ const uwc = new UniversalWalletConnector(
767
+ mockNetworks,
768
+ walletsWithWc,
769
+ false,
770
+ mockWalletConnectConfig
771
+ )
772
+ await expect(uwc.beginPairing()).resolves.toBeUndefined()
773
+ })
774
+ })
698
775
  })
@@ -334,7 +334,20 @@ export class UniversalWalletConnector {
334
334
  // Relay transport recovered — pair with `relayDisconnected` (same
335
335
  // sdkSessionId) to measure the dead window.
336
336
  onRelayReconnect: () =>
337
- this.eventManager.emit('relayReconnected', undefined)
337
+ this.eventManager.emit('relayReconnected', undefined),
338
+ // Relay/subscription health captured just before a wallet request is
339
+ // published — the CARE-263 delivery signal for a reused session whose
340
+ // transport went stale (correlate with the sign terminal).
341
+ onRelayHealthProbe: probe =>
342
+ this.eventManager.emit('relayHealthProbe', probe),
343
+ // Outbound request handed to the relay, with its JSON-RPC id — the
344
+ // correlation key for wallet-side duplicate-prompt triage.
345
+ onRequestPublished: info =>
346
+ this.eventManager.emit('wcRequestPublished', info),
347
+ // A published request hit its WC TTL unanswered — the terminal
348
+ // counterpart to the probe ("wallet never surfaced the prompt").
349
+ onRequestExpired: info =>
350
+ this.eventManager.emit('wcRequestExpired', info)
338
351
  }
339
352
  )
340
353
  )
@@ -707,6 +720,41 @@ export class UniversalWalletConnector {
707
720
  }
708
721
  }
709
722
 
723
+ /**
724
+ * Pre-generate a WalletConnect pairing ahead of the user's tap. Warms the relay
725
+ * (opens the socket even on a cold, topic-less client) and returns a URI the app
726
+ * can deep-link inside the tap gesture, so the first tap opens the wallet
727
+ * reliably; a subsequent `connect()` adopts the same pairing. Built against the
728
+ * UNION of every WalletConnect wallet's networks so the single pairing serves
729
+ * whichever wallet the user picks (optionalNamespaces let a wallet approve only
730
+ * its supported subset). Best-effort: resolves `undefined` when WalletConnect is
731
+ * not configured or there is nothing to pair, and never throws into the caller.
732
+ */
733
+ async beginPairing(): Promise<string | undefined> {
734
+ try {
735
+ const connector = this.connectors.get('walletConnect')
736
+ if (!connector?.beginPairing) {
737
+ return undefined
738
+ }
739
+
740
+ const supportedNetworkIds = [
741
+ ...new Set(
742
+ this.wallets.flatMap(
743
+ wallet => wallet.walletConnectProvider?.supportedNetworkIds ?? []
744
+ )
745
+ )
746
+ ]
747
+ if (supportedNetworkIds.length === 0) {
748
+ return undefined
749
+ }
750
+
751
+ return await connector.beginPairing({ supportedNetworkIds })
752
+ } catch {
753
+ // Best-effort pre-warm — never surface a pairing failure to the caller.
754
+ return undefined
755
+ }
756
+ }
757
+
710
758
  getConnectionURI(): string | undefined {
711
759
  return this.connectionService.getConnectionURI()
712
760
  }
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated from package.json by scripts/generate-version.mjs — do not edit.
2
- export const UWC_CORE_VERSION = '1.3.0'
2
+ export const UWC_CORE_VERSION = '1.4.0'