walletpair-sdk 1.0.0
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/LICENSE +21 -0
- package/README.md +415 -0
- package/dist/ble/framing.d.ts +23 -0
- package/dist/ble/framing.d.ts.map +1 -0
- package/dist/ble/framing.js +83 -0
- package/dist/ble/framing.js.map +1 -0
- package/dist/ble/index.d.ts +9 -0
- package/dist/ble/index.d.ts.map +1 -0
- package/dist/ble/index.js +9 -0
- package/dist/ble/index.js.map +1 -0
- package/dist/ble/web-ble-transport.d.ts +29 -0
- package/dist/ble/web-ble-transport.d.ts.map +1 -0
- package/dist/ble/web-ble-transport.js +93 -0
- package/dist/ble/web-ble-transport.js.map +1 -0
- package/dist/crypto.d.ts +102 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +279 -0
- package/dist/crypto.js.map +1 -0
- package/dist/dapp-session.d.ts +106 -0
- package/dist/dapp-session.d.ts.map +1 -0
- package/dist/dapp-session.js +918 -0
- package/dist/dapp-session.js.map +1 -0
- package/dist/emitter.d.ts +16 -0
- package/dist/emitter.d.ts.map +1 -0
- package/dist/emitter.js +41 -0
- package/dist/emitter.js.map +1 -0
- package/dist/evm/eip1193.d.ts +83 -0
- package/dist/evm/eip1193.d.ts.map +1 -0
- package/dist/evm/eip1193.js +270 -0
- package/dist/evm/eip1193.js.map +1 -0
- package/dist/evm/index.d.ts +8 -0
- package/dist/evm/index.d.ts.map +1 -0
- package/dist/evm/index.js +8 -0
- package/dist/evm/index.js.map +1 -0
- package/dist/evm/wagmi.d.ts +118 -0
- package/dist/evm/wagmi.d.ts.map +1 -0
- package/dist/evm/wagmi.js +205 -0
- package/dist/evm/wagmi.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +225 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +31 -0
- package/dist/types.js.map +1 -0
- package/dist/wallet-session.d.ts +107 -0
- package/dist/wallet-session.d.ts.map +1 -0
- package/dist/wallet-session.js +794 -0
- package/dist/wallet-session.js.map +1 -0
- package/dist/ws-transport.d.ts +29 -0
- package/dist/ws-transport.d.ts.map +1 -0
- package/dist/ws-transport.js +79 -0
- package/dist/ws-transport.js.map +1 -0
- package/package.json +55 -0
- package/src/__tests__/adversarial/crypto-attacks.test.ts +557 -0
- package/src/__tests__/adversarial/malicious-dapp.test.ts +505 -0
- package/src/__tests__/adversarial/malicious-relay.test.ts +528 -0
- package/src/__tests__/adversarial/malicious-wallet.test.ts +467 -0
- package/src/__tests__/spec-compliance/canonical-json.test.ts +227 -0
- package/src/__tests__/spec-compliance/crypto-vectors.test.ts +321 -0
- package/src/__tests__/spec-compliance/message-format.test.ts +356 -0
- package/src/__tests__/spec-compliance/sequence-numbers.test.ts +300 -0
- package/src/__tests__/spec-compliance/state-machine.test.ts +364 -0
- package/src/ble/framing.test.ts +196 -0
- package/src/ble/framing.ts +100 -0
- package/src/ble/index.ts +18 -0
- package/src/ble/web-ble-transport.test.ts +192 -0
- package/src/ble/web-ble-transport.ts +116 -0
- package/src/ble/web-bluetooth.d.ts +47 -0
- package/src/canonical-json.test.ts +612 -0
- package/src/crypto-directional.test.ts +263 -0
- package/src/crypto-hardening.test.ts +529 -0
- package/src/crypto.test.ts +635 -0
- package/src/crypto.ts +405 -0
- package/src/dapp-session.test.ts +647 -0
- package/src/dapp-session.ts +1004 -0
- package/src/emitter.test.ts +169 -0
- package/src/emitter.ts +45 -0
- package/src/evm/eip1193.test.ts +365 -0
- package/src/evm/eip1193.ts +346 -0
- package/src/evm/index.ts +19 -0
- package/src/evm/wagmi.test.ts +396 -0
- package/src/evm/wagmi.ts +321 -0
- package/src/index.ts +86 -0
- package/src/integration.test.ts +385 -0
- package/src/security.test.ts +430 -0
- package/src/sequence-validation.test.ts +1185 -0
- package/src/test-helpers.ts +216 -0
- package/src/types.test.ts +82 -0
- package/src/types.ts +305 -0
- package/src/wallet-session.test.ts +683 -0
- package/src/wallet-session.ts +922 -0
- package/src/ws-transport.test.ts +231 -0
- package/src/ws-transport.ts +92 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { generateX25519KeyPair, hexToBytes, sealPayload } from '../crypto.js'
|
|
3
|
+
import { DAppSession } from '../dapp-session.js'
|
|
4
|
+
import { MockTransport, makeJoinBody, parseSnapshot } from '../test-helpers.js'
|
|
5
|
+
import type { ProtocolMessage, RequestMessage } from '../types.js'
|
|
6
|
+
import { walletPair } from './wagmi.js'
|
|
7
|
+
|
|
8
|
+
function wait(ms = 0): Promise<void> {
|
|
9
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const meta = {
|
|
13
|
+
name: 'Test',
|
|
14
|
+
description: 'Test dApp',
|
|
15
|
+
url: 'https://test.com',
|
|
16
|
+
icon: 'https://test.com/icon.png',
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function makePersistedDappSession(): Promise<{
|
|
20
|
+
snapshot: string
|
|
21
|
+
channelId: string
|
|
22
|
+
walletPubKeyB64: string
|
|
23
|
+
walletToDappKey: Uint8Array
|
|
24
|
+
}> {
|
|
25
|
+
const transport = new MockTransport()
|
|
26
|
+
const session = new DAppSession({ transport, meta })
|
|
27
|
+
const walletKp = generateX25519KeyPair()
|
|
28
|
+
|
|
29
|
+
await session.createPairing()
|
|
30
|
+
const createMsg = transport.sent.find((m) => m.t === 'create')
|
|
31
|
+
if (!createMsg) throw new Error('create message was not sent')
|
|
32
|
+
|
|
33
|
+
transport.receive({
|
|
34
|
+
v: 1,
|
|
35
|
+
t: 'join',
|
|
36
|
+
ch: session.channelId,
|
|
37
|
+
ts: Date.now(),
|
|
38
|
+
from: walletKp.publicKeyB64,
|
|
39
|
+
body: makeJoinBody(session.channelId, createMsg.from, walletKp),
|
|
40
|
+
} as ProtocolMessage)
|
|
41
|
+
transport.receive({
|
|
42
|
+
v: 1,
|
|
43
|
+
t: 'ready',
|
|
44
|
+
ch: session.channelId,
|
|
45
|
+
ts: Date.now(),
|
|
46
|
+
from: '_adapter',
|
|
47
|
+
body: { state: 'connected', reconnect: false, remote: walletKp.publicKeyB64 },
|
|
48
|
+
} as ProtocolMessage)
|
|
49
|
+
|
|
50
|
+
const snapshot = session.serialize()
|
|
51
|
+
const snapshotData = parseSnapshot(snapshot) as { recvKey?: string | null }
|
|
52
|
+
if (!snapshotData.recvKey) throw new Error('snapshot missing recvKey')
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
snapshot,
|
|
56
|
+
channelId: session.channelId,
|
|
57
|
+
walletPubKeyB64: walletKp.publicKeyB64,
|
|
58
|
+
walletToDappKey: hexToBytes(snapshotData.recvKey),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe('walletPair connector factory', () => {
|
|
63
|
+
it('returns a CreateConnectorFn', () => {
|
|
64
|
+
const factory = walletPair({
|
|
65
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
66
|
+
meta: {
|
|
67
|
+
name: 'Test',
|
|
68
|
+
description: 'Test dApp',
|
|
69
|
+
url: 'https://test.com',
|
|
70
|
+
icon: 'https://test.com/icon.png',
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
expect(typeof factory).toBe('function')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('connector has correct id, name, type', () => {
|
|
77
|
+
const factory = walletPair({
|
|
78
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
79
|
+
meta: {
|
|
80
|
+
name: 'My dApp',
|
|
81
|
+
description: 'Test dApp',
|
|
82
|
+
url: 'https://test.com',
|
|
83
|
+
icon: 'https://test.com/icon.png',
|
|
84
|
+
},
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const emitter = {
|
|
88
|
+
emit: vi.fn(),
|
|
89
|
+
on: vi.fn(),
|
|
90
|
+
off: vi.fn(),
|
|
91
|
+
listenerCount: vi.fn(() => 0),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const connector = factory({
|
|
95
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
96
|
+
emitter,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
expect(connector.id).toBe('walletPair')
|
|
100
|
+
expect(connector.name).toBe('My dApp') // connector.name comes from meta.name
|
|
101
|
+
expect(connector.type).toBe('walletPair')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('connector name comes from meta.name', () => {
|
|
105
|
+
const factory = walletPair({
|
|
106
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
107
|
+
meta: {
|
|
108
|
+
name: 'WalletPair',
|
|
109
|
+
description: 'Test dApp',
|
|
110
|
+
url: 'https://test.com',
|
|
111
|
+
icon: 'https://test.com/icon.png',
|
|
112
|
+
},
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const connector = factory({
|
|
116
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
117
|
+
emitter: { emit: vi.fn(), on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
expect(connector.name).toBe('WalletPair')
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('connector has all required methods', () => {
|
|
124
|
+
const factory = walletPair({
|
|
125
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
126
|
+
meta: {
|
|
127
|
+
name: 'Test',
|
|
128
|
+
description: 'Test dApp',
|
|
129
|
+
url: 'https://test.com',
|
|
130
|
+
icon: 'https://test.com/icon.png',
|
|
131
|
+
},
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
const connector = factory({
|
|
135
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
136
|
+
emitter: { emit: vi.fn(), on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
expect(typeof connector.connect).toBe('function')
|
|
140
|
+
expect(typeof connector.disconnect).toBe('function')
|
|
141
|
+
expect(typeof connector.getAccounts).toBe('function')
|
|
142
|
+
expect(typeof connector.getChainId).toBe('function')
|
|
143
|
+
expect(typeof connector.getProvider).toBe('function')
|
|
144
|
+
expect(typeof connector.isAuthorized).toBe('function')
|
|
145
|
+
expect(typeof connector.onAccountsChanged).toBe('function')
|
|
146
|
+
expect(typeof connector.onChainChanged).toBe('function')
|
|
147
|
+
expect(typeof connector.onDisconnect).toBe('function')
|
|
148
|
+
expect(typeof connector.switchChain).toBe('function')
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('isAuthorized returns false with no storage', async () => {
|
|
152
|
+
const factory = walletPair({
|
|
153
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
154
|
+
meta: {
|
|
155
|
+
name: 'Test',
|
|
156
|
+
description: 'Test dApp',
|
|
157
|
+
url: 'https://test.com',
|
|
158
|
+
icon: 'https://test.com/icon.png',
|
|
159
|
+
},
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const connector = factory({
|
|
163
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
164
|
+
emitter: { emit: vi.fn(), on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
165
|
+
storage: null,
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
expect(await connector.isAuthorized()).toBe(false)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('isAuthorized returns false when no saved session', async () => {
|
|
172
|
+
const factory = walletPair({
|
|
173
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
174
|
+
meta: {
|
|
175
|
+
name: 'Test',
|
|
176
|
+
description: 'Test dApp',
|
|
177
|
+
url: 'https://test.com',
|
|
178
|
+
icon: 'https://test.com/icon.png',
|
|
179
|
+
},
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
const storage = {
|
|
183
|
+
getItem: vi.fn(() => Promise.resolve(null)),
|
|
184
|
+
setItem: vi.fn(() => Promise.resolve()),
|
|
185
|
+
removeItem: vi.fn(() => Promise.resolve()),
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const connector = factory({
|
|
189
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
190
|
+
emitter: { emit: vi.fn(), on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
191
|
+
storage,
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
expect(await connector.isAuthorized()).toBe(false)
|
|
195
|
+
expect(storage.getItem).toHaveBeenCalledWith('walletPair.session')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('connect({ isReconnecting: true }) restores and reconnects without a new pairing URI', async () => {
|
|
199
|
+
const saved = await makePersistedDappSession()
|
|
200
|
+
const transport = new MockTransport()
|
|
201
|
+
const onPairingUri = vi.fn()
|
|
202
|
+
const storage = {
|
|
203
|
+
getItem: vi.fn(() => Promise.resolve(saved.snapshot)),
|
|
204
|
+
setItem: vi.fn(() => Promise.resolve()),
|
|
205
|
+
removeItem: vi.fn(() => Promise.resolve()),
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const factory = walletPair({ transport, meta, onPairingUri })
|
|
209
|
+
const connector = factory({
|
|
210
|
+
chains: [{ id: 1, name: 'Ethereum' }] as const,
|
|
211
|
+
emitter: { emit: vi.fn(), on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
212
|
+
storage,
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
const connectResult = connector.connect({ isReconnecting: true })
|
|
216
|
+
await wait()
|
|
217
|
+
|
|
218
|
+
const createMsg = transport.sent.find((m) => m.t === 'create')
|
|
219
|
+
expect(createMsg).toBeTruthy()
|
|
220
|
+
expect(createMsg?.ch).toBe(saved.channelId)
|
|
221
|
+
expect(onPairingUri).not.toHaveBeenCalled()
|
|
222
|
+
|
|
223
|
+
transport.receive({
|
|
224
|
+
v: 1,
|
|
225
|
+
t: 'join',
|
|
226
|
+
ch: saved.channelId,
|
|
227
|
+
ts: Date.now(),
|
|
228
|
+
from: saved.walletPubKeyB64,
|
|
229
|
+
body: { sealed_join: null },
|
|
230
|
+
} as ProtocolMessage)
|
|
231
|
+
transport.receive({
|
|
232
|
+
v: 1,
|
|
233
|
+
t: 'ready',
|
|
234
|
+
ch: saved.channelId,
|
|
235
|
+
ts: Date.now(),
|
|
236
|
+
from: '_adapter',
|
|
237
|
+
body: { state: 'connected', reconnect: true, remote: saved.walletPubKeyB64 },
|
|
238
|
+
} as ProtocolMessage)
|
|
239
|
+
await wait()
|
|
240
|
+
|
|
241
|
+
const reqMsg = transport.sent.find((m): m is RequestMessage => m.t === 'req')
|
|
242
|
+
if (!reqMsg) throw new Error('reconnected session did not request accounts')
|
|
243
|
+
transport.receive({
|
|
244
|
+
v: 1,
|
|
245
|
+
t: 'res',
|
|
246
|
+
ch: saved.channelId,
|
|
247
|
+
ts: Date.now(),
|
|
248
|
+
from: saved.walletPubKeyB64,
|
|
249
|
+
body: {
|
|
250
|
+
id: reqMsg.body.id,
|
|
251
|
+
sealed: sealPayload(
|
|
252
|
+
saved.walletToDappKey,
|
|
253
|
+
saved.channelId,
|
|
254
|
+
0,
|
|
255
|
+
{ _ok: true, _result: ['0xabc'] },
|
|
256
|
+
{ type: 'res', from: saved.walletPubKeyB64, id: reqMsg.body.id },
|
|
257
|
+
),
|
|
258
|
+
},
|
|
259
|
+
} as ProtocolMessage)
|
|
260
|
+
|
|
261
|
+
await expect(connectResult).resolves.toEqual({ accounts: ['0xabc'], chainId: 1 })
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it('getProvider returns a WalletPairProvider', async () => {
|
|
265
|
+
const factory = walletPair({
|
|
266
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
267
|
+
meta: {
|
|
268
|
+
name: 'Test',
|
|
269
|
+
description: 'Test dApp',
|
|
270
|
+
url: 'https://test.com',
|
|
271
|
+
icon: 'https://test.com/icon.png',
|
|
272
|
+
},
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
const connector = factory({
|
|
276
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
277
|
+
emitter: { emit: vi.fn(), on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
const provider = await connector.getProvider()
|
|
281
|
+
expect(provider).toBeTruthy()
|
|
282
|
+
expect(typeof provider.request).toBe('function')
|
|
283
|
+
expect(typeof provider.on).toBe('function')
|
|
284
|
+
expect(typeof provider.removeListener).toBe('function')
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('onAccountsChanged emits change event', () => {
|
|
288
|
+
const factory = walletPair({
|
|
289
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
290
|
+
meta: {
|
|
291
|
+
name: 'Test',
|
|
292
|
+
description: 'Test dApp',
|
|
293
|
+
url: 'https://test.com',
|
|
294
|
+
icon: 'https://test.com/icon.png',
|
|
295
|
+
},
|
|
296
|
+
})
|
|
297
|
+
const emit = vi.fn()
|
|
298
|
+
|
|
299
|
+
const connector = factory({
|
|
300
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
301
|
+
emitter: { emit, on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
connector.onAccountsChanged(['0xabc'])
|
|
305
|
+
expect(emit).toHaveBeenCalledWith('change', { accounts: ['0xabc'] })
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('onChainChanged emits change event with numeric chainId', () => {
|
|
309
|
+
const factory = walletPair({
|
|
310
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
311
|
+
meta: {
|
|
312
|
+
name: 'Test',
|
|
313
|
+
description: 'Test dApp',
|
|
314
|
+
url: 'https://test.com',
|
|
315
|
+
icon: 'https://test.com/icon.png',
|
|
316
|
+
},
|
|
317
|
+
})
|
|
318
|
+
const emit = vi.fn()
|
|
319
|
+
|
|
320
|
+
const connector = factory({
|
|
321
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
322
|
+
emitter: { emit, on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
connector.onChainChanged('0x89') // 137
|
|
326
|
+
expect(emit).toHaveBeenCalledWith('change', { chainId: 137 })
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('onDisconnect emits disconnect event', () => {
|
|
330
|
+
const factory = walletPair({
|
|
331
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
332
|
+
meta: {
|
|
333
|
+
name: 'Test',
|
|
334
|
+
description: 'Test dApp',
|
|
335
|
+
url: 'https://test.com',
|
|
336
|
+
icon: 'https://test.com/icon.png',
|
|
337
|
+
},
|
|
338
|
+
})
|
|
339
|
+
const emit = vi.fn()
|
|
340
|
+
|
|
341
|
+
const connector = factory({
|
|
342
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
343
|
+
emitter: { emit, on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
connector.onDisconnect()
|
|
347
|
+
expect(emit).toHaveBeenCalledWith('disconnect', undefined)
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
it('disconnect cleans up session and storage', async () => {
|
|
351
|
+
const factory = walletPair({
|
|
352
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
353
|
+
meta: {
|
|
354
|
+
name: 'Test',
|
|
355
|
+
description: 'Test dApp',
|
|
356
|
+
url: 'https://test.com',
|
|
357
|
+
icon: 'https://test.com/icon.png',
|
|
358
|
+
},
|
|
359
|
+
})
|
|
360
|
+
const storage = {
|
|
361
|
+
getItem: vi.fn(() => Promise.resolve(null)),
|
|
362
|
+
setItem: vi.fn(() => Promise.resolve()),
|
|
363
|
+
removeItem: vi.fn(() => Promise.resolve()),
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const connector = factory({
|
|
367
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
368
|
+
emitter: { emit: vi.fn(), on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
369
|
+
storage,
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
await connector.disconnect()
|
|
373
|
+
expect(storage.removeItem).toHaveBeenCalledWith('walletPair.session')
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
it('switchChain throws for unconfigured chain', async () => {
|
|
377
|
+
const factory = walletPair({
|
|
378
|
+
relayUrl: 'ws://localhost:8080/v1',
|
|
379
|
+
meta: {
|
|
380
|
+
name: 'Test',
|
|
381
|
+
description: 'Test dApp',
|
|
382
|
+
url: 'https://test.com',
|
|
383
|
+
icon: 'https://test.com/icon.png',
|
|
384
|
+
},
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
const connector = factory({
|
|
388
|
+
chains: [{ id: 1, name: 'Ethereum' }] as any,
|
|
389
|
+
emitter: { emit: vi.fn(), on: vi.fn(), off: vi.fn(), listenerCount: vi.fn(() => 0) },
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
// switchChain will fail because session isn't connected, but the chain validation
|
|
393
|
+
// happens after the request. We test the error path.
|
|
394
|
+
await expect(connector.switchChain!({ chainId: 999 })).rejects.toThrow()
|
|
395
|
+
})
|
|
396
|
+
})
|