@meshconnect/uwc-core 0.7.4 → 0.7.5-snapshot.3e19fe0

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 (50) hide show
  1. package/README.md +924 -0
  2. package/dist/events.d.ts +71 -0
  3. package/dist/events.d.ts.map +1 -0
  4. package/dist/events.js +2 -0
  5. package/dist/events.js.map +1 -0
  6. package/dist/index.d.ts +2 -3
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -3
  9. package/dist/index.js.map +1 -1
  10. package/dist/managers/event-manager.d.ts +22 -3
  11. package/dist/managers/event-manager.d.ts.map +1 -1
  12. package/dist/managers/event-manager.js +63 -7
  13. package/dist/managers/event-manager.js.map +1 -1
  14. package/dist/services/connection-service.d.ts +5 -2
  15. package/dist/services/connection-service.d.ts.map +1 -1
  16. package/dist/services/connection-service.js +19 -7
  17. package/dist/services/connection-service.js.map +1 -1
  18. package/dist/services/network-switch-service.d.ts +2 -1
  19. package/dist/services/network-switch-service.d.ts.map +1 -1
  20. package/dist/services/network-switch-service.js +15 -3
  21. package/dist/services/network-switch-service.js.map +1 -1
  22. package/dist/services/signature-service.d.ts +3 -1
  23. package/dist/services/signature-service.d.ts.map +1 -1
  24. package/dist/services/signature-service.js +10 -5
  25. package/dist/services/signature-service.js.map +1 -1
  26. package/dist/services/transaction-service.d.ts +3 -1
  27. package/dist/services/transaction-service.d.ts.map +1 -1
  28. package/dist/services/transaction-service.js +10 -5
  29. package/dist/services/transaction-service.js.map +1 -1
  30. package/dist/services/wallet-capabilities-service.d.ts +2 -1
  31. package/dist/services/wallet-capabilities-service.d.ts.map +1 -1
  32. package/dist/services/wallet-capabilities-service.js +9 -2
  33. package/dist/services/wallet-capabilities-service.js.map +1 -1
  34. package/dist/universal-wallet-connector.d.ts +46 -6
  35. package/dist/universal-wallet-connector.d.ts.map +1 -1
  36. package/dist/universal-wallet-connector.js +171 -62
  37. package/dist/universal-wallet-connector.js.map +1 -1
  38. package/package.json +5 -5
  39. package/src/events.ts +73 -0
  40. package/src/index.ts +11 -3
  41. package/src/managers/event-manager.test.ts +70 -0
  42. package/src/managers/event-manager.ts +80 -9
  43. package/src/services/connection-service.test.ts +11 -3
  44. package/src/services/connection-service.ts +34 -7
  45. package/src/services/network-switch-service.ts +22 -3
  46. package/src/services/signature-service.ts +13 -5
  47. package/src/services/transaction-service.ts +13 -5
  48. package/src/services/wallet-capabilities-service.ts +14 -2
  49. package/src/universal-wallet-connector.test.ts +87 -3
  50. package/src/universal-wallet-connector.ts +254 -66
@@ -6,6 +6,7 @@ import type {
6
6
  } from '@meshconnect/uwc-types'
7
7
  import type { SessionManager } from '../managers/session-manager'
8
8
  import type { EventManager } from '../managers/event-manager'
9
+ import type { ServiceOptions } from './connection-service'
9
10
 
