@meshconnect/uwc-core 0.9.0 → 0.9.1-snapshot.95b8dbe
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 +924 -0
- package/dist/events.d.ts +71 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/managers/event-manager.d.ts +22 -3
- package/dist/managers/event-manager.d.ts.map +1 -1
- package/dist/managers/event-manager.js +63 -7
- package/dist/managers/event-manager.js.map +1 -1
- package/dist/services/connection-service.d.ts +5 -2
- package/dist/services/connection-service.d.ts.map +1 -1
- package/dist/services/connection-service.js +19 -7
- 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 +15 -3
- package/dist/services/network-switch-service.js.map +1 -1
- package/dist/services/signature-service.d.ts +3 -1
- package/dist/services/signature-service.d.ts.map +1 -1
- package/dist/services/signature-service.js +10 -5
- package/dist/services/signature-service.js.map +1 -1
- package/dist/services/transaction-service.d.ts +3 -1
- package/dist/services/transaction-service.d.ts.map +1 -1
- package/dist/services/transaction-service.js +10 -5
- package/dist/services/transaction-service.js.map +1 -1
- package/dist/services/wallet-capabilities-service.d.ts +2 -1
- package/dist/services/wallet-capabilities-service.d.ts.map +1 -1
- package/dist/services/wallet-capabilities-service.js +9 -2
- package/dist/services/wallet-capabilities-service.js.map +1 -1
- package/dist/universal-wallet-connector.d.ts +52 -6
- package/dist/universal-wallet-connector.d.ts.map +1 -1
- package/dist/universal-wallet-connector.js +271 -177
- package/dist/universal-wallet-connector.js.map +1 -1
- package/package.json +5 -5
- package/src/events.ts +73 -0
- package/src/index.ts +11 -3
- package/src/managers/event-manager.test.ts +70 -0
- package/src/managers/event-manager.ts +80 -9
- package/src/services/connection-service.test.ts +13 -19
- package/src/services/connection-service.ts +34 -7
- package/src/services/network-switch-service.ts +22 -3
- package/src/services/signature-service.ts +13 -5
- package/src/services/transaction-service.ts +13 -5
- package/src/services/wallet-capabilities-service.ts +14 -2
- package/src/universal-wallet-connector.test.ts +87 -3
- package/src/universal-wallet-connector.ts +395 -204
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meshconnect/uwc-core",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1-snapshot.95b8dbe",
|
|
4
4
|
"description": "Core functionality for Universal Wallet Connector",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"src"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@meshconnect/uwc-injected-connector": "0.
|
|
20
|
-
"@meshconnect/uwc-ton-connector": "0.6.3",
|
|
21
|
-
"@meshconnect/uwc-
|
|
22
|
-
"@meshconnect/uwc-
|
|
19
|
+
"@meshconnect/uwc-injected-connector": "0.14.0-snapshot.95b8dbe",
|
|
20
|
+
"@meshconnect/uwc-ton-connector": "0.6.3-snapshot.95b8dbe",
|
|
21
|
+
"@meshconnect/uwc-wallet-connect-connector": "0.6.0-snapshot.95b8dbe",
|
|
22
|
+
"@meshconnect/uwc-types": "0.15.0-snapshot.95b8dbe"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"typescript": "^5.9.3"
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Session,
|
|
3
|
+
Network,
|
|
4
|
+
WalletMetadata,
|
|
5
|
+
ConnectionMode,
|
|
6
|
+
WalletError,
|
|
7
|
+
EVMCapabilities
|
|
8
|
+
} from '@meshconnect/uwc-types'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* User-initiated operations that can surface through the `error` event.
|
|
12
|
+
*/
|
|
13
|
+
export type UWCOperation =
|
|
14
|
+
| 'connect'
|
|
15
|
+
| 'disconnect'
|
|
16
|
+
| 'switchNetwork'
|
|
17
|
+
| 'signMessage'
|
|
18
|
+
| 'sendTransaction'
|
|
19
|
+
| 'getWalletCapabilities'
|
|
20
|
+
| 'initialize'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Typed event map emitted by UniversalWalletConnector.
|
|
24
|
+
*
|
|
25
|
+
* Subscribe with `on(event, listener)` to react to a single kind of change
|
|
26
|
+
* instead of re-reading `getSession()` on every notification.
|
|
27
|
+
*
|
|
28
|
+
* Every typed event also triggers the `change` event, so legacy
|
|
29
|
+
* `subscribe(listener)` callers continue to fire.
|
|
30
|
+
*/
|
|
31
|
+
export interface UWCEventMap {
|
|
32
|
+
/** Wallet detection finished; `getWallets()` now reflects which are installed. */
|
|
33
|
+
ready: void
|
|
34
|
+
/** Emitted once detection finishes, with the final wallet list. */
|
|
35
|
+
walletsDetected: { wallets: WalletMetadata[] }
|
|
36
|
+
|
|
37
|
+
/** `connect()` has started. */
|
|
38
|
+
connecting: { connectionMode: ConnectionMode; walletId: string }
|
|
39
|
+
/** `connect()` completed successfully. */
|
|
40
|
+
connected: { session: Session }
|
|
41
|
+
/** `disconnect()` completed and the session was cleared. */
|
|
42
|
+
disconnected: void
|
|
43
|
+
/**
|
|
44
|
+
* A pairing URI became available for WalletConnect / TonConnect flows.
|
|
45
|
+
* Prefer this over polling `getConnectionURI()`.
|
|
46
|
+
*/
|
|
47
|
+
connectionUri: { uri: string; connectionMode: ConnectionMode }
|
|
48
|
+
|
|
49
|
+
/** The active session changed (address, network, capabilities, …). */
|
|
50
|
+
sessionChanged: { session: Session }
|
|
51
|
+
|
|
52
|
+
/** Network switching is in progress. Fires on start and end. */
|
|
53
|
+
networkSwitching: { isLoading: boolean; isWaitingForUserApproval: boolean }
|
|
54
|
+
/** Network switch succeeded. */
|
|
55
|
+
networkSwitched: { network: Network }
|
|
56
|
+
|
|
57
|
+
/** Wallet capabilities were refreshed. */
|
|
58
|
+
capabilitiesUpdated: { capabilities: Record<string, EVMCapabilities> }
|
|
59
|
+
|
|
60
|
+
/** A user-initiated operation failed. */
|
|
61
|
+
error: { error: WalletError; operation: UWCOperation }
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Back-compat catch-all. Fires after every other event above.
|
|
65
|
+
* New code should prefer the specific events.
|
|
66
|
+
*/
|
|
67
|
+
change: void
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type UWCEventName = keyof UWCEventMap
|
|
71
|
+
export type UWCEventListener<K extends UWCEventName> = (
|
|
72
|
+
data: UWCEventMap[K]
|
|
73
|
+
) => void
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export {
|
|
2
|
+
UniversalWalletConnector,
|
|
3
|
+
type UWCConfig,
|
|
4
|
+
type OperationOptions
|
|
5
|
+
} from './universal-wallet-connector'
|
|
6
|
+
export type {
|
|
7
|
+
UWCEventMap,
|
|
8
|
+
UWCEventName,
|
|
9
|
+
UWCEventListener,
|
|
10
|
+
UWCOperation
|
|
11
|
+
} from './events'
|
|
@@ -71,4 +71,74 @@ describe('EventManager', () => {
|
|
|
71
71
|
eventManager.clearAllListeners()
|
|
72
72
|
expect(eventManager.getListenerCount()).toBe(0)
|
|
73
73
|
})
|
|
74
|
+
|
|
75
|
+
describe('typed events', () => {
|
|
76
|
+
it('dispatches typed event data only to on() listeners of that event', () => {
|
|
77
|
+
const disconnected: unknown[] = []
|
|
78
|
+
const networkSwitched: unknown[] = []
|
|
79
|
+
|
|
80
|
+
eventManager.on('disconnected', data => disconnected.push(data))
|
|
81
|
+
eventManager.on('networkSwitched', data => networkSwitched.push(data))
|
|
82
|
+
|
|
83
|
+
eventManager.emit('disconnected', undefined)
|
|
84
|
+
|
|
85
|
+
expect(disconnected).toHaveLength(1)
|
|
86
|
+
expect(networkSwitched).toHaveLength(0)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('cascades every typed event to the legacy change channel', () => {
|
|
90
|
+
let changeCount = 0
|
|
91
|
+
eventManager.subscribe(() => {
|
|
92
|
+
changeCount++
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
eventManager.emit('connectionUri', {
|
|
96
|
+
uri: 'wc://x',
|
|
97
|
+
connectionMode: 'walletConnect'
|
|
98
|
+
})
|
|
99
|
+
eventManager.emit('disconnected', undefined)
|
|
100
|
+
|
|
101
|
+
expect(changeCount).toBe(2)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('off() removes a listener from the typed set', () => {
|
|
105
|
+
const calls: unknown[] = []
|
|
106
|
+
const listener = (d: unknown) => calls.push(d)
|
|
107
|
+
eventManager.on('ready', listener)
|
|
108
|
+
eventManager.off('ready', listener)
|
|
109
|
+
eventManager.emit('ready', undefined)
|
|
110
|
+
expect(calls).toHaveLength(0)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('once() removes the listener after a single dispatch', () => {
|
|
114
|
+
let calls = 0
|
|
115
|
+
eventManager.once('ready', () => {
|
|
116
|
+
calls++
|
|
117
|
+
})
|
|
118
|
+
eventManager.emit('ready', undefined)
|
|
119
|
+
eventManager.emit('ready', undefined)
|
|
120
|
+
expect(calls).toBe(1)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('a throwing listener does not stop other listeners', () => {
|
|
124
|
+
const calls: string[] = []
|
|
125
|
+
eventManager.on('ready', () => {
|
|
126
|
+
throw new Error('boom')
|
|
127
|
+
})
|
|
128
|
+
eventManager.on('ready', () => {
|
|
129
|
+
calls.push('second')
|
|
130
|
+
})
|
|
131
|
+
eventManager.emit('ready', undefined)
|
|
132
|
+
expect(calls).toEqual(['second'])
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('emit("change") does not recurse into itself', () => {
|
|
136
|
+
let count = 0
|
|
137
|
+
eventManager.on('change', () => {
|
|
138
|
+
count++
|
|
139
|
+
})
|
|
140
|
+
eventManager.emit('change', undefined)
|
|
141
|
+
expect(count).toBe(1)
|
|
142
|
+
})
|
|
143
|
+
})
|
|
74
144
|
})
|
|
@@ -1,22 +1,93 @@
|
|
|
1
|
-
type
|
|
2
|
-
type Unsubscribe = () => void
|
|
1
|
+
import type { UWCEventMap, UWCEventName, UWCEventListener } from '../events'
|
|
3
2
|
|
|
3
|
+
type LegacyListener = () => void
|
|
4
|
+
type UntypedListener = (data: unknown) => void
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Typed event emitter used by UniversalWalletConnector.
|
|
8
|
+
*
|
|
9
|
+
* Each event has its own listener set. Emitting any typed event also
|
|
10
|
+
* dispatches the legacy `change` event, so `subscribe()` callers keep
|
|
11
|
+
* receiving a notification on every state change.
|
|
12
|
+
*/
|
|
4
13
|
export class EventManager {
|
|
5
|
-
private listeners: Set<
|
|
14
|
+
private listeners: Map<UWCEventName, Set<UntypedListener>> = new Map()
|
|
15
|
+
|
|
16
|
+
/** Subscribe to a typed event. Returns an unsubscribe function. */
|
|
17
|
+
on<K extends UWCEventName>(
|
|
18
|
+
event: K,
|
|
19
|
+
listener: UWCEventListener<K>
|
|
20
|
+
): () => void {
|
|
21
|
+
let set = this.listeners.get(event)
|
|
22
|
+
if (!set) {
|
|
23
|
+
set = new Set()
|
|
24
|
+
this.listeners.set(event, set)
|
|
25
|
+
}
|
|
26
|
+
set.add(listener as UntypedListener)
|
|
27
|
+
return () => this.off(event, listener)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Subscribe once; the listener is removed after the first dispatch. */
|
|
31
|
+
once<K extends UWCEventName>(
|
|
32
|
+
event: K,
|
|
33
|
+
listener: UWCEventListener<K>
|
|
34
|
+
): () => void {
|
|
35
|
+
const wrapper: UWCEventListener<K> = data => {
|
|
36
|
+
this.off(event, wrapper)
|
|
37
|
+
listener(data)
|
|
38
|
+
}
|
|
39
|
+
return this.on(event, wrapper)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
off<K extends UWCEventName>(event: K, listener: UWCEventListener<K>): void {
|
|
43
|
+
const set = this.listeners.get(event)
|
|
44
|
+
set?.delete(listener as UntypedListener)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
emit<K extends UWCEventName>(event: K, data: UWCEventMap[K]): void {
|
|
48
|
+
this.dispatch(event, data)
|
|
49
|
+
if (event !== 'change') {
|
|
50
|
+
this.dispatch('change', undefined as UWCEventMap['change'])
|
|
51
|
+
}
|
|
52
|
+
}
|
|
6
53
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
54
|
+
private dispatch<K extends UWCEventName>(
|
|
55
|
+
event: K,
|
|
56
|
+
data: UWCEventMap[K]
|
|
57
|
+
): void {
|
|
58
|
+
const set = this.listeners.get(event)
|
|
59
|
+
if (!set || set.size === 0) return
|
|
60
|
+
// Clone to guard against mutation during dispatch.
|
|
61
|
+
for (const listener of [...set]) {
|
|
62
|
+
try {
|
|
63
|
+
listener(data)
|
|
64
|
+
} catch {
|
|
65
|
+
// A bad listener shouldn't break the rest of the dispatch chain.
|
|
66
|
+
}
|
|
11
67
|
}
|
|
12
68
|
}
|
|
13
69
|
|
|
70
|
+
// ---- legacy API (still used by the React ConnectionProvider) ----
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @deprecated Prefer `on(eventName, listener)` for targeted updates.
|
|
74
|
+
* Subscribes to the catch-all `change` event.
|
|
75
|
+
*/
|
|
76
|
+
subscribe(listener: LegacyListener): () => void {
|
|
77
|
+
return this.on('change', listener)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** @deprecated Prefer `emit(eventName, data)`. Emits `change` with no payload. */
|
|
14
81
|
notify(): void {
|
|
15
|
-
this.
|
|
82
|
+
this.dispatch('change', undefined as UWCEventMap['change'])
|
|
16
83
|
}
|
|
17
84
|
|
|
18
85
|
getListenerCount(): number {
|
|
19
|
-
|
|
86
|
+
let count = 0
|
|
87
|
+
for (const set of this.listeners.values()) {
|
|
88
|
+
count += set.size
|
|
89
|
+
}
|
|
90
|
+
return count
|
|
20
91
|
}
|
|
21
92
|
|
|
22
93
|
clearAllListeners(): void {
|
|
@@ -253,10 +253,12 @@ describe('ConnectionService', () => {
|
|
|
253
253
|
})
|
|
254
254
|
})
|
|
255
255
|
|
|
256
|
-
it('
|
|
257
|
-
vi.
|
|
256
|
+
it('emits connectionUri and change events when URI becomes available', async () => {
|
|
257
|
+
const uriListener = vi.fn()
|
|
258
|
+
const changeListener = vi.fn()
|
|
259
|
+
eventManager.on('connectionUri', uriListener)
|
|
260
|
+
eventManager.on('change', changeListener)
|
|
258
261
|
|
|
259
|
-
const notifySpy = vi.spyOn(eventManager, 'notify')
|
|
260
262
|
let connectResolve: (v: any) => void
|
|
261
263
|
|
|
262
264
|
tonConnector.connect = vi.fn().mockReturnValue(
|
|
@@ -270,7 +272,7 @@ describe('ConnectionService', () => {
|
|
|
270
272
|
tonConnector.getConnectionURI = vi.fn().mockImplementation(() => {
|
|
271
273
|
uriCallCount++
|
|
272
274
|
if (uriCallCount >= 2) return 'tc://some-uri'
|
|
273
|
-
|
|
275
|
+
throw new Error('No URI yet')
|
|
274
276
|
})
|
|
275
277
|
|
|
276
278
|
const connectPromise = connectionService.connect(
|
|
@@ -278,22 +280,14 @@ describe('ConnectionService', () => {
|
|
|
278
280
|
'tonkeeper'
|
|
279
281
|
)
|
|
280
282
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// The URI becomes available on the second poll, which should stop polling
|
|
284
|
-
// and emit the event exactly once.
|
|
285
|
-
expect(notifySpy).toHaveBeenCalledTimes(1)
|
|
286
|
-
const uriCallCountAfterDiscovery = vi.mocked(
|
|
287
|
-
tonConnector.getConnectionURI
|
|
288
|
-
).mock.calls.length
|
|
289
|
-
expect(uriCallCountAfterDiscovery).toBe(2)
|
|
283
|
+
// Wait for polling to detect URI
|
|
284
|
+
await new Promise(r => setTimeout(r, 250))
|
|
290
285
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
)
|
|
286
|
+
expect(uriListener).toHaveBeenCalledWith({
|
|
287
|
+
uri: 'tc://some-uri',
|
|
288
|
+
connectionMode: 'tonConnect'
|
|
289
|
+
})
|
|
290
|
+
expect(changeListener).toHaveBeenCalled()
|
|
297
291
|
|
|
298
292
|
// Resolve the connection to let the test finish
|
|
299
293
|
connectResolve!({
|
|
@@ -13,6 +13,10 @@ import type {
|
|
|
13
13
|
import type { SessionManager } from '../managers/session-manager'
|
|
14
14
|
import type { EventManager } from '../managers/event-manager'
|
|
15
15
|
|
|
16
|
+
export interface ServiceOptions {
|
|
17
|
+
signal?: AbortSignal
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
export class ConnectionService {
|
|
17
21
|
private activeConnector: Connector | null = null
|
|
18
22
|
|
|
@@ -28,8 +32,13 @@ export class ConnectionService {
|
|
|
28
32
|
async connect(
|
|
29
33
|
connectionMode: ConnectionMode,
|
|
30
34
|
walletId: string,
|
|
31
|
-
networkId?: NetworkId
|
|
35
|
+
networkId?: NetworkId,
|
|
36
|
+
options?: ServiceOptions
|
|
32
37
|
): Promise<void> {
|
|
38
|
+
options?.signal?.throwIfAborted()
|
|
39
|
+
|
|
40
|
+
this.eventManager.emit('connecting', { connectionMode, walletId })
|
|
41
|
+
|
|
33
42
|
// Find the wallet
|
|
34
43
|
const wallet = this.findWallet(walletId)
|
|
35
44
|
|
|
@@ -59,7 +68,12 @@ export class ConnectionService {
|
|
|
59
68
|
connectionMode === 'tonConnect' ||
|
|
60
69
|
connectionMode === 'walletConnect'
|
|
61
70
|
) {
|
|
62
|
-
result = await this.connectWithURIPoll(
|
|
71
|
+
result = await this.connectWithURIPoll(
|
|
72
|
+
connector,
|
|
73
|
+
network,
|
|
74
|
+
provider,
|
|
75
|
+
connectionMode
|
|
76
|
+
)
|
|
63
77
|
} else {
|
|
64
78
|
throw new Error(`Unsupported connection mode: ${connectionMode}`)
|
|
65
79
|
}
|
|
@@ -68,6 +82,9 @@ export class ConnectionService {
|
|
|
68
82
|
throw error
|
|
69
83
|
}
|
|
70
84
|
|
|
85
|
+
// If the caller aborted during the await, bail before mutating session
|
|
86
|
+
options?.signal?.throwIfAborted()
|
|
87
|
+
|
|
71
88
|
// Filter available addresses to only include those for configured networks
|
|
72
89
|
const filteredAvailableAddresses = result.availableAddresses.filter(addr =>
|
|
73
90
|
this.networks.some(n => n.id === addr.networkId)
|
|
@@ -84,22 +101,28 @@ export class ConnectionService {
|
|
|
84
101
|
),
|
|
85
102
|
availableAddresses: filteredAvailableAddresses
|
|
86
103
|
})
|
|
104
|
+
|
|
105
|
+
const session = this.sessionManager.getSession()
|
|
106
|
+
this.eventManager.emit('connected', { session })
|
|
107
|
+
this.eventManager.emit('sessionChanged', { session })
|
|
87
108
|
}
|
|
88
109
|
|
|
89
110
|
private async connectWithURIPoll(
|
|
90
111
|
connector: Connector,
|
|
91
112
|
network: Network,
|
|
92
|
-
provider: SupportedProvider
|
|
113
|
+
provider: SupportedProvider,
|
|
114
|
+
connectionMode: ConnectionMode
|
|
93
115
|
): Promise<ConnectorResult> {
|
|
94
116
|
const connectPromise = connector.connect(network, provider)
|
|
95
117
|
|
|
96
118
|
let pollingStopped = false
|
|
119
|
+
let emittedUri: string | undefined
|
|
97
120
|
|
|
98
121
|
const pollForURI = setInterval(() => {
|
|
99
122
|
const uri = this.getConnectionURI()
|
|
100
|
-
if (uri) {
|
|
101
|
-
|
|
102
|
-
this.eventManager.
|
|
123
|
+
if (uri && uri !== emittedUri) {
|
|
124
|
+
emittedUri = uri
|
|
125
|
+
this.eventManager.emit('connectionUri', { uri, connectionMode })
|
|
103
126
|
}
|
|
104
127
|
}, 100)
|
|
105
128
|
|
|
@@ -234,7 +257,8 @@ export class ConnectionService {
|
|
|
234
257
|
return network
|
|
235
258
|
}
|
|
236
259
|
|
|
237
|
-
async disconnect(): Promise<void> {
|
|
260
|
+
async disconnect(options?: ServiceOptions): Promise<void> {
|
|
261
|
+
options?.signal?.throwIfAborted()
|
|
238
262
|
try {
|
|
239
263
|
if (
|
|
240
264
|
this.activeConnector &&
|
|
@@ -245,6 +269,9 @@ export class ConnectionService {
|
|
|
245
269
|
} finally {
|
|
246
270
|
this.activeConnector = null
|
|
247
271
|
this.sessionManager.clearSession()
|
|
272
|
+
const session = this.sessionManager.getSession()
|
|
273
|
+
this.eventManager.emit('disconnected', undefined)
|
|
274
|
+
this.eventManager.emit('sessionChanged', { session })
|
|
248
275
|
}
|
|
249
276
|
}
|
|
250
277
|
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
} from '@meshconnect/uwc-types'
|
|
12
12
|
import type { SessionManager } from '../managers/session-manager'
|
|
13
13
|
import type { EventManager } from '../managers/event-manager'
|
|
14
|
+
import type { ServiceOptions } from './connection-service'
|
|
14
15
|
|
|
15
16
|
export class NetworkSwitchService {
|
|
16
17
|
private isLoading = false
|
|
@@ -31,7 +32,12 @@ export class NetworkSwitchService {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
async switchNetwork(
|
|
35
|
+
async switchNetwork(
|
|
36
|
+
networkId: NetworkId,
|
|
37
|
+
options?: ServiceOptions
|
|
38
|
+
): Promise<void> {
|
|
39
|
+
options?.signal?.throwIfAborted()
|
|
40
|
+
|
|
35
41
|
// Check if there's an active connection
|
|
36
42
|
const session = this.sessionManager.getSession()
|
|
37
43
|
if (!session) {
|
|
@@ -104,7 +110,10 @@ export class NetworkSwitchService {
|
|
|
104
110
|
// Set loading states
|
|
105
111
|
this.isLoading = true
|
|
106
112
|
this.isWaitingForUserApproval = requiresApproval
|
|
107
|
-
this.eventManager.
|
|
113
|
+
this.eventManager.emit('networkSwitching', {
|
|
114
|
+
isLoading: true,
|
|
115
|
+
isWaitingForUserApproval: requiresApproval
|
|
116
|
+
})
|
|
108
117
|
|
|
109
118
|
// Perform the network switch and get the new address
|
|
110
119
|
// Pass the appropriate provider based on connection mode
|
|
@@ -129,6 +138,8 @@ export class NetworkSwitchService {
|
|
|
129
138
|
result = await activeConnector.switchNetwork(network)
|
|
130
139
|
}
|
|
131
140
|
|
|
141
|
+
options?.signal?.throwIfAborted()
|
|
142
|
+
|
|
132
143
|
// Filter available addresses to only include those for configured networks
|
|
133
144
|
const filteredAvailableAddresses = result.availableAddresses
|
|
134
145
|
? result.availableAddresses.filter(addr =>
|
|
@@ -144,11 +155,19 @@ export class NetworkSwitchService {
|
|
|
144
155
|
availableAddresses: filteredAvailableAddresses
|
|
145
156
|
})
|
|
146
157
|
})
|
|
158
|
+
|
|
159
|
+
this.eventManager.emit('networkSwitched', { network })
|
|
160
|
+
this.eventManager.emit('sessionChanged', {
|
|
161
|
+
session: this.sessionManager.getSession()
|
|
162
|
+
})
|
|
147
163
|
} finally {
|
|
148
164
|
// Clear loading states
|
|
149
165
|
this.isLoading = false
|
|
150
166
|
this.isWaitingForUserApproval = false
|
|
151
|
-
this.eventManager.
|
|
167
|
+
this.eventManager.emit('networkSwitching', {
|
|
168
|
+
isLoading: false,
|
|
169
|
+
isWaitingForUserApproval: false
|
|
170
|
+
})
|
|
152
171
|
}
|
|
153
172
|
}
|
|
154
173
|
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
EIP712TypedData
|
|
10
10
|
} from '@meshconnect/uwc-types'
|
|
11
11
|
import type { SessionManager } from '../managers/session-manager'
|
|
12
|
+
import type { ServiceOptions } from './connection-service'
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Service for handling signature operations across different connector types
|
|
@@ -23,12 +24,15 @@ export class SignatureService {
|
|
|
23
24
|
* Sign a message with the currently connected wallet
|
|
24
25
|
* @param message The message to sign
|
|
25
26
|
* @param provider The wallet provider to use for signing (required for injected connector)
|
|
27
|
+
* @param options Optional cancellation signal
|
|
26
28
|
* @returns A promise that resolves to the signature
|
|
27
29
|
*/
|
|
28
30
|
async signMessage(
|
|
29
31
|
message: string,
|
|
30
|
-
provider?: SupportedProvider
|
|
32
|
+
provider?: SupportedProvider,
|
|
33
|
+
options?: ServiceOptions
|
|
31
34
|
): Promise<SignatureType> {
|
|
35
|
+
options?.signal?.throwIfAborted()
|
|
32
36
|
const session = this.sessionManager.getSession()
|
|
33
37
|
|
|
34
38
|
if (!session.connectionMode) {
|
|
@@ -53,30 +57,34 @@ export class SignatureService {
|
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
// Call the connector's signMessage method with the appropriate provider
|
|
60
|
+
let signature: SignatureType
|
|
56
61
|
if (session.connectionMode === 'injected') {
|
|
57
62
|
if (!provider) {
|
|
58
63
|
throw new Error('Provider is required for injected connector')
|
|
59
64
|
}
|
|
60
|
-
|
|
65
|
+
signature = await connector.signMessage(
|
|
61
66
|
message,
|
|
62
67
|
provider as
|
|
63
68
|
| ExtensionInjectedProvider
|
|
64
69
|
| IntegratedBrowserInjectedProvider
|
|
65
70
|
)
|
|
66
71
|
} else if (session.connectionMode === 'tonConnect') {
|
|
67
|
-
|
|
72
|
+
signature = await connector.signMessage(message)
|
|
68
73
|
} else if (session.connectionMode === 'walletConnect') {
|
|
69
74
|
if (!provider) {
|
|
70
75
|
throw new Error('Provider is required for WalletConnect connector')
|
|
71
76
|
}
|
|
72
|
-
|
|
77
|
+
signature = await connector.signMessage(
|
|
73
78
|
message,
|
|
74
79
|
provider as WalletConnectProvider
|
|
75
80
|
)
|
|
76
81
|
} else {
|
|
77
82
|
// For other connectors that might not need the provider
|
|
78
|
-
|
|
83
|
+
signature = await connector.signMessage(message)
|
|
79
84
|
}
|
|
85
|
+
|
|
86
|
+
options?.signal?.throwIfAborted()
|
|
87
|
+
return signature
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
/**
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
TransactionResult
|
|
10
10
|
} from '@meshconnect/uwc-types'
|
|
11
11
|
import type { SessionManager } from '../managers/session-manager'
|
|
12
|
+
import type { ServiceOptions } from './connection-service'
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Service for handling transaction operations across different connector types
|
|
@@ -23,12 +24,15 @@ export class TransactionService {
|
|
|
23
24
|
* Send a transaction with the currently connected wallet
|
|
24
25
|
* @param request The transaction request parameters
|
|
25
26
|
* @param provider The wallet provider to use for sending (required for injected connector)
|
|
27
|
+
* @param options Optional cancellation signal
|
|
26
28
|
* @returns A promise that resolves to the transaction result
|
|
27
29
|
*/
|
|
28
30
|
async sendTransaction(
|
|
29
31
|
request: TransactionRequest,
|
|
30
|
-
provider?: SupportedProvider
|
|
32
|
+
provider?: SupportedProvider,
|
|
33
|
+
options?: ServiceOptions
|
|
31
34
|
): Promise<TransactionResult> {
|
|
35
|
+
options?.signal?.throwIfAborted()
|
|
32
36
|
const session = this.sessionManager.getSession()
|
|
33
37
|
|
|
34
38
|
if (!session.connectionMode) {
|
|
@@ -53,29 +57,33 @@ export class TransactionService {
|
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
// Call the connector's sendTransaction method with the appropriate provider
|
|
60
|
+
let result: TransactionResult
|
|
56
61
|
if (session.connectionMode === 'injected') {
|
|
57
62
|
if (!provider) {
|
|
58
63
|
throw new Error('Provider is required for injected connector')
|
|
59
64
|
}
|
|
60
|
-
|
|
65
|
+
result = await connector.sendTransaction(
|
|
61
66
|
request,
|
|
62
67
|
provider as
|
|
63
68
|
| ExtensionInjectedProvider
|
|
64
69
|
| IntegratedBrowserInjectedProvider
|
|
65
70
|
)
|
|
66
71
|
} else if (session.connectionMode === 'tonConnect') {
|
|
67
|
-
|
|
72
|
+
result = await connector.sendTransaction(request)
|
|
68
73
|
} else if (session.connectionMode === 'walletConnect') {
|
|
69
74
|
if (!provider) {
|
|
70
75
|
throw new Error('Provider is required for WalletConnect connector')
|
|
71
76
|
}
|
|
72
|
-
|
|
77
|
+
result = await connector.sendTransaction(
|
|
73
78
|
request,
|
|
74
79
|
provider as WalletConnectProvider
|
|
75
80
|
)
|
|
76
81
|
} else {
|
|
77
82
|
// For other connectors that might not need the provider
|
|
78
|
-
|
|
83
|
+
result = await connector.sendTransaction(request)
|
|
79
84
|
}
|
|
85
|
+
|
|
86
|
+
options?.signal?.throwIfAborted()
|
|
87
|
+
return result
|
|
80
88
|
}
|
|
81
89
|
}
|
|
@@ -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
|
-
|
|
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
|
}
|