@meshconnect/uwc-core 0.2.13 → 0.2.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meshconnect/uwc-core",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
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.2.9",
20
+ "@meshconnect/uwc-types": "0.2.6",
19
21
  "@meshconnect/uwc-bridge-child": "0.0.3",
20
- "@meshconnect/uwc-types": "0.2.5",
21
- "@meshconnect/uwc-injected-connector": "0.2.8",
22
- "@meshconnect/uwc-wallet-connect-connector": "0.2.8"
22
+ "@meshconnect/uwc-wallet-connect-connector": "0.2.9"
23
23
  },
24
24
  "devDependencies": {
25
25
  "typescript": "^5.9.2"
@@ -0,0 +1,74 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { EventManager } from './event-manager'
3
+
4
+ describe('EventManager', () => {
5
+ let eventManager: EventManager
6
+
7
+ beforeEach(() => {
8
+ eventManager = new EventManager()
9
+ })
10
+ it('should subscribe and notify listeners', () => {
11
+ let eventReceived = false
12
+
13
+ const unsubscribe = eventManager.subscribe(() => {
14
+ eventReceived = true
15
+ })
16
+
17
+ eventManager.notify()
18
+
19
+ expect(eventReceived).toBe(true)
20
+
21
+ unsubscribe()
22
+ eventReceived = false
23
+
24
+ eventManager.notify()
25
+
26
+ expect(eventReceived).toBe(false)
27
+ })
28
+
29
+ it('should handle multiple subscribers', () => {
30
+ let subscriber1Called = false
31
+ let subscriber2Called = false
32
+
33
+ const unsubscribe1 = eventManager.subscribe(() => {
34
+ subscriber1Called = true
35
+ })
36
+
37
+ const unsubscribe2 = eventManager.subscribe(() => {
38
+ subscriber2Called = true
39
+ })
40
+
41
+ eventManager.notify()
42
+
43
+ expect(subscriber1Called).toBe(true)
44
+ expect(subscriber2Called).toBe(true)
45
+
46
+ unsubscribe1()
47
+ unsubscribe2()
48
+ })
49
+
50
+ it('should track listener count', () => {
51
+ expect(eventManager.getListenerCount()).toBe(0)
52
+
53
+ const unsubscribe1 = eventManager.subscribe(() => {})
54
+ expect(eventManager.getListenerCount()).toBe(1)
55
+
56
+ const unsubscribe2 = eventManager.subscribe(() => {})
57
+ expect(eventManager.getListenerCount()).toBe(2)
58
+
59
+ unsubscribe1()
60
+ expect(eventManager.getListenerCount()).toBe(1)
61
+
62
+ unsubscribe2()
63
+ expect(eventManager.getListenerCount()).toBe(0)
64
+ })
65
+
66
+ it('should clear all listeners', () => {
67
+ eventManager.subscribe(() => {})
68
+ eventManager.subscribe(() => {})
69
+ expect(eventManager.getListenerCount()).toBe(2)
70
+
71
+ eventManager.clearAllListeners()
72
+ expect(eventManager.getListenerCount()).toBe(0)
73
+ })
74
+ })
@@ -0,0 +1,119 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { SessionManager } from './session-manager'
3
+ import type { Network } from '../../../types/src'
4
+
5
+ describe('SessionManager', () => {
6
+ let sessionManager: SessionManager
7
+
8
+ beforeEach(() => {
9
+ sessionManager = new SessionManager()
10
+ })
11
+
12
+ it('should initialize with empty session', () => {
13
+ const session = sessionManager.getSession()
14
+ expect(session.connectionMode).toBeNull()
15
+ expect(session.activeWallet).toBeNull()
16
+ expect(session.activeNetwork).toBeNull()
17
+ expect(session.activeAddress).toBeNull()
18
+ expect(session.availableNetworks).toEqual([])
19
+ expect(session.availableAddresses).toEqual([])
20
+ })
21
+
22
+ it('should update session correctly', () => {
23
+ const updates = {
24
+ connectionMode: 'injected' as const,
25
+ activeAddress: '0x1234567890abcdef',
26
+ availableNetworks: [
27
+ {
28
+ id: 'eip155:1',
29
+ internalId: 1,
30
+ name: 'Ethereum Mainnet',
31
+ namespace: 'eip155',
32
+ logoUrl:
33
+ 'https://ethereum.org/static/6b935ac0e619424734bc5a3b11026273/6ed5f/eth-diamond-black.png',
34
+ rpcUrls: {
35
+ default: {
36
+ http: ['https://mainnet.infura.io/v3/test']
37
+ }
38
+ },
39
+ blockExplorers: {
40
+ default: {
41
+ name: 'Etherscan',
42
+ url: 'https://etherscan.io'
43
+ }
44
+ },
45
+ networkType: 'evm',
46
+ nativeCurrency: {
47
+ name: 'Ether',
48
+ symbol: 'ETH',
49
+ decimals: 18
50
+ }
51
+ }
52
+ ]
53
+ }
54
+
55
+ sessionManager.updateSession(updates)
56
+ const session = sessionManager.getSession()
57
+
58
+ expect(session.connectionMode).toBe('injected')
59
+ expect(session.activeAddress).toBe('0x1234567890abcdef')
60
+ expect(session.availableNetworks).toHaveLength(1)
61
+ expect(session.availableNetworks[0].name).toBe('Ethereum Mainnet')
62
+ })
63
+
64
+ it('should clear session correctly', () => {
65
+ // First update the session
66
+ sessionManager.updateSession({
67
+ connectionMode: 'injected',
68
+ activeAddress: '0x1234567890abcdef'
69
+ })
70
+
71
+ // Then clear it
72
+ sessionManager.clearSession()
73
+ const session = sessionManager.getSession()
74
+
75
+ expect(session.connectionMode).toBeNull()
76
+ expect(session.activeWallet).toBeNull()
77
+ expect(session.activeNetwork).toBeNull()
78
+ expect(session.activeAddress).toBeNull()
79
+ expect(session.availableNetworks).toEqual([])
80
+ expect(session.availableAddresses).toEqual([])
81
+ })
82
+
83
+ it('should set individual properties', () => {
84
+ const network: Network = {
85
+ id: 'eip155:1',
86
+ internalId: 1,
87
+ name: 'Ethereum Mainnet',
88
+ namespace: 'eip155',
89
+ logoUrl:
90
+ 'https://ethereum.org/static/6b935ac0e619424734bc5a3b11026273/6ed5f/eth-diamond-black.png',
91
+ rpcUrls: {
92
+ default: {
93
+ http: ['https://mainnet.infura.io/v3/test']
94
+ }
95
+ },
96
+ blockExplorers: {
97
+ default: {
98
+ name: 'Etherscan',
99
+ url: 'https://etherscan.io'
100
+ }
101
+ },
102
+ networkType: 'evm',
103
+ nativeCurrency: {
104
+ name: 'Ether',
105
+ symbol: 'ETH',
106
+ decimals: 18
107
+ }
108
+ }
109
+
110
+ sessionManager.setActiveNetwork(network)
111
+ sessionManager.setActiveAddress('0xabcdef1234567890')
112
+ sessionManager.setAvailableNetworks([network])
113
+
114
+ const session = sessionManager.getSession()
115
+ expect(session.activeNetwork).toEqual(network)
116
+ expect(session.activeAddress).toBe('0xabcdef1234567890')
117
+ expect(session.availableNetworks).toEqual([network])
118
+ })
119
+ })
@@ -0,0 +1,151 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
+ import { ConnectionService } from './connection-service'
3
+ import { SessionManager } from '../managers/session-manager'
4
+ import { EventManager } from '../managers/event-manager'
5
+ import type {
6
+ Network,
7
+ WalletMetadata,
8
+ ConnectionMode,
9
+ Connector
10
+ } from '../../../types/src'
11
+
12
+ describe('ConnectionService', () => {
13
+ let connectionService: ConnectionService
14
+ let sessionManager: SessionManager
15
+ let eventManager: EventManager
16
+ let mockConnector: Connector
17
+ let networks: Network[]
18
+ let wallets: WalletMetadata[]
19
+ let connectors: Map<ConnectionMode, Connector>
20
+
21
+ beforeEach(() => {
22
+ sessionManager = new SessionManager()
23
+ eventManager = new EventManager()
24
+
25
+ networks = [
26
+ {
27
+ id: 'eip155:1',
28
+ internalId: 1,
29
+ name: 'Ethereum Mainnet',
30
+ namespace: 'eip155',
31
+ logoUrl:
32
+ 'https://ethereum.org/static/6b935ac0e619424734bc5a3b11026273/6ed5f/eth-diamond-black.png',
33
+ rpcUrls: {
34
+ default: {
35
+ http: ['https://mainnet.infura.io/v3/test']
36
+ }
37
+ },
38
+ blockExplorers: {
39
+ default: {
40
+ name: 'Etherscan',
41
+ url: 'https://etherscan.io'
42
+ }
43
+ },
44
+ networkType: 'evm',
45
+ nativeCurrency: {
46
+ name: 'Ether',
47
+ symbol: 'ETH',
48
+ decimals: 18
49
+ }
50
+ }
51
+ ]
52
+
53
+ wallets = [
54
+ {
55
+ id: 'metamask',
56
+ name: 'MetaMask',
57
+ metadata: {
58
+ icon: 'data:image/svg+xml;base64,test',
59
+ downloadUrl: 'https://metamask.io',
60
+ supportedChains: ['eip155:1']
61
+ },
62
+ extensionInjectedProvider: {} as any,
63
+ walletConnectProvider: {} as any
64
+ }
65
+ ]
66
+
67
+ mockConnector = {
68
+ connect: vi.fn(),
69
+ disconnect: vi.fn(),
70
+ signMessage: vi.fn(),
71
+ sendTransaction: vi.fn(),
72
+ switchNetwork: vi.fn()
73
+ }
74
+
75
+ connectors = new Map()
76
+ connectors.set('injected', mockConnector)
77
+
78
+ connectionService = new ConnectionService(
79
+ networks,
80
+ wallets,
81
+ sessionManager,
82
+ connectors,
83
+ false,
84
+ eventManager
85
+ )
86
+ })
87
+
88
+ it('should initialize with provided parameters', () => {
89
+ expect(connectionService).toBeDefined()
90
+ })
91
+
92
+ it('should find wallet by id', () => {
93
+ const wallet = connectionService['findWallet']('metamask')
94
+ expect(wallet).toEqual(wallets[0])
95
+ })
96
+
97
+ it('should throw error for unknown wallet', () => {
98
+ expect(() => {
99
+ connectionService['findWallet']('unknown')
100
+ }).toThrow("Wallet with id 'unknown' not found")
101
+ })
102
+
103
+ it('should validate network exists', () => {
104
+ const mockProvider = {
105
+ supportedNetworkIds: ['eip155:1']
106
+ }
107
+ const network = connectionService['validateAndDetermineNetwork'](
108
+ 'eip155:1',
109
+ mockProvider as any
110
+ )
111
+ expect(network).toEqual(networks[0])
112
+ })
113
+
114
+ it('should throw error for unknown network', () => {
115
+ const mockProvider = {
116
+ supportedNetworkIds: ['eip155:1']
117
+ }
118
+ expect(() => {
119
+ connectionService['validateAndDetermineNetwork'](
120
+ 'eip155:999',
121
+ mockProvider as any
122
+ )
123
+ }).toThrow('Network eip155:999 is not in the configured networks')
124
+ })
125
+
126
+ it('should get connector by mode', () => {
127
+ const connector = connectionService['getConnector']('injected')
128
+ expect(connector).toBe(mockConnector)
129
+ })
130
+
131
+ it('should throw error for unknown connector mode', () => {
132
+ expect(() => {
133
+ connectionService['getConnector']('unknown' as ConnectionMode)
134
+ }).toThrow('No connector found for connection mode: unknown')
135
+ })
136
+
137
+ it('should get provider for injected mode', () => {
138
+ const wallet = wallets[0]
139
+ const provider = connectionService['getProviderForMode'](wallet, 'injected')
140
+ expect(provider).toBeDefined()
141
+ })
142
+
143
+ it('should get provider for walletConnect mode', () => {
144
+ const wallet = wallets[0]
145
+ const provider = connectionService['getProviderForMode'](
146
+ wallet,
147
+ 'walletConnect'
148
+ )
149
+ expect(provider).toBeDefined()
150
+ })
151
+ })
@@ -0,0 +1,187 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
+ import { NetworkSwitchService } from './network-switch-service'
3
+ import { SessionManager } from '../managers/session-manager'
4
+ import { EventManager } from '../managers/event-manager'
5
+ import type { Network, ConnectionMode, Connector } from '../../../types/src'
6
+
7
+ describe('NetworkSwitchService', () => {
8
+ let networkSwitchService: NetworkSwitchService
9
+ let sessionManager: SessionManager
10
+ let eventManager: EventManager
11
+ let mockConnector: Connector
12
+ let networks: Network[]
13
+ let connectors: Map<ConnectionMode, Connector>
14
+
15
+ beforeEach(() => {
16
+ sessionManager = new SessionManager()
17
+ eventManager = new EventManager()
18
+
19
+ networks = [
20
+ {
21
+ id: 'eip155:1',
22
+ internalId: 1,
23
+ name: 'Ethereum Mainnet',
24
+ namespace: 'eip155',
25
+ logoUrl:
26
+ 'https://ethereum.org/static/6b935ac0e619424734bc5a3b11026273/6ed5f/eth-diamond-black.png',
27
+ rpcUrls: {
28
+ default: {
29
+ http: ['https://mainnet.infura.io/v3/test']
30
+ }
31
+ },
32
+ blockExplorers: {
33
+ default: {
34
+ name: 'Etherscan',
35
+ url: 'https://etherscan.io'
36
+ }
37
+ },
38
+ networkType: 'evm',
39
+ nativeCurrency: {
40
+ name: 'Ether',
41
+ symbol: 'ETH',
42
+ decimals: 18
43
+ }
44
+ },
45
+ {
46
+ id: 'eip155:137',
47
+ internalId: 137,
48
+ name: 'Polygon',
49
+ namespace: 'eip155',
50
+ logoUrl: 'https://polygon.technology/static/polygon-logo.png',
51
+ rpcUrls: {
52
+ default: {
53
+ http: ['https://polygon-rpc.com']
54
+ }
55
+ },
56
+ blockExplorers: {
57
+ default: {
58
+ name: 'Polygonscan',
59
+ url: 'https://polygonscan.com'
60
+ }
61
+ },
62
+ networkType: 'evm',
63
+ nativeCurrency: {
64
+ name: 'MATIC',
65
+ symbol: 'MATIC',
66
+ decimals: 18
67
+ }
68
+ }
69
+ ]
70
+
71
+ mockConnector = {
72
+ connect: vi.fn(),
73
+ disconnect: vi.fn(),
74
+ signMessage: vi.fn(),
75
+ sendTransaction: vi.fn(),
76
+ switchNetwork: vi.fn()
77
+ }
78
+
79
+ connectors = new Map()
80
+ connectors.set('injected', mockConnector)
81
+
82
+ networkSwitchService = new NetworkSwitchService(
83
+ networks,
84
+ sessionManager,
85
+ connectors,
86
+ false,
87
+ eventManager
88
+ )
89
+ })
90
+
91
+ it('should initialize with provided parameters', () => {
92
+ expect(networkSwitchService).toBeDefined()
93
+ })
94
+
95
+ it('should return loading state', () => {
96
+ const state = networkSwitchService.getLoadingState()
97
+ expect(state).toEqual({
98
+ isLoading: false,
99
+ isWaitingForUserApproval: false
100
+ })
101
+ })
102
+
103
+ it('should throw error when no active connection', async () => {
104
+ await expect(
105
+ networkSwitchService.switchNetwork('eip155:1')
106
+ ).rejects.toThrow('No active wallet found in session')
107
+ })
108
+
109
+ it('should throw error for unknown network', async () => {
110
+ sessionManager.updateSession({
111
+ connectionMode: 'injected',
112
+ activeWallet: {
113
+ id: 'metamask',
114
+ name: 'MetaMask',
115
+ metadata: {
116
+ icon: 'test',
117
+ downloadUrl: 'test',
118
+ supportedChains: ['eip155:1']
119
+ }
120
+ }
121
+ })
122
+
123
+ await expect(
124
+ networkSwitchService.switchNetwork('eip155:999')
125
+ ).rejects.toThrow("Network with id 'eip155:999' not found")
126
+ })
127
+
128
+ it('should throw error when no active wallet', async () => {
129
+ sessionManager.updateSession({
130
+ connectionMode: 'injected'
131
+ })
132
+
133
+ await expect(
134
+ networkSwitchService.switchNetwork('eip155:1')
135
+ ).rejects.toThrow('No active wallet found in session')
136
+ })
137
+
138
+ it('should throw error when wallet does not support network', async () => {
139
+ sessionManager.updateSession({
140
+ connectionMode: 'injected',
141
+ activeWallet: {
142
+ id: 'metamask',
143
+ name: 'MetaMask',
144
+ metadata: {
145
+ icon: 'test',
146
+ downloadUrl: 'test',
147
+ supportedChains: ['eip155:1']
148
+ }
149
+ }
150
+ })
151
+
152
+ await expect(
153
+ networkSwitchService.switchNetwork('eip155:137')
154
+ ).rejects.toThrow(
155
+ "Wallet 'metamask' does not have a provider for injected mode"
156
+ )
157
+ })
158
+
159
+ it('should throw error when connector does not support network switching', async () => {
160
+ const connectorWithoutSwitch = {
161
+ connect: vi.fn(),
162
+ disconnect: vi.fn(),
163
+ signMessage: vi.fn(),
164
+ sendTransaction: vi.fn()
165
+ }
166
+ connectors.set('injected', connectorWithoutSwitch)
167
+
168
+ sessionManager.updateSession({
169
+ connectionMode: 'injected',
170
+ activeWallet: {
171
+ id: 'metamask',
172
+ name: 'MetaMask',
173
+ metadata: {
174
+ icon: 'test',
175
+ downloadUrl: 'test',
176
+ supportedChains: ['eip155:1']
177
+ }
178
+ }
179
+ })
180
+
181
+ await expect(
182
+ networkSwitchService.switchNetwork('eip155:1')
183
+ ).rejects.toThrow(
184
+ "Wallet 'metamask' does not have a provider for injected mode"
185
+ )
186
+ })
187
+ })
@@ -0,0 +1,108 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest'
2
+ import { SignatureService } from './signature-service'
3
+ import { SessionManager } from '../managers/session-manager'
4
+ import type { ConnectionMode, Connector } from '../../../types/src'
5
+
6
+ describe('SignatureService', () => {
7
+ let signatureService: SignatureService
8
+ let sessionManager: SessionManager
9
+ let mockConnector: Connector
10
+ let connectors: Map<ConnectionMode, Connector>
11
+
12
+ beforeEach(() => {
13
+ sessionManager = new SessionManager()
14
+
15
+ mockConnector = {
16
+ connect: vi.fn(),
17
+ disconnect: vi.fn(),
18
+ signMessage: vi.fn().mockResolvedValue('0xsignature'),
19
+ sendTransaction: vi.fn(),
20
+ switchNetwork: vi.fn()
21
+ }
22
+
23
+ connectors = new Map()
24
+ connectors.set('injected', mockConnector)
25
+
26
+ signatureService = new SignatureService(sessionManager, connectors)
27
+ })
28
+
29
+ it('should initialize with provided parameters', () => {
30
+ expect(signatureService).toBeDefined()
31
+ })
32
+
33
+ it('should throw error when no active connection', async () => {
34
+ await expect(signatureService.signMessage('test message')).rejects.toThrow(
35
+ 'No active connection'
36
+ )
37
+ })
38
+
39
+ it('should throw error when no active address', async () => {
40
+ sessionManager.updateSession({
41
+ connectionMode: 'injected'
42
+ })
43
+
44
+ await expect(signatureService.signMessage('test message')).rejects.toThrow(
45
+ 'No active wallet address'
46
+ )
47
+ })
48
+
49
+ it('should throw error when connector not found', async () => {
50
+ sessionManager.updateSession({
51
+ connectionMode: 'unknown' as ConnectionMode,
52
+ activeAddress: '0x123'
53
+ })
54
+
55
+ await expect(signatureService.signMessage('test message')).rejects.toThrow(
56
+ 'No connector found for connection mode: unknown'
57
+ )
58
+ })
59
+
60
+ it('should throw error when connector does not support signing', async () => {
61
+ const connectorWithoutSign = {
62
+ connect: vi.fn(),
63
+ disconnect: vi.fn(),
64
+ sendTransaction: vi.fn(),
65
+ switchNetwork: vi.fn()
66
+ }
67
+ connectors.set('injected', connectorWithoutSign)
68
+
69
+ sessionManager.updateSession({
70
+ connectionMode: 'injected',
71
+ activeAddress: '0x123'
72
+ })
73
+
74
+ await expect(signatureService.signMessage('test message')).rejects.toThrow(
75
+ 'Connector does not support message signing'
76
+ )
77
+ })
78
+
79
+ it('should sign message successfully', async () => {
80
+ sessionManager.updateSession({
81
+ connectionMode: 'injected',
82
+ activeAddress: '0x123'
83
+ })
84
+
85
+ const provider = {} as any
86
+ const result = await signatureService.signMessage('test message', provider)
87
+ expect(result).toBe('0xsignature')
88
+ expect(mockConnector.signMessage).toHaveBeenCalledWith(
89
+ 'test message',
90
+ provider
91
+ )
92
+ })
93
+
94
+ it('should sign message with provider', async () => {
95
+ sessionManager.updateSession({
96
+ connectionMode: 'injected',
97
+ activeAddress: '0x123'
98
+ })
99
+
100
+ const provider = {} as any
101
+ const result = await signatureService.signMessage('test message', provider)
102
+ expect(result).toBe('0xsignature')
103
+ expect(mockConnector.signMessage).toHaveBeenCalledWith(
104
+ 'test message',
105
+ provider
106
+ )
107
+ })
108
+ })