10
11
  export class WalletCapabilitiesService {
11
12
  constructor(
@@ -17,8 +18,11 @@ export class WalletCapabilitiesService {
17
18
 
18
19
  async getWalletCapabilities(
19
20
  address: string,
20
- activeNetwork?: Network
21
+ activeNetwork?: Network,
22
+ options?: ServiceOptions
21
23
  ): Promise<void> {
24
+ options?.signal?.throwIfAborted()
25
+
22
26
  let activeWalletCapabilities: Record<string, EVMCapabilities> = {}
23
27
  let activeNetworkWalletCapabilities: EVMCapabilities = {}
24
28
 
@@ -49,6 +53,8 @@ export class WalletCapabilitiesService {
49
53
  capabilities = {}
50
54
  }
51
55
 
56
+ options?.signal?.throwIfAborted()
57
+
52
58
  // keep only supported networks
53
59
  activeWalletCapabilities = Object.fromEntries(
54
60
  Object.entries(capabilities).filter(([networkId, _]) =>
@@ -71,7 +77,13 @@ export class WalletCapabilitiesService {
71
77
  activeNetworkWalletCapabilities: null
72
78
  })
73
79
  }
74
- this.eventManager.notify()
80
+
81
+ this.eventManager.emit('capabilitiesUpdated', {
82
+ capabilities: activeWalletCapabilities
83
+ })
84
+ this.eventManager.emit('sessionChanged', {
85
+ session: this.sessionManager.getSession()
86
+ })
75
87
  }
76
88
  }
77
89
  }
@@ -29,7 +29,11 @@ const mockSessionManager = {
29
29
 
30
30
  const mockEventManager = {
31
31
  subscribe: vi.fn().mockReturnValue(() => {}),
32
- notify: vi.fn()
32
+ notify: vi.fn(),
33
+ emit: vi.fn(),
34
+ on: vi.fn().mockReturnValue(() => {}),
35
+ off: vi.fn(),
36
+ once: vi.fn().mockReturnValue(() => {})
33
37
  }
34
38
 
35
39
  const mockConnectionService = {
@@ -117,8 +121,7 @@ describe('UniversalWalletConnector', () => {
117
121
  let mockWalletConnectConfig: WalletConnectConfig
118
122
 
119
123
  beforeEach(() => {
120
- ;(UniversalWalletConnector as any).instanceCount = 0
121
- ;(UniversalWalletConnector as any).hasWarnedAboutMultipleInstances = false
124
+ UniversalWalletConnector.resetInstance()
122
125
 
123
126
  mockNetworks = [
124
127
  {
@@ -402,6 +405,87 @@ describe('UniversalWalletConnector', () => {
402
405
  expect(typeof connector.sendTransaction).toBe('function')
403
406
  expect(typeof connector.isConnectionModeAvailable).toBe('function')
404
407
  expect(typeof connector.getActiveWalletCapabilities).toBe('function')
408
+ expect(typeof connector.on).toBe('function')
409
+ expect(typeof connector.once).toBe('function')
410
+ expect(typeof connector.off).toBe('function')
411
+ })
412
+ })
413
+
414
+ describe('getInstance singleton factory', () => {
415
+ it('creates an instance on first call and reuses it afterwards', () => {
416
+ const a = UniversalWalletConnector.getInstance({
417
+ networks: mockNetworks,
418
+ wallets: mockWallets
419
+ })
420
+ const b = UniversalWalletConnector.getInstance({
421
+ networks: mockNetworks,
422
+ wallets: mockWallets
423
+ })
424
+ expect(a).toBe(b)
425
+ })
426
+
427
+ it('throws when called before any instance exists and no config is given', () => {
428
+ expect(() => UniversalWalletConnector.getInstance()).toThrow(
429
+ /getInstance.*before an instance existed/
430
+ )
431
+ })
432
+
433
+ it('resetInstance() clears the shared reference', () => {
434
+ const first = UniversalWalletConnector.getInstance({
435
+ networks: mockNetworks,
436
+ wallets: mockWallets
437
+ })
438
+ UniversalWalletConnector.resetInstance()
439
+ const second = UniversalWalletConnector.getInstance({
440
+ networks: mockNetworks,
441
+ wallets: mockWallets
442
+ })
443
+ expect(second).not.toBe(first)
444
+ })
445
+
446
+ it('adopts a `new`-constructed instance as the singleton', () => {
447
+ const created = new UniversalWalletConnector(mockNetworks, mockWallets)
448
+ expect(UniversalWalletConnector.getInstance()).toBe(created)
449
+ })
450
+ })
451
+
452
+ describe('config-object constructor', () => {
453
+ it('accepts the { networks, wallets, ... } form', () => {
454
+ const connector = new UniversalWalletConnector({
455
+ networks: mockNetworks,
456
+ wallets: mockWallets,
457
+ walletConnectConfig: mockWalletConnectConfig
458
+ })
459
+ expect(connector).toBeInstanceOf(UniversalWalletConnector)
460
+ })
461
+ })
462
+
463
+ describe('AbortSignal support', () => {
464
+ it('connect rejects immediately when signal is already aborted', async () => {
465
+ const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
466
+ const controller = new AbortController()
467
+ controller.abort()
468
+
469
+ // ConnectionService.connect is mocked to return undefined by default,
470
+ // so the abort check is what surfaces the error.
471
+ mockConnectionService.connect = vi.fn().mockImplementation(
472
+ async (
473
+ _m: unknown,
474
+ _w: unknown,
475
+ _n: unknown,
476
+ options?: {
477
+ signal?: AbortSignal
478
+ }
479
+ ) => {
480
+ options?.signal?.throwIfAborted()
481
+ }
482
+ )
483
+
484
+ await expect(
485
+ connector.connect('injected', 'metamask', undefined, {
486
+ signal: controller.signal
487
+ })
488
+ ).rejects.toThrow()
405
489
  })
406
490
  })
407
491
 
@@ -16,8 +16,10 @@ import type {
16
16
  TransactionResult,
17
17
  NetworkRpcMap,
18
18
  EVMCapabilities,
19
- SignatureType
19
+ SignatureType,
20
+ WalletError
20
21
  } from '@meshconnect/uwc-types'
22
+ import { WalletConnectorError } from '@meshconnect/uwc-types'
21
23
  import { InjectedConnector } from '@meshconnect/uwc-injected-connector'
22
24
  import { WalletConnectConnector } from '@meshconnect/uwc-wallet-connect-connector'
23
25
  import { TonConnectConnector } from '@meshconnect/uwc-ton-connector'
@@ -29,8 +31,28 @@ import { SignatureService } from './services/signature-service'
29
31
  import { TransactionService } from './services/transaction-service'
30
32
  import { WalletCapabilitiesService } from './services/wallet-capabilities-service'
31
33
  import { createNetworkRpcMap } from './utils/network-rpc-utils'
34
+ import type { UWCEventName, UWCEventListener, UWCOperation } from './events'
35
+
36
+ /**
37
+ * Configuration object form of the UniversalWalletConnector constructor.
38
+ * Prefer this over the positional constructor in new code.
39
+ */
40
+ export interface UWCConfig {
41
+ networks: Network[]
42
+ wallets?: WalletMetadata[]
43
+ usingIntegratedBrowser?: boolean
44
+ walletConnectConfig?: WalletConnectConfig
45
+ tonConnectConfig?: TonConnectConfig
46
+ }
47
+
48
+ /** Common options for user-initiated async operations. */
49
+ export interface OperationOptions {
50
+ /** Abort the operation if the signal fires. State mutations are skipped after abort. */
51
+ signal?: AbortSignal
52
+ }
32
53
 
33
54
  export class UniversalWalletConnector {
55
+ private static instance: UniversalWalletConnector | null = null
34
56
  private static instanceCount = 0
35
57
  private static hasWarnedAboutMultipleInstances = false
36
58
 
@@ -48,17 +70,78 @@ export class UniversalWalletConnector {
48
70
  private detectionComplete = false
49
71
  private networkRpcMap: NetworkRpcMap
50
72
 
73
+ /**
74
+ * Recommended entry point: returns the shared instance, creating it on the
75
+ * first call. Subsequent calls ignore the `config` argument — use
76
+ * `resetInstance()` first if you need to reinitialise (e.g. in tests).
77
+ */
78
+ static getInstance(config?: UWCConfig): UniversalWalletConnector {
79
+ if (UniversalWalletConnector.instance) {
80
+ return UniversalWalletConnector.instance
81
+ }
82
+ if (!config) {
83
+ throw new Error(
84
+ 'UniversalWalletConnector.getInstance() was called before an instance existed. Pass a config on the first call.'
85
+ )
86
+ }
87
+ return new UniversalWalletConnector(config)
88
+ }
89
+
90
+ /** Clear the shared instance reference. Primarily for tests and full-logout flows. */
91
+ static resetInstance(): void {
92
+ UniversalWalletConnector.instance = null
93
+ UniversalWalletConnector.instanceCount = 0
94
+ UniversalWalletConnector.hasWarnedAboutMultipleInstances = false
95
+ }
96
+
97
+ constructor(config: UWCConfig)
51
98
  constructor(
52
99
  networks: Network[],
100
+ wallets?: WalletMetadata[],
101
+ usingIntegratedBrowser?: boolean,
102
+ walletConnectConfig?: WalletConnectConfig,
103
+ tonConnectConfig?: TonConnectConfig
104
+ )
105
+ constructor(
106
+ networksOrConfig: Network[] | UWCConfig,
53
107
  wallets: WalletMetadata[] = [],
54
108
  usingIntegratedBrowser = false,
55
109
  walletConnectConfig?: WalletConnectConfig,
56
110
  tonConnectConfig?: TonConnectConfig
57
111
  ) {
58
- // Track instance creation
59
- UniversalWalletConnector.instanceCount++
112
+ if (!networksOrConfig) {
113
+ throw new Error(
114
+ 'Universal Wallet Connector must be initialized with at least one network'
115
+ )
116
+ }
117
+
118
+ let config: UWCConfig
119
+ if (Array.isArray(networksOrConfig)) {
120
+ config = {
121
+ networks: networksOrConfig,
122
+ wallets,
123
+ usingIntegratedBrowser
124
+ }
125
+ if (walletConnectConfig) {
126
+ config.walletConnectConfig = walletConnectConfig
127
+ }
128
+ if (tonConnectConfig) {
129
+ config.tonConnectConfig = tonConnectConfig
130
+ }
131
+ } else {
132
+ config = networksOrConfig
133
+ }
134
+
135
+ const {
136
+ networks,
137
+ wallets: configuredWallets = [],
138
+ usingIntegratedBrowser: configuredUsingIntegratedBrowser = false,
139
+ walletConnectConfig: configuredWalletConnectConfig,
140
+ tonConnectConfig: configuredTonConnectConfig
141
+ } = config
60
142
 
61
- // Warn about multiple instances
143
+ // Track instance creation and warn on duplicates
144
+ UniversalWalletConnector.instanceCount++
62
145
  if (
63
146
  UniversalWalletConnector.instanceCount > 1 &&
64
147
  !UniversalWalletConnector.hasWarnedAboutMultipleInstances
@@ -68,7 +151,7 @@ export class UniversalWalletConnector {
68
151
  `⚠️ WARNING: Multiple instances of UniversalWalletConnector detected (${UniversalWalletConnector.instanceCount} instances). ` +
69
152
  'This can lead to state inconsistencies and unexpected behavior. ' +
70
153
  'Please ensure you create only one instance and reuse it throughout your application. ' +
71
- 'Consider using a singleton pattern or a context provider. ' +
154
+ 'Use UniversalWalletConnector.getInstance(config) instead of `new` to guarantee a single instance. ' +
72
155
  'You can ignore this warning if you are using React Strict Mode, which intentionally mounts components twice in development to help identify side effects.'
73
156
  )
74
157
  UniversalWalletConnector.hasWarnedAboutMultipleInstances = true
@@ -79,14 +162,14 @@ export class UniversalWalletConnector {
79
162
  'Universal Wallet Connector must be initialized with at least one network'
80
163
  )
81
164
  }
82
- if (!wallets) {
165
+ if (!configuredWallets) {
83
166
  throw new Error(
84
167
  'Universal Wallet Connector must be initialized with at least one wallet'
85
168
  )
86
169
  }
87
170
 
88
- this.wallets = wallets
89
- this.usingIntegratedBrowser = usingIntegratedBrowser
171
+ this.wallets = configuredWallets
172
+ this.usingIntegratedBrowser = configuredUsingIntegratedBrowser
90
173
 
91
174
  // Create RPC map from networks
92
175
  this.networkRpcMap = createNetworkRpcMap(networks)
@@ -96,22 +179,25 @@ export class UniversalWalletConnector {
96
179
  this.injectedConnector = new InjectedConnector(
97
180
  this.networkRpcMap,
98
181
  this.wallets,
99
- tonConnectConfig
182
+ configuredTonConnectConfig
100
183
  )
101
184
  this.connectors.set('injected', this.injectedConnector)
102
185
 
103
- if (tonConnectConfig) {
186
+ if (configuredTonConnectConfig) {
104
187
  this.connectors.set(
105
188
  'tonConnect',
106
- new TonConnectConnector(tonConnectConfig)
189
+ new TonConnectConnector(configuredTonConnectConfig)
107
190
  )
108
191
  }
109
192
 
110
193
  // Only add WalletConnect connector if config is provided
111
- if (walletConnectConfig) {
194
+ if (configuredWalletConnectConfig) {
112
195
  this.connectors.set(
113
196
  'walletConnect',
114
- new WalletConnectConnector(walletConnectConfig, this.networkRpcMap)
197
+ new WalletConnectConnector(
198
+ configuredWalletConnectConfig,
199
+ this.networkRpcMap
200
+ )
115
201
  )
116
202
  }
117
203
 
@@ -122,14 +208,14 @@ export class UniversalWalletConnector {
122
208
  this.wallets,
123
209
  this.sessionManager,
124
210
  this.connectors,
125
- usingIntegratedBrowser,
211
+ configuredUsingIntegratedBrowser,
126
212
  this.eventManager
127
213
  )
128
214
  this.networkSwitchService = new NetworkSwitchService(
129
215
  networks,
130
216
  this.sessionManager,
131
217
  this.connectors,
132
- usingIntegratedBrowser,
218
+ configuredUsingIntegratedBrowser,
133
219
  this.eventManager
134
220
  )
135
221
  this.signatureService = new SignatureService(
@@ -147,6 +233,12 @@ export class UniversalWalletConnector {
147
233
  this.eventManager
148
234
  )
149
235
 
236
+ // Register as the shared instance so getInstance() works
237
+ // (only set if no instance exists, to avoid overwriting an earlier one)
238
+ if (!UniversalWalletConnector.instance) {
239
+ UniversalWalletConnector.instance = this
240
+ }
241
+
150
242
  // Initialize detected wallets after services are created
151
243
  this.initializeDetectedWallets()
152
244
  }
@@ -323,12 +415,12 @@ export class UniversalWalletConnector {
323
415
  // Mark detection as complete
324
416
  this.detectionComplete = true
325
417
 
326
- // Notify listeners about the update
327
- this.eventManager.notify()
328
- } catch {
329
- // Silently handle error during initialization
418
+ this.eventManager.emit('walletsDetected', { wallets: this.wallets })
419
+ this.eventManager.emit('ready', undefined)
420
+ } catch (error) {
330
421
  this.detectionComplete = true
331
- this.eventManager.notify()
422
+ this.emitError(error, 'initialize')
423
+ this.eventManager.emit('ready', undefined)
332
424
  }
333
425
  }
334
426
 
@@ -340,6 +432,32 @@ export class UniversalWalletConnector {
340
432
  return this.detectionComplete
341
433
  }
342
434
 
435
+ // ---- Event subscription ----
436
+
437
+ /** Subscribe to a typed event. Returns an unsubscribe function. */
438
+ on<K extends UWCEventName>(
439
+ event: K,
440
+ listener: UWCEventListener<K>
441
+ ): () => void {
442
+ return this.eventManager.on(event, listener)
443
+ }
444
+
445
+ /** Subscribe once; the listener is removed after the first dispatch. */
446
+ once<K extends UWCEventName>(
447
+ event: K,
448
+ listener: UWCEventListener<K>
449
+ ): () => void {
450
+ return this.eventManager.once(event, listener)
451
+ }
452
+
453
+ off<K extends UWCEventName>(event: K, listener: UWCEventListener<K>): void {
454
+ this.eventManager.off(event, listener)
455
+ }
456
+
457
+ /**
458
+ * @deprecated Prefer `on(eventName, listener)` for targeted updates.
459
+ * Subscribes to the catch-all `change` event.
460
+ */
343
461
  subscribe(listener: () => void): () => void {
344
462
  return this.eventManager.subscribe(listener)
345
463
  }
@@ -361,24 +479,45 @@ export class UniversalWalletConnector {
361
479
  async connect(
362
480
  connectionMode: ConnectionMode,
363
481
  walletId: string,
364
- networkId?: NetworkId
482
+ networkId?: NetworkId,
483
+ options?: OperationOptions
365
484
  ): Promise<void> {
366
- await this.connectionService.connect(connectionMode, walletId, networkId)
367
- this.eventManager.notify()
485
+ try {
486
+ await this.connectionService.connect(
487
+ connectionMode,
488
+ walletId,
489
+ networkId,
490
+ options
491
+ )
492
+ } catch (error) {
493
+ this.emitError(error, 'connect')
494
+ throw error
495
+ }
368
496
  }
369
497
 
370
498
  getConnectionURI(): string | undefined {
371
499
  return this.connectionService.getConnectionURI()
372
500
  }
373
501
 
374
- async disconnect(): Promise<void> {
375
- await this.connectionService.disconnect()
376
- this.eventManager.notify()
502
+ async disconnect(options?: OperationOptions): Promise<void> {
503
+ try {
504
+ await this.connectionService.disconnect(options)
505
+ } catch (error) {
506
+ this.emitError(error, 'disconnect')
507
+ throw error
508
+ }
377
509
  }
378
510
 
379
- async switchNetwork(networkId: NetworkId): Promise<void> {
380
- await this.networkSwitchService.switchNetwork(networkId)
381
- this.eventManager.notify()
511
+ async switchNetwork(
512
+ networkId: NetworkId,
513
+ options?: OperationOptions
514
+ ): Promise<void> {
515
+ try {
516
+ await this.networkSwitchService.switchNetwork(networkId, options)
517
+ } catch (error) {
518
+ this.emitError(error, 'switchNetwork')
519
+ throw error
520
+ }
382
521
  }
383
522
 
384
523
  getNetworkSwitchLoadingState() {
@@ -388,9 +527,13 @@ export class UniversalWalletConnector {
388
527
  /**
389
528
  * Sign a message with the connected wallet
390
529
  * @param message The message to sign
530
+ * @param options Optional cancellation signal
391
531
  * @returns A promise that resolves to the signature
392
532
  */
393
- async signMessage(message: string): Promise<SignatureType> {
533
+ async signMessage(
534
+ message: string,
535
+ options?: OperationOptions
536
+ ): Promise<SignatureType> {
394
537
  const session = this.sessionManager.getSession()
395
538
 
396
539
  if (!session.activeWallet) {
@@ -401,32 +544,43 @@ export class UniversalWalletConnector {
401
544
  throw new Error('No active connection')
402
545
  }
403
546
 
404
- // Get the provider from the active wallet based on connection mode
405
- let provider
406
- if (session.connectionMode === 'injected') {
407
- provider = this.usingIntegratedBrowser
408
- ? session.activeWallet.integratedBrowserInjectedProvider
409
- : session.activeWallet.extensionInjectedProvider
410
- } else if (session.connectionMode === 'walletConnect') {
411
- provider = session.activeWallet.walletConnectProvider
412
- } else if (session.connectionMode === 'tonConnect') {
413
- return await this.signatureService.signMessage(message)
414
- }
547
+ try {
548
+ // Get the provider from the active wallet based on connection mode
549
+ let provider
550
+ if (session.connectionMode === 'injected') {
551
+ provider = this.usingIntegratedBrowser
552
+ ? session.activeWallet.integratedBrowserInjectedProvider
553
+ : session.activeWallet.extensionInjectedProvider
554
+ } else if (session.connectionMode === 'walletConnect') {
555
+ provider = session.activeWallet.walletConnectProvider
556
+ } else if (session.connectionMode === 'tonConnect') {
557
+ return await this.signatureService.signMessage(
558
+ message,
559
+ undefined,
560
+ options
561
+ )
562
+ }
415
563
 
416
- if (!provider) {
417
- throw new Error('No provider available for the active wallet')
418
- }
564
+ if (!provider) {
565
+ throw new Error('No provider available for the active wallet')
566
+ }
419
567
 
420
- return await this.signatureService.signMessage(message, provider)
568
+ return await this.signatureService.signMessage(message, provider, options)
569
+ } catch (error) {
570
+ this.emitError(error, 'signMessage')
571
+ throw error
572
+ }
421
573
  }
422
574
 
423
575
  /**
424
576
  * Send a transaction with the connected wallet
425
577
  * @param request The transaction request parameters
578
+ * @param options Optional cancellation signal
426
579
  * @returns A promise that resolves to the transaction result
427
580
  */
428
581
  async sendTransaction(
429
- request: TransactionRequest
582
+ request: TransactionRequest,
583
+ options?: OperationOptions
430
584
  ): Promise<TransactionResult> {
431
585
  const session = this.sessionManager.getSession()
432
586
 
@@ -438,39 +592,60 @@ export class UniversalWalletConnector {
438
592
  throw new Error('No active connection')
439
593
  }
440
594
 
441
- // Get the provider from the active wallet based on connection mode
442
- let provider
443
- if (session.connectionMode === 'injected') {
444
- provider = this.usingIntegratedBrowser
445
- ? session.activeWallet.integratedBrowserInjectedProvider
446
- : session.activeWallet.extensionInjectedProvider
447
- } else if (session.connectionMode === 'walletConnect') {
448
- provider = session.activeWallet.walletConnectProvider
449
- } else if (session.connectionMode === 'tonConnect') {
450
- return await this.transactionService.sendTransaction(request)
451
- }
595
+ try {
596
+ // Get the provider from the active wallet based on connection mode
597
+ let provider
598
+ if (session.connectionMode === 'injected') {
599
+ provider = this.usingIntegratedBrowser
600
+ ? session.activeWallet.integratedBrowserInjectedProvider
601
+ : session.activeWallet.extensionInjectedProvider
602
+ } else if (session.connectionMode === 'walletConnect') {
603
+ provider = session.activeWallet.walletConnectProvider
604
+ } else if (session.connectionMode === 'tonConnect') {
605
+ return await this.transactionService.sendTransaction(
606
+ request,
607
+ undefined,
608
+ options
609
+ )
610
+ }
452
611
 
453
- if (!provider) {
454
- throw new Error('No provider available for the active wallet')
455
- }
612
+ if (!provider) {
613
+ throw new Error('No provider available for the active wallet')
614
+ }
456
615
 
457
- return await this.transactionService.sendTransaction(request, provider)
616
+ return await this.transactionService.sendTransaction(
617
+ request,
618
+ provider,
619
+ options
620
+ )
621
+ } catch (error) {
622
+ this.emitError(error, 'sendTransaction')
623
+ throw error
624
+ }
458
625
  }
459
626
 
460
627
  /**
461
628
  * Fetch wallet capabilities for the connected wallet
462
629
  * @param address The wallet address to fetch capabilities for
463
630
  * @param activeNetwork Optional network to set as the active network for capabilities
631
+ * @param options Optional cancellation signal
464
632
  * @returns A promise that resolves when capabilities are fetched and updated
465
633
  */
466
634
  async getWalletCapabilities(
467
635
  address: string,
468
- activeNetwork?: Network
636
+ activeNetwork?: Network,
637
+ options?: OperationOptions
469
638
  ): Promise<void> {
470
- return await this.walletCapabilitiesService.getWalletCapabilities(
471
- address,
472
- activeNetwork
473
- )
639
+ try {
640
+ return await this.walletCapabilitiesService.getWalletCapabilities(
641
+ address,
642
+ activeNetwork,
643
+ options
644
+ )
645
+ } catch (error) {
646
+ this.emitError(error, 'getWalletCapabilities')
647
+ throw error
648
+ }
474
649
  }
475
650
 
476
651
  /**
@@ -532,4 +707,17 @@ export class UniversalWalletConnector {
532
707
  const session = this.sessionManager.getSession()
533
708
  return session.activeWalletCapabilities
534
709
  }
710
+
711
+ // ---- private helpers ----
712
+
713
+ private emitError(error: unknown, operation: UWCOperation): void {
714
+ const walletError: WalletError =
715
+ error instanceof WalletConnectorError
716
+ ? { type: error.type, message: error.message }
717
+ : {
718
+ type: 'unknown',
719
+ message: error instanceof Error ? error.message : String(error)
720
+ }
721
+ this.eventManager.emit('error', { error: walletError, operation })
722
+ }
535
723
  }