walletpair-sdk 1.0.3 → 1.0.5
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 +13 -0
- package/dist/ble/framing.d.ts.map +1 -1
- package/dist/ble/framing.js +2 -2
- package/dist/ble/framing.js.map +1 -1
- package/dist/ble/index.d.ts +2 -2
- package/dist/ble/index.d.ts.map +1 -1
- package/dist/ble/index.js +2 -2
- package/dist/ble/index.js.map +1 -1
- package/dist/ble/web-ble-transport.d.ts +1 -1
- package/dist/ble/web-ble-transport.d.ts.map +1 -1
- package/dist/ble/web-ble-transport.js +23 -12
- package/dist/ble/web-ble-transport.js.map +1 -1
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +29 -12
- package/dist/crypto.js.map +1 -1
- package/dist/dapp-session.d.ts.map +1 -1
- package/dist/dapp-session.js +15 -5
- package/dist/dapp-session.js.map +1 -1
- package/dist/emitter.d.ts +1 -3
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +4 -2
- package/dist/emitter.js.map +1 -1
- package/dist/evm/eip1193.d.ts +2 -2
- package/dist/evm/eip1193.d.ts.map +1 -1
- package/dist/evm/eip1193.js +32 -18
- package/dist/evm/eip1193.js.map +1 -1
- package/dist/evm/index.d.ts +2 -2
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js.map +1 -1
- package/dist/wallet-session.d.ts.map +1 -1
- package/dist/wallet-session.js +4 -3
- package/dist/wallet-session.js.map +1 -1
- package/dist/ws-transport.d.ts +1 -1
- package/dist/ws-transport.d.ts.map +1 -1
- package/dist/ws-transport.js +12 -4
- package/dist/ws-transport.js.map +1 -1
- package/package.json +20 -1
- package/src/__tests__/adversarial/crypto-attacks.test.ts +240 -233
- package/src/__tests__/adversarial/malicious-dapp.test.ts +228 -194
- package/src/__tests__/adversarial/malicious-relay.test.ts +292 -220
- package/src/__tests__/adversarial/malicious-wallet.test.ts +246 -180
- package/src/__tests__/spec-compliance/canonical-json.test.ts +105 -105
- package/src/__tests__/spec-compliance/crypto-vectors.test.ts +149 -154
- package/src/__tests__/spec-compliance/message-format.test.ts +180 -151
- package/src/__tests__/spec-compliance/sequence-numbers.test.ts +142 -149
- package/src/__tests__/spec-compliance/state-machine.test.ts +203 -180
- package/src/ble/framing.test.ts +122 -114
- package/src/ble/framing.ts +48 -51
- package/src/ble/index.ts +7 -7
- package/src/ble/web-ble-transport.test.ts +93 -84
- package/src/ble/web-ble-transport.ts +70 -57
- package/src/ble/web-bluetooth.d.ts +19 -19
- package/src/canonical-json.test.ts +301 -285
- package/src/crypto-directional.test.ts +155 -129
- package/src/crypto-hardening.test.ts +292 -283
- package/src/crypto.test.ts +364 -346
- package/src/crypto.ts +185 -175
- package/src/dapp-session.test.ts +522 -385
- package/src/dapp-session.ts +17 -11
- package/src/emitter.test.ts +122 -122
- package/src/emitter.ts +20 -18
- package/src/evm/eip1193.test.ts +283 -205
- package/src/evm/eip1193.ts +162 -138
- package/src/evm/index.ts +5 -5
- package/src/evm/wagmi.test.ts +1 -1
- package/src/integration.test.ts +329 -201
- package/src/security.test.ts +331 -238
- package/src/sequence-validation.test.ts +6 -9
- package/src/test-helpers.ts +102 -78
- package/src/types.test.ts +45 -50
- package/src/wallet-session.test.ts +611 -383
- package/src/wallet-session.ts +7 -9
- package/src/ws-transport.test.ts +141 -139
- package/src/ws-transport.ts +51 -41
package/src/evm/eip1193.test.ts
CHANGED
|
@@ -1,71 +1,91 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import {
|
|
3
|
-
import { DAppSession } from '../dapp-session.js';
|
|
4
|
-
import { makeJoinBody, MockTransport } from '../test-helpers.js';
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import type { AadHeader } from '../crypto.js'
|
|
5
3
|
import {
|
|
6
|
-
|
|
4
|
+
b64urlDecode,
|
|
7
5
|
computeSharedSecret,
|
|
8
6
|
deriveSessionKey,
|
|
7
|
+
generateX25519KeyPair,
|
|
9
8
|
sealPayload,
|
|
10
|
-
|
|
11
|
-
} from '../
|
|
12
|
-
import
|
|
13
|
-
import type { ProtocolMessage } from '../types.js'
|
|
9
|
+
} from '../crypto.js'
|
|
10
|
+
import { DAppSession } from '../dapp-session.js'
|
|
11
|
+
import { MockTransport, makeJoinBody } from '../test-helpers.js'
|
|
12
|
+
import type { ProtocolMessage } from '../types.js'
|
|
13
|
+
import { WalletPairProvider } from './eip1193.js'
|
|
14
14
|
|
|
15
15
|
function flushMicrotasks(): Promise<void> {
|
|
16
|
-
return new Promise((r) => setTimeout(r, 10))
|
|
16
|
+
return new Promise((r) => setTimeout(r, 10))
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
describe('WalletPairProvider', () => {
|
|
20
|
-
let transport: MockTransport
|
|
21
|
-
let session: DAppSession
|
|
22
|
-
let provider: WalletPairProvider
|
|
23
|
-
let walletKp: ReturnType<typeof generateX25519KeyPair
|
|
24
|
-
let sessionKey: Uint8Array
|
|
20
|
+
let transport: MockTransport
|
|
21
|
+
let session: DAppSession
|
|
22
|
+
let provider: WalletPairProvider
|
|
23
|
+
let walletKp: ReturnType<typeof generateX25519KeyPair>
|
|
24
|
+
let sessionKey: Uint8Array
|
|
25
25
|
|
|
26
26
|
async function setupConnectedSession(chainId = 1) {
|
|
27
|
-
walletSendSeq = 0
|
|
28
|
-
transport = new MockTransport()
|
|
29
|
-
session = new DAppSession({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
walletSendSeq = 0
|
|
28
|
+
transport = new MockTransport()
|
|
29
|
+
session = new DAppSession({
|
|
30
|
+
transport,
|
|
31
|
+
meta: {
|
|
32
|
+
name: 'Test',
|
|
33
|
+
description: 'Test dApp',
|
|
34
|
+
url: 'https://test.com',
|
|
35
|
+
icon: 'https://test.com/icon.png',
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
provider = new WalletPairProvider({ session, chainId })
|
|
39
|
+
|
|
40
|
+
await session.createPairing()
|
|
41
|
+
walletKp = generateX25519KeyPair()
|
|
34
42
|
|
|
35
43
|
// Join
|
|
36
44
|
transport.receive({
|
|
37
|
-
v: 1,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
v: 1,
|
|
46
|
+
t: 'join',
|
|
47
|
+
ch: session.channelId,
|
|
48
|
+
ts: Date.now(),
|
|
49
|
+
from: walletKp.publicKeyB64,
|
|
50
|
+
body: makeJoinBody(session.channelId, transport.sent[0]?.from!, walletKp),
|
|
51
|
+
} as ProtocolMessage)
|
|
41
52
|
|
|
42
53
|
// Responses/events from the manual wallet use the wallet->dApp key.
|
|
43
|
-
const dappPub = b64urlDecode(transport.sent[0]
|
|
44
|
-
const shared = computeSharedSecret(walletKp.privateKey, dappPub)
|
|
45
|
-
deriveSessionKey(shared, session.channelId)
|
|
46
|
-
sessionKey = (session as any).recvKey
|
|
54
|
+
const dappPub = b64urlDecode(transport.sent[0]?.from!)
|
|
55
|
+
const shared = computeSharedSecret(walletKp.privateKey, dappPub)
|
|
56
|
+
deriveSessionKey(shared, session.channelId)
|
|
57
|
+
sessionKey = (session as any).recvKey
|
|
47
58
|
|
|
48
59
|
// Connect
|
|
49
60
|
transport.receive({
|
|
50
|
-
v: 1,
|
|
51
|
-
|
|
61
|
+
v: 1,
|
|
62
|
+
t: 'ready',
|
|
63
|
+
ch: session.channelId,
|
|
64
|
+
ts: Date.now(),
|
|
65
|
+
from: '_adapter',
|
|
52
66
|
body: { state: 'connected', reconnect: false, remote: walletKp.publicKeyB64 },
|
|
53
|
-
} as ProtocolMessage)
|
|
67
|
+
} as ProtocolMessage)
|
|
54
68
|
}
|
|
55
69
|
|
|
56
|
-
let walletSendSeq = 0
|
|
70
|
+
let walletSendSeq = 0
|
|
57
71
|
|
|
58
72
|
function respondToLatestReq(result: unknown, ok = true) {
|
|
59
|
-
const reqMsg = [...transport.sent].reverse().find(m => m.t === 'req') as any
|
|
60
|
-
if (!reqMsg) throw new Error('No req found')
|
|
61
|
-
const reqId = reqMsg.body.id
|
|
62
|
-
const hdr: AadHeader = { type: 'res', from: walletKp.publicKeyB64, id: reqId }
|
|
63
|
-
const sealedData = ok ? { _ok: true, _result: result } : { _ok: false, ...result as object }
|
|
73
|
+
const reqMsg = [...transport.sent].reverse().find((m) => m.t === 'req') as any
|
|
74
|
+
if (!reqMsg) throw new Error('No req found')
|
|
75
|
+
const reqId = reqMsg.body.id
|
|
76
|
+
const hdr: AadHeader = { type: 'res', from: walletKp.publicKeyB64, id: reqId }
|
|
77
|
+
const sealedData = ok ? { _ok: true, _result: result } : { _ok: false, ...(result as object) }
|
|
64
78
|
transport.receive({
|
|
65
|
-
v: 1,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
v: 1,
|
|
80
|
+
t: 'res',
|
|
81
|
+
ch: session.channelId,
|
|
82
|
+
ts: Date.now(),
|
|
83
|
+
from: walletKp.publicKeyB64,
|
|
84
|
+
body: {
|
|
85
|
+
id: reqId,
|
|
86
|
+
sealed: sealPayload(sessionKey, session.channelId, walletSendSeq++, sealedData, hdr),
|
|
87
|
+
},
|
|
88
|
+
} as ProtocolMessage)
|
|
69
89
|
}
|
|
70
90
|
|
|
71
91
|
// -----------------------------------------------------------------------
|
|
@@ -74,25 +94,25 @@ describe('WalletPairProvider', () => {
|
|
|
74
94
|
|
|
75
95
|
describe('eth_chainId', () => {
|
|
76
96
|
it('returns chain ID as hex (default mainnet)', async () => {
|
|
77
|
-
await setupConnectedSession()
|
|
78
|
-
const result = await provider.request({ method: 'eth_chainId' })
|
|
79
|
-
expect(result).toBe('0x1')
|
|
80
|
-
})
|
|
97
|
+
await setupConnectedSession()
|
|
98
|
+
const result = await provider.request({ method: 'eth_chainId' })
|
|
99
|
+
expect(result).toBe('0x1')
|
|
100
|
+
})
|
|
81
101
|
|
|
82
102
|
it('returns custom chain ID', async () => {
|
|
83
|
-
await setupConnectedSession(137)
|
|
84
|
-
const result = await provider.request({ method: 'eth_chainId' })
|
|
85
|
-
expect(result).toBe('0x89')
|
|
86
|
-
})
|
|
87
|
-
})
|
|
103
|
+
await setupConnectedSession(137)
|
|
104
|
+
const result = await provider.request({ method: 'eth_chainId' })
|
|
105
|
+
expect(result).toBe('0x89')
|
|
106
|
+
})
|
|
107
|
+
})
|
|
88
108
|
|
|
89
109
|
describe('net_version', () => {
|
|
90
110
|
it('returns chain ID as decimal string', async () => {
|
|
91
|
-
await setupConnectedSession()
|
|
92
|
-
const result = await provider.request({ method: 'net_version' })
|
|
93
|
-
expect(result).toBe('1')
|
|
94
|
-
})
|
|
95
|
-
})
|
|
111
|
+
await setupConnectedSession()
|
|
112
|
+
const result = await provider.request({ method: 'net_version' })
|
|
113
|
+
expect(result).toBe('1')
|
|
114
|
+
})
|
|
115
|
+
})
|
|
96
116
|
|
|
97
117
|
// -----------------------------------------------------------------------
|
|
98
118
|
// eth_requestAccounts / eth_accounts
|
|
@@ -100,47 +120,47 @@ describe('WalletPairProvider', () => {
|
|
|
100
120
|
|
|
101
121
|
describe('eth_requestAccounts', () => {
|
|
102
122
|
it('maps to wallet_getAccounts and returns result', async () => {
|
|
103
|
-
await setupConnectedSession()
|
|
123
|
+
await setupConnectedSession()
|
|
104
124
|
|
|
105
|
-
const promise = provider.request({ method: 'eth_requestAccounts' })
|
|
106
|
-
await flushMicrotasks()
|
|
125
|
+
const promise = provider.request({ method: 'eth_requestAccounts' })
|
|
126
|
+
await flushMicrotasks()
|
|
107
127
|
|
|
108
128
|
// Verify it sent wallet_getAccounts
|
|
109
|
-
const reqMsg = transport.sent.find(m => m.t === 'req') as any
|
|
110
|
-
expect(reqMsg.body.sealed).toBeTruthy()
|
|
129
|
+
const reqMsg = transport.sent.find((m) => m.t === 'req') as any
|
|
130
|
+
expect(reqMsg.body.sealed).toBeTruthy() // method inside sealed
|
|
111
131
|
|
|
112
|
-
respondToLatestReq(['0xabc123'])
|
|
113
|
-
const result = await promise
|
|
114
|
-
expect(result).toEqual(['0xabc123'])
|
|
115
|
-
})
|
|
132
|
+
respondToLatestReq(['0xabc123'])
|
|
133
|
+
const result = await promise
|
|
134
|
+
expect(result).toEqual(['0xabc123'])
|
|
135
|
+
})
|
|
116
136
|
|
|
117
137
|
it('caches accounts after request', async () => {
|
|
118
|
-
await setupConnectedSession()
|
|
138
|
+
await setupConnectedSession()
|
|
119
139
|
|
|
120
|
-
const promise = provider.request({ method: 'eth_requestAccounts' })
|
|
121
|
-
await flushMicrotasks()
|
|
122
|
-
respondToLatestReq(['0xabc123'])
|
|
123
|
-
await promise
|
|
140
|
+
const promise = provider.request({ method: 'eth_requestAccounts' })
|
|
141
|
+
await flushMicrotasks()
|
|
142
|
+
respondToLatestReq(['0xabc123'])
|
|
143
|
+
await promise
|
|
124
144
|
|
|
125
|
-
expect(provider.getAccounts()).toEqual(['0xabc123'])
|
|
126
|
-
})
|
|
127
|
-
})
|
|
145
|
+
expect(provider.getAccounts()).toEqual(['0xabc123'])
|
|
146
|
+
})
|
|
147
|
+
})
|
|
128
148
|
|
|
129
149
|
describe('eth_accounts', () => {
|
|
130
150
|
it('maps to wallet_getAccounts', async () => {
|
|
131
|
-
await setupConnectedSession()
|
|
151
|
+
await setupConnectedSession()
|
|
132
152
|
|
|
133
|
-
const promise = provider.request({ method: 'eth_accounts' })
|
|
134
|
-
await flushMicrotasks()
|
|
153
|
+
const promise = provider.request({ method: 'eth_accounts' })
|
|
154
|
+
await flushMicrotasks()
|
|
135
155
|
|
|
136
|
-
const reqMsg = transport.sent.find(m => m.t === 'req') as any
|
|
137
|
-
expect(reqMsg.body.sealed).toBeTruthy()
|
|
156
|
+
const reqMsg = transport.sent.find((m) => m.t === 'req') as any
|
|
157
|
+
expect(reqMsg.body.sealed).toBeTruthy() // method inside sealed
|
|
138
158
|
|
|
139
|
-
respondToLatestReq(['0x456'])
|
|
140
|
-
const result = await promise
|
|
141
|
-
expect(result).toEqual(['0x456'])
|
|
142
|
-
})
|
|
143
|
-
})
|
|
159
|
+
respondToLatestReq(['0x456'])
|
|
160
|
+
const result = await promise
|
|
161
|
+
expect(result).toEqual(['0x456'])
|
|
162
|
+
})
|
|
163
|
+
})
|
|
144
164
|
|
|
145
165
|
// -----------------------------------------------------------------------
|
|
146
166
|
// personal_sign
|
|
@@ -148,41 +168,41 @@ describe('WalletPairProvider', () => {
|
|
|
148
168
|
|
|
149
169
|
describe('personal_sign', () => {
|
|
150
170
|
it('maps hex data to wallet_signMessage with decoded text', async () => {
|
|
151
|
-
await setupConnectedSession()
|
|
171
|
+
await setupConnectedSession()
|
|
152
172
|
|
|
153
173
|
// 0x48656c6c6f is "Hello" in hex
|
|
154
174
|
const promise = provider.request({
|
|
155
175
|
method: 'personal_sign',
|
|
156
176
|
params: ['0x48656c6c6f', '0xabc'],
|
|
157
|
-
})
|
|
158
|
-
await flushMicrotasks()
|
|
177
|
+
})
|
|
178
|
+
await flushMicrotasks()
|
|
159
179
|
|
|
160
|
-
const reqMsg = transport.sent.find(m => m.t === 'req') as any
|
|
161
|
-
expect(reqMsg.body.sealed).toBeTruthy()
|
|
180
|
+
const reqMsg = transport.sent.find((m) => m.t === 'req') as any
|
|
181
|
+
expect(reqMsg.body.sealed).toBeTruthy() // method inside sealed
|
|
162
182
|
|
|
163
183
|
// Wallet responds with { signature }, mapResponse unwraps to just the string
|
|
164
|
-
respondToLatestReq({ signature: '0xsig...' })
|
|
165
|
-
const result = await promise
|
|
166
|
-
expect(result).toBe('0xsig...')
|
|
167
|
-
})
|
|
184
|
+
respondToLatestReq({ signature: '0xsig...' })
|
|
185
|
+
const result = await promise
|
|
186
|
+
expect(result).toBe('0xsig...')
|
|
187
|
+
})
|
|
168
188
|
|
|
169
189
|
it('maps plain text to wallet_signMessage', async () => {
|
|
170
|
-
await setupConnectedSession()
|
|
190
|
+
await setupConnectedSession()
|
|
171
191
|
|
|
172
192
|
const promise = provider.request({
|
|
173
193
|
method: 'personal_sign',
|
|
174
194
|
params: ['Hello, WalletPair!', '0xabc'],
|
|
175
|
-
})
|
|
176
|
-
await flushMicrotasks()
|
|
195
|
+
})
|
|
196
|
+
await flushMicrotasks()
|
|
177
197
|
|
|
178
|
-
const reqMsg = transport.sent.find(m => m.t === 'req') as any
|
|
179
|
-
expect(reqMsg.body.sealed).toBeTruthy()
|
|
198
|
+
const reqMsg = transport.sent.find((m) => m.t === 'req') as any
|
|
199
|
+
expect(reqMsg.body.sealed).toBeTruthy() // method inside sealed
|
|
180
200
|
|
|
181
|
-
respondToLatestReq({ signature: '0xsig...' })
|
|
182
|
-
const result = await promise
|
|
183
|
-
expect(result).toBe('0xsig...')
|
|
184
|
-
})
|
|
185
|
-
})
|
|
201
|
+
respondToLatestReq({ signature: '0xsig...' })
|
|
202
|
+
const result = await promise
|
|
203
|
+
expect(result).toBe('0xsig...')
|
|
204
|
+
})
|
|
205
|
+
})
|
|
186
206
|
|
|
187
207
|
// -----------------------------------------------------------------------
|
|
188
208
|
// eth_sendTransaction
|
|
@@ -190,24 +210,24 @@ describe('WalletPairProvider', () => {
|
|
|
190
210
|
|
|
191
211
|
describe('eth_sendTransaction', () => {
|
|
192
212
|
it('maps to wallet_sendTransaction', async () => {
|
|
193
|
-
await setupConnectedSession()
|
|
213
|
+
await setupConnectedSession()
|
|
194
214
|
|
|
195
|
-
const tx = { to: '0x123', value: '0x0', data: '0x' }
|
|
215
|
+
const tx = { to: '0x123', value: '0x0', data: '0x', type: '0x2', chainId: '0x1' }
|
|
196
216
|
const promise = provider.request({
|
|
197
217
|
method: 'eth_sendTransaction',
|
|
198
218
|
params: [tx],
|
|
199
|
-
})
|
|
200
|
-
await flushMicrotasks()
|
|
219
|
+
})
|
|
220
|
+
await flushMicrotasks()
|
|
201
221
|
|
|
202
|
-
const reqMsg = transport.sent.find(m => m.t === 'req') as any
|
|
203
|
-
expect(reqMsg.body.sealed).toBeTruthy()
|
|
222
|
+
const reqMsg = transport.sent.find((m) => m.t === 'req') as any
|
|
223
|
+
expect(reqMsg.body.sealed).toBeTruthy() // method inside sealed
|
|
204
224
|
|
|
205
225
|
// mapResponse unwraps { txHash } to just the hash string
|
|
206
|
-
respondToLatestReq({ txHash: '0xtx...' })
|
|
207
|
-
const result = await promise
|
|
208
|
-
expect(result).toBe('0xtx...')
|
|
209
|
-
})
|
|
210
|
-
})
|
|
226
|
+
respondToLatestReq({ txHash: '0xtx...' })
|
|
227
|
+
const result = await promise
|
|
228
|
+
expect(result).toBe('0xtx...')
|
|
229
|
+
})
|
|
230
|
+
})
|
|
211
231
|
|
|
212
232
|
// -----------------------------------------------------------------------
|
|
213
233
|
// wallet_switchEthereumChain
|
|
@@ -215,21 +235,21 @@ describe('WalletPairProvider', () => {
|
|
|
215
235
|
|
|
216
236
|
describe('wallet_switchEthereumChain', () => {
|
|
217
237
|
it('maps to wallet_switchChain', async () => {
|
|
218
|
-
await setupConnectedSession()
|
|
238
|
+
await setupConnectedSession()
|
|
219
239
|
|
|
220
240
|
const promise = provider.request({
|
|
221
241
|
method: 'wallet_switchEthereumChain',
|
|
222
242
|
params: [{ chainId: '0x89' }],
|
|
223
|
-
})
|
|
224
|
-
await flushMicrotasks()
|
|
243
|
+
})
|
|
244
|
+
await flushMicrotasks()
|
|
225
245
|
|
|
226
|
-
const reqMsg = transport.sent.find(m => m.t === 'req') as any
|
|
227
|
-
expect(reqMsg.body.sealed).toBeTruthy()
|
|
246
|
+
const reqMsg = transport.sent.find((m) => m.t === 'req') as any
|
|
247
|
+
expect(reqMsg.body.sealed).toBeTruthy() // method inside sealed
|
|
228
248
|
|
|
229
|
-
respondToLatestReq({ success: true })
|
|
230
|
-
await promise
|
|
231
|
-
})
|
|
232
|
-
})
|
|
249
|
+
respondToLatestReq({ success: true })
|
|
250
|
+
await promise
|
|
251
|
+
})
|
|
252
|
+
})
|
|
233
253
|
|
|
234
254
|
// -----------------------------------------------------------------------
|
|
235
255
|
// Events
|
|
@@ -237,79 +257,127 @@ describe('WalletPairProvider', () => {
|
|
|
237
257
|
|
|
238
258
|
describe('EIP-1193 events', () => {
|
|
239
259
|
it('emits connect when session connects', async () => {
|
|
240
|
-
await setupConnectedSession()
|
|
260
|
+
await setupConnectedSession()
|
|
241
261
|
// Provider constructor registered the listener, and session is already connected
|
|
242
|
-
expect(provider.isConnected()).toBe(true)
|
|
243
|
-
})
|
|
262
|
+
expect(provider.isConnected()).toBe(true)
|
|
263
|
+
})
|
|
244
264
|
|
|
245
265
|
it('emits disconnect when session closes', async () => {
|
|
246
|
-
await setupConnectedSession()
|
|
247
|
-
const handler = vi.fn()
|
|
248
|
-
provider.on('disconnect', handler)
|
|
266
|
+
await setupConnectedSession()
|
|
267
|
+
const handler = vi.fn()
|
|
268
|
+
provider.on('disconnect', handler)
|
|
249
269
|
|
|
250
|
-
session.close()
|
|
251
|
-
expect(handler).toHaveBeenCalledWith({ code: 4900, message: 'Disconnected' })
|
|
252
|
-
expect(provider.isConnected()).toBe(false)
|
|
253
|
-
})
|
|
270
|
+
session.close()
|
|
271
|
+
expect(handler).toHaveBeenCalledWith({ code: 4900, message: 'Disconnected' })
|
|
272
|
+
expect(provider.isConnected()).toBe(false)
|
|
273
|
+
})
|
|
254
274
|
|
|
255
275
|
it('emits accountsChanged from wallet event', async () => {
|
|
256
|
-
await setupConnectedSession()
|
|
257
|
-
const handler = vi.fn()
|
|
258
|
-
provider.on('accountsChanged', handler)
|
|
276
|
+
await setupConnectedSession()
|
|
277
|
+
const handler = vi.fn()
|
|
278
|
+
provider.on('accountsChanged', handler)
|
|
259
279
|
|
|
260
280
|
transport.receive({
|
|
261
|
-
v: 1,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
281
|
+
v: 1,
|
|
282
|
+
t: 'evt',
|
|
283
|
+
ch: session.channelId,
|
|
284
|
+
ts: Date.now(),
|
|
285
|
+
from: walletKp.publicKeyB64,
|
|
286
|
+
body: {
|
|
287
|
+
id: 'evt-1',
|
|
288
|
+
sealed: sealPayload(
|
|
289
|
+
sessionKey,
|
|
290
|
+
session.channelId,
|
|
291
|
+
0,
|
|
292
|
+
{ _event: 'accountsChanged', accounts: ['0xnew'] },
|
|
293
|
+
{ type: 'evt', from: walletKp.publicKeyB64, id: 'evt-1' },
|
|
294
|
+
),
|
|
295
|
+
},
|
|
296
|
+
} as ProtocolMessage)
|
|
297
|
+
|
|
298
|
+
expect(handler).toHaveBeenCalledWith(['0xnew'])
|
|
299
|
+
expect(provider.getAccounts()).toEqual(['0xnew'])
|
|
300
|
+
})
|
|
269
301
|
|
|
270
302
|
it('emits chainChanged from wallet event (CAIP-2 format)', async () => {
|
|
271
|
-
await setupConnectedSession()
|
|
272
|
-
const handler = vi.fn()
|
|
273
|
-
provider.on('chainChanged', handler)
|
|
303
|
+
await setupConnectedSession()
|
|
304
|
+
const handler = vi.fn()
|
|
305
|
+
provider.on('chainChanged', handler)
|
|
274
306
|
|
|
275
307
|
transport.receive({
|
|
276
|
-
v: 1,
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
308
|
+
v: 1,
|
|
309
|
+
t: 'evt',
|
|
310
|
+
ch: session.channelId,
|
|
311
|
+
ts: Date.now(),
|
|
312
|
+
from: walletKp.publicKeyB64,
|
|
313
|
+
body: {
|
|
314
|
+
id: 'evt-2',
|
|
315
|
+
sealed: sealPayload(
|
|
316
|
+
sessionKey,
|
|
317
|
+
session.channelId,
|
|
318
|
+
0,
|
|
319
|
+
{ _event: 'chainChanged', chainId: 'eip155:137' },
|
|
320
|
+
{ type: 'evt', from: walletKp.publicKeyB64, id: 'evt-2' },
|
|
321
|
+
),
|
|
322
|
+
},
|
|
323
|
+
} as ProtocolMessage)
|
|
324
|
+
|
|
325
|
+
expect(handler).toHaveBeenCalledWith('0x89')
|
|
326
|
+
})
|
|
283
327
|
|
|
284
328
|
it('emits chainChanged from wallet event (hex format)', async () => {
|
|
285
|
-
await setupConnectedSession()
|
|
286
|
-
const handler = vi.fn()
|
|
287
|
-
provider.on('chainChanged', handler)
|
|
329
|
+
await setupConnectedSession()
|
|
330
|
+
const handler = vi.fn()
|
|
331
|
+
provider.on('chainChanged', handler)
|
|
288
332
|
|
|
289
333
|
transport.receive({
|
|
290
|
-
v: 1,
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
334
|
+
v: 1,
|
|
335
|
+
t: 'evt',
|
|
336
|
+
ch: session.channelId,
|
|
337
|
+
ts: Date.now(),
|
|
338
|
+
from: walletKp.publicKeyB64,
|
|
339
|
+
body: {
|
|
340
|
+
id: 'evt-3',
|
|
341
|
+
sealed: sealPayload(
|
|
342
|
+
sessionKey,
|
|
343
|
+
session.channelId,
|
|
344
|
+
0,
|
|
345
|
+
{ _event: 'chainChanged', chainId: '0x89' },
|
|
346
|
+
{ type: 'evt', from: walletKp.publicKeyB64, id: 'evt-3' },
|
|
347
|
+
),
|
|
348
|
+
},
|
|
349
|
+
} as ProtocolMessage)
|
|
350
|
+
|
|
351
|
+
expect(handler).toHaveBeenCalledWith('0x89')
|
|
352
|
+
})
|
|
297
353
|
|
|
298
354
|
it('removeListener stops event delivery', async () => {
|
|
299
|
-
await setupConnectedSession()
|
|
300
|
-
const handler = vi.fn()
|
|
301
|
-
provider.on('accountsChanged', handler)
|
|
302
|
-
provider.removeListener('accountsChanged', handler)
|
|
355
|
+
await setupConnectedSession()
|
|
356
|
+
const handler = vi.fn()
|
|
357
|
+
provider.on('accountsChanged', handler)
|
|
358
|
+
provider.removeListener('accountsChanged', handler)
|
|
303
359
|
|
|
304
360
|
transport.receive({
|
|
305
|
-
v: 1,
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
361
|
+
v: 1,
|
|
362
|
+
t: 'evt',
|
|
363
|
+
ch: session.channelId,
|
|
364
|
+
ts: Date.now(),
|
|
365
|
+
from: walletKp.publicKeyB64,
|
|
366
|
+
body: {
|
|
367
|
+
id: 'evt-4',
|
|
368
|
+
sealed: sealPayload(
|
|
369
|
+
sessionKey,
|
|
370
|
+
session.channelId,
|
|
371
|
+
0,
|
|
372
|
+
{ _event: 'accountsChanged', accounts: ['0x1'] },
|
|
373
|
+
{ type: 'evt', from: walletKp.publicKeyB64, id: 'evt-4' },
|
|
374
|
+
),
|
|
375
|
+
},
|
|
376
|
+
} as ProtocolMessage)
|
|
377
|
+
|
|
378
|
+
expect(handler).not.toHaveBeenCalled()
|
|
379
|
+
})
|
|
380
|
+
})
|
|
313
381
|
|
|
314
382
|
// -----------------------------------------------------------------------
|
|
315
383
|
// Helper methods
|
|
@@ -317,15 +385,15 @@ describe('WalletPairProvider', () => {
|
|
|
317
385
|
|
|
318
386
|
describe('helper methods', () => {
|
|
319
387
|
it('getChainId returns hex string', async () => {
|
|
320
|
-
await setupConnectedSession(42161)
|
|
321
|
-
expect(provider.getChainId()).toBe('0xa4b1')
|
|
322
|
-
})
|
|
388
|
+
await setupConnectedSession(42161)
|
|
389
|
+
expect(provider.getChainId()).toBe('0xa4b1')
|
|
390
|
+
})
|
|
323
391
|
|
|
324
392
|
it('getSession returns the underlying session', async () => {
|
|
325
|
-
await setupConnectedSession()
|
|
326
|
-
expect(provider.getSession()).toBe(session)
|
|
327
|
-
})
|
|
328
|
-
})
|
|
393
|
+
await setupConnectedSession()
|
|
394
|
+
expect(provider.getSession()).toBe(session)
|
|
395
|
+
})
|
|
396
|
+
})
|
|
329
397
|
|
|
330
398
|
// -----------------------------------------------------------------------
|
|
331
399
|
// Error handling
|
|
@@ -333,33 +401,43 @@ describe('WalletPairProvider', () => {
|
|
|
333
401
|
|
|
334
402
|
describe('error handling', () => {
|
|
335
403
|
it('rejects on wallet error response', async () => {
|
|
336
|
-
await setupConnectedSession()
|
|
404
|
+
await setupConnectedSession()
|
|
337
405
|
|
|
338
|
-
const promise = provider.request({ method: 'eth_requestAccounts' })
|
|
339
|
-
await flushMicrotasks()
|
|
406
|
+
const promise = provider.request({ method: 'eth_requestAccounts' })
|
|
407
|
+
await flushMicrotasks()
|
|
340
408
|
|
|
341
|
-
respondToLatestReq({ code: 'user_rejected', message: 'Denied' }, false)
|
|
342
|
-
await expect(promise).rejects.toThrow('Denied')
|
|
343
|
-
})
|
|
344
|
-
})
|
|
409
|
+
respondToLatestReq({ code: 'user_rejected', message: 'Denied' }, false)
|
|
410
|
+
await expect(promise).rejects.toThrow('Denied')
|
|
411
|
+
})
|
|
412
|
+
})
|
|
345
413
|
|
|
346
414
|
// -----------------------------------------------------------------------
|
|
347
415
|
// Passthrough methods
|
|
348
416
|
// -----------------------------------------------------------------------
|
|
349
417
|
|
|
350
418
|
describe('unknown methods', () => {
|
|
351
|
-
it('
|
|
352
|
-
await setupConnectedSession()
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
})
|
|
419
|
+
it('routes read-only methods to rpcProvider when available', async () => {
|
|
420
|
+
await setupConnectedSession()
|
|
421
|
+
const rpcProvider = { request: vi.fn().mockResolvedValue('0x1234') }
|
|
422
|
+
;(provider as any).rpcProvider = rpcProvider
|
|
423
|
+
|
|
424
|
+
const result = await provider.request({
|
|
425
|
+
method: 'eth_getBalance',
|
|
426
|
+
params: ['0x123', 'latest'],
|
|
427
|
+
})
|
|
428
|
+
expect(rpcProvider.request).toHaveBeenCalledWith({
|
|
429
|
+
method: 'eth_getBalance',
|
|
430
|
+
params: ['0x123', 'latest'],
|
|
431
|
+
})
|
|
432
|
+
expect(result).toBe('0x1234')
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
it('throws for read-only methods without rpcProvider', async () => {
|
|
436
|
+
await setupConnectedSession()
|
|
437
|
+
|
|
438
|
+
await expect(
|
|
439
|
+
provider.request({ method: 'eth_getBalance', params: ['0x123', 'latest'] }),
|
|
440
|
+
).rejects.toThrow('Unsupported method: eth_getBalance')
|
|
441
|
+
})
|
|
442
|
+
})
|
|
443
|
+
})
|