@meshconnect/uwc-core 0.10.1 → 1.0.1

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 (56) hide show
  1. package/README.md +928 -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 -4
  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 +36 -9
  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 +56 -8
  35. package/dist/universal-wallet-connector.d.ts.map +1 -1
  36. package/dist/universal-wallet-connector.js +299 -194
  37. package/dist/universal-wallet-connector.js.map +1 -1
  38. package/dist/utils/abort.d.ts +14 -0
  39. package/dist/utils/abort.d.ts.map +1 -0
  40. package/dist/utils/abort.js +31 -0
  41. package/dist/utils/abort.js.map +1 -0
  42. package/package.json +3 -3
  43. package/src/events.ts +73 -0
  44. package/src/index.ts +11 -8
  45. package/src/managers/event-manager.test.ts +70 -0
  46. package/src/managers/event-manager.ts +80 -9
  47. package/src/services/connection-service.test.ts +148 -19
  48. package/src/services/connection-service.ts +63 -12
  49. package/src/services/network-switch-service.ts +22 -3
  50. package/src/services/signature-service.ts +13 -5
  51. package/src/services/transaction-service.ts +13 -5
  52. package/src/services/wallet-capabilities-service.ts +14 -2
  53. package/src/universal-wallet-connector.test.ts +196 -3
  54. package/src/universal-wallet-connector.ts +430 -222
  55. package/src/utils/abort.test.ts +68 -0
  56. package/src/utils/abort.ts +38 -0
@@ -1,3 +1,4 @@
1
+ import { WalletConnectorError } from '@meshconnect/uwc-types';
1
2
  import { InjectedConnector } from '@meshconnect/uwc-injected-connector';
2
3
  import { WalletConnectConnector } from '@meshconnect/uwc-wallet-connect-connector';
3
4
  import { TonConnectConnector } from '@meshconnect/uwc-ton-connector';
@@ -10,6 +11,7 @@ import { TransactionService } from './services/transaction-service';
10
11
  import { WalletCapabilitiesService } from './services/wallet-capabilities-service';
11
12
  import { createNetworkRpcMap } from './utils/network-rpc-utils';
