@meshconnect/uwc-core 0.2.13 → 0.2.15
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/dist/services/connection-service.d.ts.map +1 -1
- package/dist/services/connection-service.js +1 -1
- package/dist/services/connection-service.js.map +1 -1
- package/package.json +5 -5
- package/src/managers/event-manager.test.ts +74 -0
- package/src/managers/session-manager.test.ts +119 -0
- package/src/services/connection-service.test.ts +238 -0
- package/src/services/connection-service.ts +4 -1
- package/src/services/network-switch-service.test.ts +187 -0
- package/src/services/signature-service.test.ts +108 -0
- package/src/services/transaction-service.test.ts +163 -0
- package/src/universal-wallet-connector.test.ts +428 -0
- package/src/utils/network-rpc-utils.test.ts +329 -0
- package/dist/connection-manager.d.ts +0 -18
- package/dist/connection-manager.d.ts.map +0 -1
- package/dist/connection-manager.js +0 -54
- package/dist/connection-manager.js.map +0 -1
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
|
|
2
|
+
import { UniversalWalletConnector } from './universal-wallet-connector'
|
|
3
|
+
import type {
|
|
4
|
+
Network,
|
|
5
|
+
WalletMetadata,
|
|
6
|
+
WalletConnectConfig,
|
|
7
|
+
TransactionRequest
|
|
8
|
+
} from '@meshconnect/uwc-types'
|
|
9
|
+
|
|
10
|
+
const mockInjectedConnector = {
|
|
11
|
+
getAvailableWallets: vi.fn().mockResolvedValue([])
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const mockWalletConnectConnector = {
|
|
15
|
+
getAvailableWallets: vi.fn().mockResolvedValue([])
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const mockSessionManager = {
|
|
19
|
+
getSession: vi.fn().mockReturnValue({
|
|
20
|
+
connectionMode: null,
|
|
21
|
+
activeWallet: null,
|
|
22
|
+
activeNetwork: null,
|
|
23
|
+
activeAddress: null,
|
|
24
|
+
availableNetworks: [],
|
|
25
|
+
availableAddresses: [],
|
|
26
|
+
activeWalletCapabilities: null
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const mockEventManager = {
|
|
31
|
+
subscribe: vi.fn().mockReturnValue(() => {}),
|
|
32
|
+
notify: vi.fn()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const mockConnectionService = {
|
|
36
|
+
getWallets: vi.fn().mockReturnValue([]),
|
|
37
|
+
getNetworks: vi.fn().mockReturnValue([]),
|
|
38
|
+
connect: vi.fn(),
|
|
39
|
+
disconnect: vi.fn(),
|
|
40
|
+
getConnectionURI: vi.fn(),
|
|
41
|
+
updateWallets: vi.fn()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const mockNetworkSwitchService = {
|
|
45
|
+
switchNetwork: vi.fn(),
|
|
46
|
+
getLoadingState: vi.fn().mockReturnValue(false)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const mockSignatureService = {
|
|
50
|
+
signMessage: vi.fn()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const mockTransactionService = {
|
|
54
|
+
sendTransaction: vi.fn()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Mock the dependencies
|
|
58
|
+
vi.mock('@meshconnect/uwc-injected-connector', () => ({
|
|
59
|
+
InjectedConnector: vi.fn(() => mockInjectedConnector)
|
|
60
|
+
}))
|
|
61
|
+
|
|
62
|
+
vi.mock('@meshconnect/uwc-wallet-connect-connector', () => ({
|
|
63
|
+
WalletConnectConnector: vi.fn(() => mockWalletConnectConnector)
|
|
64
|
+
}))
|
|
65
|
+
|
|
66
|
+
vi.mock('@meshconnect/uwc-bridge-child', () => ({
|
|
67
|
+
BridgeChild: vi.fn()
|
|
68
|
+
}))
|
|
69
|
+
|
|
70
|
+
vi.mock('./managers/session-manager', () => ({
|
|
71
|
+
SessionManager: vi.fn(() => mockSessionManager)
|
|
72
|
+
}))
|
|
73
|
+
|
|
74
|
+
vi.mock('./managers/event-manager', () => ({
|
|
75
|
+
EventManager: vi.fn(() => mockEventManager)
|
|
76
|
+
}))
|
|
77
|
+
|
|
78
|
+
vi.mock('./services/connection-service', () => ({
|
|
79
|
+
ConnectionService: vi.fn(() => mockConnectionService)
|
|
80
|
+
}))
|
|
81
|
+
|
|
82
|
+
vi.mock('./services/network-switch-service', () => ({
|
|
83
|
+
NetworkSwitchService: vi.fn(() => mockNetworkSwitchService)
|
|
84
|
+
}))
|
|
85
|
+
|
|
86
|
+
vi.mock('./services/signature-service', () => ({
|
|
87
|
+
SignatureService: vi.fn(() => mockSignatureService)
|
|
88
|
+
}))
|
|
89
|
+
|
|
90
|
+
vi.mock('./services/transaction-service', () => ({
|
|
91
|
+
TransactionService: vi.fn(() => mockTransactionService)
|
|
92
|
+
}))
|
|
93
|
+
|
|
94
|
+
vi.mock('./utils/network-rpc-utils', () => ({
|
|
95
|
+
createNetworkRpcMap: vi.fn().mockReturnValue(new Map())
|
|
96
|
+
}))
|
|
97
|
+
|
|
98
|
+
describe('UniversalWalletConnector', () => {
|
|
99
|
+
let mockNetworks: Network[]
|
|
100
|
+
let mockWallets: WalletMetadata[]
|
|
101
|
+
let mockWalletConnectConfig: WalletConnectConfig
|
|
102
|
+
|
|
103
|
+
beforeEach(() => {
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
+
;(UniversalWalletConnector as any).instanceCount = 0
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
+
;(UniversalWalletConnector as any).hasWarnedAboutMultipleInstances = false
|
|
108
|
+
|
|
109
|
+
mockNetworks = [
|
|
110
|
+
{
|
|
111
|
+
id: 'eip155:1',
|
|
112
|
+
internalId: 1,
|
|
113
|
+
name: 'Ethereum Mainnet',
|
|
114
|
+
namespace: 'eip155',
|
|
115
|
+
logoUrl: 'https://example.com/eth.png',
|
|
116
|
+
rpcUrls: {
|
|
117
|
+
default: {
|
|
118
|
+
http: ['https://mainnet.infura.io/v3/test']
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
blockExplorers: {
|
|
122
|
+
default: {
|
|
123
|
+
name: 'Etherscan',
|
|
124
|
+
url: 'https://etherscan.io'
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
networkType: 'evm',
|
|
128
|
+
nativeCurrency: {
|
|
129
|
+
name: 'Ether',
|
|
130
|
+
symbol: 'ETH',
|
|
131
|
+
decimals: 18
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
mockWallets = [
|
|
137
|
+
{
|
|
138
|
+
id: 'metamask',
|
|
139
|
+
name: 'MetaMask',
|
|
140
|
+
metadata: {},
|
|
141
|
+
extensionInjectedProvider: {
|
|
142
|
+
supportedNetworkIds: ['eip155:1'] as const,
|
|
143
|
+
namespaceMetaData: {
|
|
144
|
+
eip155: {
|
|
145
|
+
eip155Name: 'MetaMask',
|
|
146
|
+
injectedId: 'io.metamask',
|
|
147
|
+
supportsAddingNetworks: true,
|
|
148
|
+
requiresUserApprovalOnNetworkSwitch: false
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
requiresUserApprovalOnNamespaceSwitch: false
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
mockWalletConnectConfig = {
|
|
157
|
+
projectId: 'test-project-id',
|
|
158
|
+
metadata: {
|
|
159
|
+
name: 'Test App',
|
|
160
|
+
description: 'Test App Description',
|
|
161
|
+
url: 'https://test.com',
|
|
162
|
+
icons: ['https://test.com/icon.png']
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Clear all mocks
|
|
167
|
+
vi.clearAllMocks()
|
|
168
|
+
|
|
169
|
+
// Reset mock return values for methods that are called in the constructor
|
|
170
|
+
mockInjectedConnector.getAvailableWallets.mockResolvedValue([])
|
|
171
|
+
mockWalletConnectConnector.getAvailableWallets.mockResolvedValue([])
|
|
172
|
+
mockConnectionService.getWallets.mockReturnValue(mockWallets)
|
|
173
|
+
mockConnectionService.getNetworks.mockReturnValue(mockNetworks)
|
|
174
|
+
mockSessionManager.getSession.mockReturnValue({
|
|
175
|
+
connectionMode: null,
|
|
176
|
+
activeWallet: null,
|
|
177
|
+
activeNetwork: null,
|
|
178
|
+
activeAddress: null,
|
|
179
|
+
availableNetworks: [],
|
|
180
|
+
availableAddresses: [],
|
|
181
|
+
activeWalletCapabilities: null
|
|
182
|
+
})
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
afterEach(() => {
|
|
186
|
+
vi.restoreAllMocks()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
describe('constructor', () => {
|
|
190
|
+
it('should initialize with required parameters', () => {
|
|
191
|
+
const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
192
|
+
|
|
193
|
+
expect(connector).toBeInstanceOf(UniversalWalletConnector)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('should initialize with WalletConnect config when provided', () => {
|
|
197
|
+
const connector = new UniversalWalletConnector(
|
|
198
|
+
mockNetworks,
|
|
199
|
+
mockWallets,
|
|
200
|
+
false,
|
|
201
|
+
mockWalletConnectConfig
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
expect(connector).toBeInstanceOf(UniversalWalletConnector)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('should throw error when networks are not provided', () => {
|
|
208
|
+
expect(() => {
|
|
209
|
+
// @ts-expect-error Testing error case with null networks
|
|
210
|
+
new UniversalWalletConnector(null, mockWallets)
|
|
211
|
+
}).toThrow(
|
|
212
|
+
'Universal Wallet Connector must be initialized with at least one network'
|
|
213
|
+
)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('should throw error when wallets are not provided', () => {
|
|
217
|
+
expect(() => {
|
|
218
|
+
// @ts-expect-error Testing error case with null wallets
|
|
219
|
+
new UniversalWalletConnector(mockNetworks, null)
|
|
220
|
+
}).toThrow(
|
|
221
|
+
'Universal Wallet Connector must be initialized with at least one wallet'
|
|
222
|
+
)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('should warn about multiple instances', () => {
|
|
226
|
+
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
227
|
+
|
|
228
|
+
new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
229
|
+
new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
230
|
+
|
|
231
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
232
|
+
expect.stringContaining(
|
|
233
|
+
'WARNING: Multiple instances of UniversalWalletConnector detected'
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
consoleSpy.mockRestore()
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
describe('isReady', () => {
|
|
242
|
+
it('should return true after detection is complete', async () => {
|
|
243
|
+
const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
244
|
+
|
|
245
|
+
// Wait for async initialization to complete
|
|
246
|
+
await new Promise(resolve => setTimeout(resolve, 0))
|
|
247
|
+
|
|
248
|
+
expect(connector.isReady()).toBe(true)
|
|
249
|
+
})
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
describe('isConnectionModeAvailable', () => {
|
|
253
|
+
it('should return false for unknown wallet', () => {
|
|
254
|
+
const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
255
|
+
|
|
256
|
+
expect(
|
|
257
|
+
connector.isConnectionModeAvailable('injected', 'unknown-wallet')
|
|
258
|
+
).toBe(false)
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
it('should return false for WalletConnect when connector not available', () => {
|
|
262
|
+
const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
263
|
+
|
|
264
|
+
expect(
|
|
265
|
+
connector.isConnectionModeAvailable('walletConnect', 'metamask')
|
|
266
|
+
).toBe(false)
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('should return true for WalletConnect when connector and provider available', () => {
|
|
270
|
+
const walletWithWalletConnect = {
|
|
271
|
+
...mockWallets[0],
|
|
272
|
+
walletConnectProvider: {
|
|
273
|
+
supportedNetworkIds: ['eip155:1'] as const,
|
|
274
|
+
deeplinks: {
|
|
275
|
+
universal: 'https://metamask.app',
|
|
276
|
+
native: 'metamask://'
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
} as WalletMetadata
|
|
280
|
+
|
|
281
|
+
const connector = new UniversalWalletConnector(
|
|
282
|
+
mockNetworks,
|
|
283
|
+
[walletWithWalletConnect],
|
|
284
|
+
false,
|
|
285
|
+
mockWalletConnectConfig
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
expect(
|
|
289
|
+
connector.isConnectionModeAvailable('walletConnect', 'metamask')
|
|
290
|
+
).toBe(true)
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('should return false for injected when no provider', () => {
|
|
294
|
+
const walletWithoutProvider = {
|
|
295
|
+
...mockWallets[0],
|
|
296
|
+
extensionInjectedProvider: undefined
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const connector = new UniversalWalletConnector(mockNetworks, [
|
|
300
|
+
walletWithoutProvider
|
|
301
|
+
])
|
|
302
|
+
|
|
303
|
+
expect(connector.isConnectionModeAvailable('injected', 'metamask')).toBe(
|
|
304
|
+
false
|
|
305
|
+
)
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('should return false for injected when no installed namespace', () => {
|
|
309
|
+
const walletWithoutInstalledNamespace = {
|
|
310
|
+
...mockWallets[0],
|
|
311
|
+
extensionInjectedProvider: {
|
|
312
|
+
supportedNetworkIds: ['eip155:1'] as const,
|
|
313
|
+
namespaceMetaData: {
|
|
314
|
+
eip155: {
|
|
315
|
+
eip155Name: 'MetaMask',
|
|
316
|
+
injectedId: 'io.metamask',
|
|
317
|
+
supportsAddingNetworks: true,
|
|
318
|
+
requiresUserApprovalOnNetworkSwitch: false,
|
|
319
|
+
installed: false
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
requiresUserApprovalOnNamespaceSwitch: false
|
|
323
|
+
}
|
|
324
|
+
} as WalletMetadata
|
|
325
|
+
|
|
326
|
+
const connector = new UniversalWalletConnector(mockNetworks, [
|
|
327
|
+
walletWithoutInstalledNamespace
|
|
328
|
+
])
|
|
329
|
+
|
|
330
|
+
expect(connector.isConnectionModeAvailable('injected', 'metamask')).toBe(
|
|
331
|
+
false
|
|
332
|
+
)
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
it('should return true for injected when namespace is installed', () => {
|
|
336
|
+
const walletWithInstalledNamespace = {
|
|
337
|
+
...mockWallets[0],
|
|
338
|
+
extensionInjectedProvider: {
|
|
339
|
+
supportedNetworkIds: ['eip155:1'] as const,
|
|
340
|
+
namespaceMetaData: {
|
|
341
|
+
eip155: {
|
|
342
|
+
eip155Name: 'MetaMask',
|
|
343
|
+
injectedId: 'io.metamask',
|
|
344
|
+
supportsAddingNetworks: true,
|
|
345
|
+
requiresUserApprovalOnNetworkSwitch: false,
|
|
346
|
+
installed: true
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
requiresUserApprovalOnNamespaceSwitch: false
|
|
350
|
+
}
|
|
351
|
+
} as WalletMetadata
|
|
352
|
+
|
|
353
|
+
const connector = new UniversalWalletConnector(mockNetworks, [
|
|
354
|
+
walletWithInstalledNamespace
|
|
355
|
+
])
|
|
356
|
+
|
|
357
|
+
expect(connector.isConnectionModeAvailable('injected', 'metamask')).toBe(
|
|
358
|
+
true
|
|
359
|
+
)
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
it('should return false for unknown connection mode', () => {
|
|
363
|
+
const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
364
|
+
|
|
365
|
+
expect(
|
|
366
|
+
// @ts-expect-error Testing unknown connection mode
|
|
367
|
+
connector.isConnectionModeAvailable('unknown', 'metamask')
|
|
368
|
+
).toBe(false)
|
|
369
|
+
})
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
describe('basic functionality', () => {
|
|
373
|
+
it('should have all required methods', () => {
|
|
374
|
+
const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
375
|
+
|
|
376
|
+
expect(typeof connector.getSession).toBe('function')
|
|
377
|
+
expect(typeof connector.isReady).toBe('function')
|
|
378
|
+
expect(typeof connector.subscribe).toBe('function')
|
|
379
|
+
expect(typeof connector.getState).toBe('function')
|
|
380
|
+
expect(typeof connector.getWallets).toBe('function')
|
|
381
|
+
expect(typeof connector.getNetworks).toBe('function')
|
|
382
|
+
expect(typeof connector.connect).toBe('function')
|
|
383
|
+
expect(typeof connector.getConnectionURI).toBe('function')
|
|
384
|
+
expect(typeof connector.disconnect).toBe('function')
|
|
385
|
+
expect(typeof connector.switchNetwork).toBe('function')
|
|
386
|
+
expect(typeof connector.getNetworkSwitchLoadingState).toBe('function')
|
|
387
|
+
expect(typeof connector.signMessage).toBe('function')
|
|
388
|
+
expect(typeof connector.sendTransaction).toBe('function')
|
|
389
|
+
expect(typeof connector.isConnectionModeAvailable).toBe('function')
|
|
390
|
+
expect(typeof connector.getActiveWalletCapabilities).toBe('function')
|
|
391
|
+
})
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
describe('error handling', () => {
|
|
395
|
+
it('should handle signMessage errors gracefully', async () => {
|
|
396
|
+
const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
397
|
+
|
|
398
|
+
// This should not throw an error, but should handle the case where session manager is not properly mocked
|
|
399
|
+
await expect(connector.signMessage('test')).rejects.toThrow()
|
|
400
|
+
})
|
|
401
|
+
|
|
402
|
+
it('should handle sendTransaction errors gracefully', async () => {
|
|
403
|
+
const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
404
|
+
|
|
405
|
+
// This should not throw an error, but should handle the case where session manager is not properly mocked
|
|
406
|
+
await expect(
|
|
407
|
+
connector.sendTransaction({
|
|
408
|
+
to: '0x123',
|
|
409
|
+
value: '0x0',
|
|
410
|
+
data: '0x',
|
|
411
|
+
gas: '0x5208'
|
|
412
|
+
} as unknown as TransactionRequest)
|
|
413
|
+
).rejects.toThrow()
|
|
414
|
+
})
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
describe('wallet detection', () => {
|
|
418
|
+
it('should handle wallet detection gracefully', async () => {
|
|
419
|
+
const connector = new UniversalWalletConnector(mockNetworks, mockWallets)
|
|
420
|
+
|
|
421
|
+
// Wait for async initialization to complete
|
|
422
|
+
await new Promise(resolve => setTimeout(resolve, 0))
|
|
423
|
+
|
|
424
|
+
// Should complete without throwing
|
|
425
|
+
expect(connector.isReady()).toBe(true)
|
|
426
|
+
})
|
|
427
|
+
})
|
|
428
|
+
})
|