12
13
  export class UniversalWalletConnector {
14
+ static instance = null;
13
15
  static instanceCount = 0;
14
16
  static hasWarnedAboutMultipleInstances = false;
15
17
  sessionManager;
@@ -25,48 +27,100 @@ export class UniversalWalletConnector {
25
27
  usingIntegratedBrowser;
26
28
  detectionComplete = false;
27
29
  networkRpcMap;
28
- constructor(networks, wallets = [], usingIntegratedBrowser = false, walletConnectConfig, tonConnectConfig, tronConnectorConfig) {
29
- // Track instance creation
30
+ /**
31
+ * Recommended entry point: returns the shared instance, creating it on the
32
+ * first call. Subsequent calls ignore the `config` argument — use
33
+ * `resetInstance()` first if you need to reinitialise (e.g. in tests).
34
+ */
35
+ static getInstance(config) {
36
+ if (UniversalWalletConnector.instance) {
37
+ return UniversalWalletConnector.instance;
38
+ }
39
+ if (!config) {
40
+ throw new Error('UniversalWalletConnector.getInstance() was called before an instance existed. Pass a config on the first call.');
41
+ }
42
+ return new UniversalWalletConnector(config);
43
+ }
44
+ /** Clear the shared instance reference. Primarily for tests and full-logout flows. */
45
+ static resetInstance() {
46
+ UniversalWalletConnector.instance = null;
47
+ UniversalWalletConnector.instanceCount = 0;
48
+ UniversalWalletConnector.hasWarnedAboutMultipleInstances = false;
49
+ }
50
+ constructor(networksOrConfig, wallets = [], usingIntegratedBrowser = false, walletConnectConfig, tonConnectConfig, tronConnectorConfig) {
51
+ if (!networksOrConfig) {
52
+ throw new Error('Universal Wallet Connector must be initialized with at least one network');
53
+ }
54
+ let config;
55
+ if (Array.isArray(networksOrConfig)) {
56
+ config = {
57
+ networks: networksOrConfig,
58
+ wallets,
59
+ usingIntegratedBrowser
60
+ };
61
+ if (walletConnectConfig) {
62
+ config.walletConnectConfig = walletConnectConfig;
63
+ }
64
+ if (tonConnectConfig) {
65
+ config.tonConnectConfig = tonConnectConfig;
66
+ }
67
+ if (tronConnectorConfig) {
68
+ config.tronConnectorConfig = tronConnectorConfig;
69
+ }
70
+ }
71
+ else {
72
+ config = networksOrConfig;
73
+ }
74
+ const { networks, wallets: configuredWallets = [], usingIntegratedBrowser: configuredUsingIntegratedBrowser = false, walletConnectConfig: configuredWalletConnectConfig, tonConnectConfig: configuredTonConnectConfig, tronConnectorConfig: configuredTronConnectorConfig } = config;
75
+ // Track instance creation and warn on duplicates
30
76
  UniversalWalletConnector.instanceCount++;
31
- // Warn about multiple instances
32
77
  if (UniversalWalletConnector.instanceCount > 1 &&
33
78
  !UniversalWalletConnector.hasWarnedAboutMultipleInstances) {
34
79
  // eslint-disable-next-line no-console
35
80
  console.error(`⚠️ WARNING: Multiple instances of UniversalWalletConnector detected (${UniversalWalletConnector.instanceCount} instances). ` +
36
81
  'This can lead to state inconsistencies and unexpected behavior. ' +
37
82
  'Please ensure you create only one instance and reuse it throughout your application. ' +
38
- 'Consider using a singleton pattern or a context provider. ' +
83
+ 'Use UniversalWalletConnector.getInstance(config) instead of `new` to guarantee a single instance. ' +
39
84
  'You can ignore this warning if you are using React Strict Mode, which intentionally mounts components twice in development to help identify side effects.');
40
85
  UniversalWalletConnector.hasWarnedAboutMultipleInstances = true;
41
86
  }
42
- if (!networks) {
87
+ // Reject empty arrays as well as null/undefined to honour the error
88
+ // messages below. The companion type `UWCConfig.wallets` is required, so
89
+ // TypeScript callers get a compile error for the missing-wallets case;
90
+ // this runtime guard catches the empty-array case the type can't express.
91
+ if (!networks || networks.length === 0) {
43
92
  throw new Error('Universal Wallet Connector must be initialized with at least one network');
44
93
  }
45
- if (!wallets) {
94
+ if (!configuredWallets || configuredWallets.length === 0) {
46
95
  throw new Error('Universal Wallet Connector must be initialized with at least one wallet');
47
96
  }
48
- this.wallets = wallets;
49
- this.usingIntegratedBrowser = usingIntegratedBrowser;
97
+ this.wallets = configuredWallets;
98
+ this.usingIntegratedBrowser = configuredUsingIntegratedBrowser;
50
99
  // Create RPC map from networks
51
100
  this.networkRpcMap = createNetworkRpcMap(networks);
52
101
  // Initialize connectors with RPC map
53
102
  this.connectors = new Map();
54
- this.injectedConnector = new InjectedConnector(this.networkRpcMap, this.wallets, tonConnectConfig, tronConnectorConfig, networks);
103
+ this.injectedConnector = new InjectedConnector(this.networkRpcMap, this.wallets, configuredTonConnectConfig, configuredTronConnectorConfig, networks);
55
104
  this.connectors.set('injected', this.injectedConnector);
56
- if (tonConnectConfig) {
57
- this.connectors.set('tonConnect', new TonConnectConnector(tonConnectConfig));
105
+ if (configuredTonConnectConfig) {
106
+ this.connectors.set('tonConnect', new TonConnectConnector(configuredTonConnectConfig));
58
107
  }
59
108
  // Only add WalletConnect connector if config is provided
60
- if (walletConnectConfig) {
61
- this.connectors.set('walletConnect', new WalletConnectConnector(walletConnectConfig, this.networkRpcMap));
109
+ if (configuredWalletConnectConfig) {
110
+ this.connectors.set('walletConnect', new WalletConnectConnector(configuredWalletConnectConfig, this.networkRpcMap));
62
111
  }
63
112
  this.sessionManager = new SessionManager();
64
113
  this.eventManager = new EventManager();
65
- this.connectionService = new ConnectionService(networks, this.wallets, this.sessionManager, this.connectors, usingIntegratedBrowser, this.eventManager);
66
- this.networkSwitchService = new NetworkSwitchService(networks, this.sessionManager, this.connectors, usingIntegratedBrowser, this.eventManager);
114
+ this.connectionService = new ConnectionService(networks, this.wallets, this.sessionManager, this.connectors, configuredUsingIntegratedBrowser, this.eventManager);
115
+ this.networkSwitchService = new NetworkSwitchService(networks, this.sessionManager, this.connectors, configuredUsingIntegratedBrowser, this.eventManager);
67
116
  this.signatureService = new SignatureService(this.sessionManager, this.connectors);
68
117
  this.transactionService = new TransactionService(this.sessionManager, this.connectors);
69
118
  this.walletCapabilitiesService = new WalletCapabilitiesService(networks, this.connectors, this.sessionManager, this.eventManager);
119
+ // Register as the shared instance so getInstance() works
120
+ // (only set if no instance exists, to avoid overwriting an earlier one)
121
+ if (!UniversalWalletConnector.instance) {
122
+ UniversalWalletConnector.instance = this;
123
+ }
70
124
  // Initialize detected wallets after services are created
71
125
  this.initializeDetectedWallets();
72
126
  }
@@ -87,153 +141,136 @@ export class UniversalWalletConnector {
87
141
  });
88
142
  }
89
143
  });
90
- // Check available wallets for each namespace across all connectors
144
+ // Run every namespace × connector detection concurrently.
145
+ // The namespace loop used to be sequential and `await`-ed each call,
146
+ // which meant a 100ms EIP-6963 native timeout + any bridge polling
147
+ // added up. With Promise.all the total time is the slowest single
148
+ // call, not the sum.
149
+ const detectionTasks = [];
91
150
  for (const namespace of namespaces) {
92
151
  for (const [, connector] of this.connectors) {
93
- // Check if connector supports getAvailableWallets
94
- if (typeof connector.getAvailableWallets === 'function') {
95
- try {
96
- const detectedWallets = await connector.getAvailableWallets(namespace, this.wallets);
97
- // Update wallets with detected information
98
- detectedWallets.forEach(detected => {
99
- // Find matching wallet based on namespace-specific naming
100
- let wallet;
101
- if (namespace === 'eip155') {
102
- // For EIP155, match by eip155Name
103
- wallet = this.wallets.find(w => {
104
- const provider = this.usingIntegratedBrowser
105
- ? w.integratedBrowserInjectedProvider
106
- : w.extensionInjectedProvider;
107
- return (provider?.namespaceMetaData?.eip155?.eip155Name?.toLowerCase() ===
108
- detected.name.toLowerCase());
109
- });
110
- }
111
- else if (namespace === 'solana') {
112
- // For Solana, match by walletStandardName
113
- wallet = this.wallets.find(w => {
114
- const provider = this.usingIntegratedBrowser
115
- ? w.integratedBrowserInjectedProvider
116
- : w.extensionInjectedProvider;
117
- return (provider?.namespaceMetaData?.solana?.walletStandardName?.toLowerCase() ===
118
- detected.name.toLowerCase());
119
- });
120
- }
121
- else if (namespace === 'tron') {
122
- // Two id schemes can appear here:
123
- // - Mesh-owned TronConnector: uuid === WalletMetadata.id
124
- // (a stable TronWalletId like 'tronlink').
125
- // - Legacy window-global discovery: uuid === `tron-${injectedId}`
126
- // (e.g. 'tron-tronlink'), derived from the injection path.
127
- const tronDetected = detected;
128
- wallet = this.wallets.find(w => {
129
- if (tronDetected.uuid === w.id)
130
- return true;
131
- const provider = this.usingIntegratedBrowser
132
- ? w.integratedBrowserInjectedProvider
133
- : w.extensionInjectedProvider;
134
- const injectedId = provider?.namespaceMetaData?.tron?.injectedId;
135
- if (!injectedId)
136
- return false;
137
- // Mesh-owned connector reports the canonical injectedId —
138
- // match it directly so catalog-driven wallets (whose `id`
139
- // is a backend GUID, not a TronWalletId) are recognized.
140
- if (tronDetected.injectedId &&
141
- injectedId.toLowerCase() ===
142
- tronDetected.injectedId.toLowerCase()) {
143
- return true;
144
- }
145
- // Legacy window-global discovery scheme: `tron-${injectedId}`.
146
- return (tronDetected.uuid ===
147
- `tron-${injectedId}`.toLowerCase().replace(/\./g, '-'));
148
- });
149
- }
150
- else if (namespace === 'tvm') {
151
- // For TON, match by jsBridgeKey
152
- const tonDetected = detected;
153
- wallet = this.wallets.find(w => {
154
- const provider = this.usingIntegratedBrowser
155
- ? w.integratedBrowserInjectedProvider
156
- : w.extensionInjectedProvider;
157
- return (provider?.namespaceMetaData?.tvm?.jsBridgeKey ===
158
- tonDetected.jsBridgeKey);
159
- });
160
- }
161
- else {
162
- // For other namespaces, fall back to wallet name
163
- wallet = this.wallets.find(w => w.name.toLowerCase() === detected.name.toLowerCase());
164
- }
165
- if (wallet) {
166
- // Determine which provider to update based on usingIntegratedBrowser
167
- const provider = this.usingIntegratedBrowser
168
- ? wallet.integratedBrowserInjectedProvider
169
- : wallet.extensionInjectedProvider;
170
- // Update the appropriate namespace metadata
171
- if (provider?.namespaceMetaData) {
172
- const nsMetadata = provider.namespaceMetaData[namespace];
173
- if (nsMetadata && namespace === 'eip155') {
174
- // For EIP155, we have specific fields
175
- const eip155Metadata = nsMetadata;
176
- eip155Metadata.installed = true;
177
- const eip155Detected = detected;
178
- eip155Metadata.detectedWallet = {
179
- uuid: eip155Detected.uuid,
180
- name: eip155Detected.name,
181
- icon: eip155Detected.icon,
182
- rdns: eip155Detected.rdns
183
- };
184
- }
185
- else if (namespace === 'solana') {
186
- // For Solana, we have different fields
187
- const solanaMetadata = nsMetadata;
188
- solanaMetadata.installed = true;
189
- const solanaDetected = detected;
190
- solanaMetadata.detectedWallet = {
191
- uuid: solanaDetected.uuid,
192
- name: solanaDetected.name,
193
- features: solanaDetected.features
194
- };
195
- }
196
- else if (namespace === 'tron') {
197
- const tronMetadata = nsMetadata;
198
- tronMetadata.installed = true;
199
- const tronDetected = detected;
200
- tronMetadata.detectedWallet = {
201
- uuid: tronDetected.uuid,
202
- name: tronDetected.name
203
- };
204
- }
205
- else if (namespace === 'tvm') {
206
- const tonMetadata = nsMetadata;
207
- tonMetadata.installed = true;
208
- const tonDetected = detected;
209
- tonMetadata.detectedWallet = {
210
- uuid: tonDetected.uuid,
211
- name: tonDetected.name,
212
- icon: tonDetected.icon,
213
- jsBridgeKey: tonDetected.jsBridgeKey
214
- };
215
- }
216
- }
217
- }
218
- });
219
- }
220
- catch {
221
- // Silently handle errors for individual connector/namespace combinations
152
+ if (typeof connector.getAvailableWallets !== 'function')
153
+ continue;
154
+ detectionTasks.push(Promise.resolve()
155
+ .then(() => connector.getAvailableWallets(namespace, this.wallets))
156
+ .then(detected => {
157
+ for (const d of detected) {
158
+ this.applyDetectedWallet(namespace, d);
222
159
  }
223
- }
160
+ })
161
+ .catch(() => {
162
+ // Silently handle errors for individual connector/namespace combinations
163
+ }));
224
164
  }
225
165
  }
166
+ await Promise.all(detectionTasks);
226
167
  // Notify the connection service to update its internal wallets reference
227
168
  this.connectionService.updateWallets(this.wallets);
228
169
  // Mark detection as complete
229
170
  this.detectionComplete = true;
230
- // Notify listeners about the update
231
- this.eventManager.notify();
171
+ this.eventManager.emit('walletsDetected', { wallets: this.wallets });
172
+ this.eventManager.emit('ready', undefined);
232
173
  }
233
- catch {
234
- // Silently handle error during initialization
174
+ catch (error) {
235
175
  this.detectionComplete = true;
236
- this.eventManager.notify();
176
+ this.emitError(error, 'initialize');
177
+ this.eventManager.emit('ready', undefined);
178
+ }
179
+ }
180
+ /**
181
+ * Merge a single detected wallet (from one namespace × connector run) into
182
+ * the configured wallet metadata. Factored out so detection tasks can run
183
+ * in parallel without racing on the merge logic.
184
+ */
185
+ applyDetectedWallet(namespace, detected) {
186
+ let wallet;
187
+ const pickProvider = (w) => this.usingIntegratedBrowser
188
+ ? w.integratedBrowserInjectedProvider
189
+ : w.extensionInjectedProvider;
190
+ if (namespace === 'eip155') {
191
+ wallet = this.wallets.find(w => pickProvider(w)?.namespaceMetaData?.eip155?.eip155Name?.toLowerCase() ===
192
+ detected.name.toLowerCase());
193
+ }
194
+ else if (namespace === 'solana') {
195
+ wallet = this.wallets.find(w => pickProvider(w)?.namespaceMetaData?.solana?.walletStandardName?.toLowerCase() ===
196
+ detected.name.toLowerCase());
197
+ }
198
+ else if (namespace === 'tron') {
199
+ // Three id schemes can appear here:
200
+ // - Mesh-owned TronConnector reports the canonical `injectedId` directly
201
+ // (added in #174) — match catalog wallets whose `id` is a backend GUID.
202
+ // - Mesh-owned TronConnector: uuid === WalletMetadata.id
203
+ // (a stable TronWalletId like 'tronlink').
204
+ // - Legacy window-global discovery: uuid === `tron-${injectedId}`
205
+ // (e.g. 'tron-tronlink'), derived from the injection path.
206
+ const tronDetected = detected;
207
+ wallet = this.wallets.find(w => {
208
+ if (tronDetected.uuid === w.id)
209
+ return true;
210
+ const injectedId = pickProvider(w)?.namespaceMetaData?.tron?.injectedId;
211
+ if (!injectedId)
212
+ return false;
213
+ if (tronDetected.injectedId &&
214
+ injectedId.toLowerCase() === tronDetected.injectedId.toLowerCase()) {
215
+ return true;
216
+ }
217
+ return (tronDetected.uuid ===
218
+ `tron-${injectedId}`.toLowerCase().replace(/\./g, '-'));
219
+ });
220
+ }
221
+ else if (namespace === 'tvm') {
222
+ const tonDetected = detected;
223
+ wallet = this.wallets.find(w => pickProvider(w)?.namespaceMetaData?.tvm?.jsBridgeKey ===
224
+ tonDetected.jsBridgeKey);
225
+ }
226
+ else {
227
+ wallet = this.wallets.find(w => w.name.toLowerCase() === detected.name.toLowerCase());
228
+ }
229
+ if (!wallet)
230
+ return;
231
+ const provider = pickProvider(wallet);
232
+ if (!provider?.namespaceMetaData)
233
+ return;
234
+ const nsMetadata = provider.namespaceMetaData[namespace];
235
+ if (!nsMetadata)
236
+ return;
237
+ if (namespace === 'eip155') {
238
+ const m = nsMetadata;
239
+ const d = detected;
240
+ m.installed = true;
241
+ m.detectedWallet = {
242
+ uuid: d.uuid,
243
+ name: d.name,
244
+ icon: d.icon,
245
+ rdns: d.rdns
246
+ };
247
+ }
248
+ else if (namespace === 'solana') {
249
+ const m = nsMetadata;
250
+ const d = detected;
251
+ m.installed = true;
252
+ m.detectedWallet = {
253
+ uuid: d.uuid,
254
+ name: d.name,
255
+ features: d.features
256
+ };
257
+ }
258
+ else if (namespace === 'tron') {
259
+ const m = nsMetadata;
260
+ const d = detected;
261
+ m.installed = true;
262
+ m.detectedWallet = { uuid: d.uuid, name: d.name };
263
+ }
264
+ else if (namespace === 'tvm') {
265
+ const m = nsMetadata;
266
+ const d = detected;
267
+ m.installed = true;
268
+ m.detectedWallet = {
269
+ uuid: d.uuid,
270
+ name: d.name,
271
+ icon: d.icon,
272
+ jsBridgeKey: d.jsBridgeKey
273
+ };
237
274
  }
238
275
  }
239
276
  getSession() {
@@ -242,6 +279,22 @@ export class UniversalWalletConnector {
242
279
  isReady() {
243
280
  return this.detectionComplete;
244
281
  }
282
+ // ---- Event subscription ----
283
+ /** Subscribe to a typed event. Returns an unsubscribe function. */
284
+ on(event, listener) {
285
+ return this.eventManager.on(event, listener);
286
+ }
287
+ /** Subscribe once; the listener is removed after the first dispatch. */
288
+ once(event, listener) {
289
+ return this.eventManager.once(event, listener);
290
+ }
291
+ off(event, listener) {
292
+ this.eventManager.off(event, listener);
293
+ }
294
+ /**
295
+ * @deprecated Prefer `on(eventName, listener)` for targeted updates.
296
+ * Subscribes to the catch-all `change` event.
297
+ */
245
298
  subscribe(listener) {
246
299
  return this.eventManager.subscribe(listener);
247
300
  }
@@ -256,20 +309,35 @@ export class UniversalWalletConnector {
256
309
  getNetworks() {
257
310
  return this.connectionService.getNetworks();
258
311
  }
259
- async connect(connectionMode, walletId, networkId) {
260
- await this.connectionService.connect(connectionMode, walletId, networkId);
261
- this.eventManager.notify();
312
+ async connect(connectionMode, walletId, networkId, options) {
313
+ try {
314
+ await this.connectionService.connect(connectionMode, walletId, networkId, options);
315
+ }
316
+ catch (error) {
317
+ this.emitError(error, 'connect');
318
+ throw error;
319
+ }
262
320
  }
263
321
  getConnectionURI() {
264
322
  return this.connectionService.getConnectionURI();
265
323
  }
266
- async disconnect() {
267
- await this.connectionService.disconnect();
268
- this.eventManager.notify();
324
+ async disconnect(options) {
325
+ try {
326
+ await this.connectionService.disconnect(options);
327
+ }
328
+ catch (error) {
329
+ this.emitError(error, 'disconnect');
330
+ throw error;
331
+ }
269
332
  }
270
- async switchNetwork(networkId) {
271
- await this.networkSwitchService.switchNetwork(networkId);
272
- this.eventManager.notify();
333
+ async switchNetwork(networkId, options) {
334
+ try {
335
+ await this.networkSwitchService.switchNetwork(networkId, options);
336
+ }
337
+ catch (error) {
338
+ this.emitError(error, 'switchNetwork');
339
+ throw error;
340
+ }
273
341
  }
274
342
  getNetworkSwitchLoadingState() {
275
343
  return this.networkSwitchService.getLoadingState();
@@ -277,9 +345,10 @@ export class UniversalWalletConnector {
277
345
  /**
278
346
  * Sign a message with the connected wallet
279
347
  * @param message The message to sign
348
+ * @param options Optional cancellation signal
280
349
  * @returns A promise that resolves to the signature
281
350
  */
282
- async signMessage(message) {
351
+ async signMessage(message, options) {
283
352
  const session = this.sessionManager.getSession();
284
353
  if (!session.activeWallet) {
285
354
  throw new Error('No active wallet');
@@ -287,23 +356,29 @@ export class UniversalWalletConnector {
287
356
  if (!session.connectionMode) {
288
357
  throw new Error('No active connection');
289
358
  }
290
- // Get the provider from the active wallet based on connection mode
291
- let provider;
292
- if (session.connectionMode === 'injected') {
293
- provider = this.usingIntegratedBrowser
294
- ? session.activeWallet.integratedBrowserInjectedProvider
295
- : session.activeWallet.extensionInjectedProvider;
296
- }
297
- else if (session.connectionMode === 'walletConnect') {
298
- provider = session.activeWallet.walletConnectProvider;
299
- }
300
- else if (session.connectionMode === 'tonConnect') {
301
- return await this.signatureService.signMessage(message);
359
+ try {
360
+ // Get the provider from the active wallet based on connection mode
361
+ let provider;
362
+ if (session.connectionMode === 'injected') {
363
+ provider = this.usingIntegratedBrowser
364
+ ? session.activeWallet.integratedBrowserInjectedProvider
365
+ : session.activeWallet.extensionInjectedProvider;
366
+ }
367
+ else if (session.connectionMode === 'walletConnect') {
368
+ provider = session.activeWallet.walletConnectProvider;
369
+ }
370
+ else if (session.connectionMode === 'tonConnect') {
371
+ return await this.signatureService.signMessage(message, undefined, options);
372
+ }
373
+ if (!provider) {
374
+ throw new Error('No provider available for the active wallet');
375
+ }
376
+ return await this.signatureService.signMessage(message, provider, options);
302
377
  }
303
- if (!provider) {
304
- throw new Error('No provider available for the active wallet');
378
+ catch (error) {
379
+ this.emitError(error, 'signMessage');
380
+ throw error;
305
381
  }
306
- return await this.signatureService.signMessage(message, provider);
307
382
  }
308
383
  /**
309
384
  * Sign EIP-712 typed structured data (eth_signTypedData_v4).
@@ -340,9 +415,10 @@ export class UniversalWalletConnector {
340
415
  /**
341
416
  * Send a transaction with the connected wallet
342
417
  * @param request The transaction request parameters
418
+ * @param options Optional cancellation signal
343
419
  * @returns A promise that resolves to the transaction result
344
420
  */
345
- async sendTransaction(request) {
421
+ async sendTransaction(request, options) {
346
422
  const session = this.sessionManager.getSession();
347
423
  if (!session.activeWallet) {
348
424
  throw new Error('No active wallet');
@@ -350,23 +426,29 @@ export class UniversalWalletConnector {
350
426
  if (!session.connectionMode) {
351
427
  throw new Error('No active connection');
352
428
  }
353
- // Get the provider from the active wallet based on connection mode
354
- let provider;
355
- if (session.connectionMode === 'injected') {
356
- provider = this.usingIntegratedBrowser
357
- ? session.activeWallet.integratedBrowserInjectedProvider
358
- : session.activeWallet.extensionInjectedProvider;
359
- }
360
- else if (session.connectionMode === 'walletConnect') {
361
- provider = session.activeWallet.walletConnectProvider;
362
- }
363
- else if (session.connectionMode === 'tonConnect') {
364
- return await this.transactionService.sendTransaction(request);
429
+ try {
430
+ // Get the provider from the active wallet based on connection mode
431
+ let provider;
432
+ if (session.connectionMode === 'injected') {
433
+ provider = this.usingIntegratedBrowser
434
+ ? session.activeWallet.integratedBrowserInjectedProvider
435
+ : session.activeWallet.extensionInjectedProvider;
436
+ }
437
+ else if (session.connectionMode === 'walletConnect') {
438
+ provider = session.activeWallet.walletConnectProvider;
439
+ }
440
+ else if (session.connectionMode === 'tonConnect') {
441
+ return await this.transactionService.sendTransaction(request, undefined, options);
442
+ }
443
+ if (!provider) {
444
+ throw new Error('No provider available for the active wallet');
445
+ }
446
+ return await this.transactionService.sendTransaction(request, provider, options);
365
447
  }
366
- if (!provider) {
367
- throw new Error('No provider available for the active wallet');
448
+ catch (error) {
449
+ this.emitError(error, 'sendTransaction');
450
+ throw error;
368
451
  }
369
- return await this.transactionService.sendTransaction(request, provider);
370
452
  }
371
453
  /**
372
454
  * Sign a Solana tx without broadcasting (raw bytes in/out, no @solana/web3.js
@@ -392,10 +474,17 @@ export class UniversalWalletConnector {
392
474
  * Fetch wallet capabilities for the connected wallet
393
475
  * @param address The wallet address to fetch capabilities for
394
476
  * @param activeNetwork Optional network to set as the active network for capabilities
477
+ * @param options Optional cancellation signal
395
478
  * @returns A promise that resolves when capabilities are fetched and updated
396
479
  */
397
- async getWalletCapabilities(address, activeNetwork) {
398
- return await this.walletCapabilitiesService.getWalletCapabilities(address, activeNetwork);
480
+ async getWalletCapabilities(address, activeNetwork, options) {
481
+ try {
482
+ return await this.walletCapabilitiesService.getWalletCapabilities(address, activeNetwork, options);
483
+ }
484
+ catch (error) {
485
+ this.emitError(error, 'getWalletCapabilities');
486
+ throw error;
487
+ }
399
488
  }
400
489
  /**
401
490
  * Check if a connection mode is available for a specific wallet
@@ -439,5 +528,21 @@ export class UniversalWalletConnector {
439
528
  const session = this.sessionManager.getSession();
440
529
  return session.activeWalletCapabilities;
441
530
  }
531
+ // ---- private helpers ----
532
+ emitError(error, operation) {
533
+ // Intentional cancellations (AbortSignal.throwIfAborted, fetch abort,
534
+ // DOMException) should NOT surface as wallet errors — the caller already
535
+ // knows it asked to abort. Skip emission so error telemetry stays clean.
536
+ if (error instanceof Error && error.name === 'AbortError') {
537
+ return;
538
+ }
539
+ const walletError = error instanceof WalletConnectorError
540
+ ? { type: error.type, message: error.message }
541
+ : {
542
+ type: 'unknown',
543
+ message: error instanceof Error ? error.message : String(error)
544
+ };
545
+ this.eventManager.emit('error', { error: walletError, operation });
546
+ }
442
547
  }
443
548
  //# sourceMappingURL=universal-wallet-connector.js.map