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
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
* handles all documented edge cases.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { describe, expect, it } from 'vitest'
|
|
10
|
-
import { canonicalJson, sha256Hex } from '../../crypto.js'
|
|
9
|
+
import { describe, expect, it } from 'vitest'
|
|
10
|
+
import { canonicalJson, sha256Hex } from '../../crypto.js'
|
|
11
11
|
|
|
12
12
|
function hash(s: string): string {
|
|
13
|
-
return sha256Hex(new TextEncoder().encode(s))
|
|
13
|
+
return sha256Hex(new TextEncoder().encode(s))
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
@@ -22,19 +22,19 @@ describe('Section 6.3 — Vector 1: capabilities (key sorting, nested objects)',
|
|
|
22
22
|
methods: ['wallet_signTransaction', 'wallet_signMessage'],
|
|
23
23
|
events: ['accountsChanged', 'chainChanged'],
|
|
24
24
|
chains: ['eip155:1', 'eip155:137'],
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
const expected =
|
|
27
|
-
'{"chains":["eip155:1","eip155:137"],"events":["accountsChanged","chainChanged"],"methods":["wallet_signTransaction","wallet_signMessage"]}'
|
|
28
|
-
const expectedHash = '4da366e2aae26b47b3d90fff52410752348733350ce2525dce7d64510f571333'
|
|
27
|
+
'{"chains":["eip155:1","eip155:137"],"events":["accountsChanged","chainChanged"],"methods":["wallet_signTransaction","wallet_signMessage"]}'
|
|
28
|
+
const expectedHash = '4da366e2aae26b47b3d90fff52410752348733350ce2525dce7d64510f571333'
|
|
29
29
|
|
|
30
30
|
it('produces the correct canonical output', () => {
|
|
31
|
-
expect(canonicalJson(input)).toBe(expected)
|
|
32
|
-
})
|
|
31
|
+
expect(canonicalJson(input)).toBe(expected)
|
|
32
|
+
})
|
|
33
33
|
|
|
34
34
|
it('SHA-256 of output matches the spec', () => {
|
|
35
|
-
expect(hash(canonicalJson(input))).toBe(expectedHash)
|
|
36
|
-
})
|
|
37
|
-
})
|
|
35
|
+
expect(hash(canonicalJson(input))).toBe(expectedHash)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
38
|
|
|
39
39
|
describe('Section 6.3 — Vector 2: join plaintext (nested + meta)', () => {
|
|
40
40
|
const input = {
|
|
@@ -49,75 +49,75 @@ describe('Section 6.3 — Vector 2: join plaintext (nested + meta)', () => {
|
|
|
49
49
|
url: 'https://mywallet.app',
|
|
50
50
|
icon: 'https://mywallet.app/icon.png',
|
|
51
51
|
},
|
|
52
|
-
}
|
|
52
|
+
}
|
|
53
53
|
const expected =
|
|
54
|
-
'{"capabilities":{"chains":["eip155:1","eip155:137"],"events":["accountsChanged","chainChanged"],"methods":["wallet_signTransaction","wallet_signMessage"]},"meta":{"description":"A multi-chain wallet","icon":"https://mywallet.app/icon.png","name":"MyWallet","url":"https://mywallet.app"}}'
|
|
55
|
-
const expectedHash = '9f4f3b71b0db39ba8b86173b8c78182799d0a745c68b6e89e5d8f0d3def52594'
|
|
54
|
+
'{"capabilities":{"chains":["eip155:1","eip155:137"],"events":["accountsChanged","chainChanged"],"methods":["wallet_signTransaction","wallet_signMessage"]},"meta":{"description":"A multi-chain wallet","icon":"https://mywallet.app/icon.png","name":"MyWallet","url":"https://mywallet.app"}}'
|
|
55
|
+
const expectedHash = '9f4f3b71b0db39ba8b86173b8c78182799d0a745c68b6e89e5d8f0d3def52594'
|
|
56
56
|
|
|
57
57
|
it('produces the correct canonical output', () => {
|
|
58
|
-
expect(canonicalJson(input)).toBe(expected)
|
|
59
|
-
})
|
|
58
|
+
expect(canonicalJson(input)).toBe(expected)
|
|
59
|
+
})
|
|
60
60
|
|
|
61
61
|
it('SHA-256 of output matches the spec', () => {
|
|
62
|
-
expect(hash(canonicalJson(input))).toBe(expectedHash)
|
|
63
|
-
})
|
|
64
|
-
})
|
|
62
|
+
expect(hash(canonicalJson(input))).toBe(expectedHash)
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
65
|
|
|
66
66
|
describe('Section 6.3 — Vector 3: primitives', () => {
|
|
67
67
|
it('null -> "null"', () => {
|
|
68
|
-
const output = canonicalJson(null)
|
|
69
|
-
expect(output).toBe('null')
|
|
70
|
-
expect(hash(output)).toBe('74234e98afe7498fb5daf1f36ac2d78acc339464f950703b8c019892f982b90b')
|
|
71
|
-
})
|
|
68
|
+
const output = canonicalJson(null)
|
|
69
|
+
expect(output).toBe('null')
|
|
70
|
+
expect(hash(output)).toBe('74234e98afe7498fb5daf1f36ac2d78acc339464f950703b8c019892f982b90b')
|
|
71
|
+
})
|
|
72
72
|
|
|
73
73
|
it('true -> "true"', () => {
|
|
74
|
-
const output = canonicalJson(true)
|
|
75
|
-
expect(output).toBe('true')
|
|
76
|
-
expect(hash(output)).toBe('b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b')
|
|
77
|
-
})
|
|
74
|
+
const output = canonicalJson(true)
|
|
75
|
+
expect(output).toBe('true')
|
|
76
|
+
expect(hash(output)).toBe('b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b')
|
|
77
|
+
})
|
|
78
78
|
|
|
79
79
|
it('42 -> "42"', () => {
|
|
80
|
-
const output = canonicalJson(42)
|
|
81
|
-
expect(output).toBe('42')
|
|
82
|
-
expect(hash(output)).toBe('73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049')
|
|
83
|
-
})
|
|
80
|
+
const output = canonicalJson(42)
|
|
81
|
+
expect(output).toBe('42')
|
|
82
|
+
expect(hash(output)).toBe('73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049')
|
|
83
|
+
})
|
|
84
84
|
|
|
85
85
|
it('"hello" -> \'"hello"\'', () => {
|
|
86
|
-
const output = canonicalJson('hello')
|
|
87
|
-
expect(output).toBe('"hello"')
|
|
88
|
-
expect(hash(output)).toBe('5aa762ae383fbb727af3c7a36d4940a5b8c40a989452d2304fc958ff3f354e7a')
|
|
89
|
-
})
|
|
90
|
-
})
|
|
86
|
+
const output = canonicalJson('hello')
|
|
87
|
+
expect(output).toBe('"hello"')
|
|
88
|
+
expect(hash(output)).toBe('5aa762ae383fbb727af3c7a36d4940a5b8c40a989452d2304fc958ff3f354e7a')
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
91
|
|
|
92
92
|
describe('Section 6.3 — Vector 4: empty containers', () => {
|
|
93
93
|
it('{} -> "{}"', () => {
|
|
94
|
-
const output = canonicalJson({})
|
|
95
|
-
expect(output).toBe('{}')
|
|
96
|
-
expect(hash(output)).toBe('44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a')
|
|
97
|
-
})
|
|
94
|
+
const output = canonicalJson({})
|
|
95
|
+
expect(output).toBe('{}')
|
|
96
|
+
expect(hash(output)).toBe('44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a')
|
|
97
|
+
})
|
|
98
98
|
|
|
99
99
|
it('[] -> "[]"', () => {
|
|
100
|
-
const output = canonicalJson([])
|
|
101
|
-
expect(output).toBe('[]')
|
|
102
|
-
expect(hash(output)).toBe('4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945')
|
|
103
|
-
})
|
|
104
|
-
})
|
|
100
|
+
const output = canonicalJson([])
|
|
101
|
+
expect(output).toBe('[]')
|
|
102
|
+
expect(hash(output)).toBe('4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945')
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
105
|
|
|
106
106
|
describe('Section 6.3 — Vector 5: negative zero', () => {
|
|
107
107
|
it('-0 -> "0"', () => {
|
|
108
|
-
const output = canonicalJson(-0)
|
|
109
|
-
expect(output).toBe('0')
|
|
110
|
-
expect(hash(output)).toBe('5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9')
|
|
111
|
-
})
|
|
112
|
-
})
|
|
108
|
+
const output = canonicalJson(-0)
|
|
109
|
+
expect(output).toBe('0')
|
|
110
|
+
expect(hash(output)).toBe('5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9')
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
113
|
|
|
114
114
|
describe('Section 6.3 — Vector 6: escaped control character', () => {
|
|
115
115
|
it('U+0001 -> "\\u0001" (lowercase hex)', () => {
|
|
116
|
-
const output = canonicalJson('\u0001')
|
|
117
|
-
expect(output).toBe('"\\u0001"')
|
|
118
|
-
expect(hash(output)).toBe('b81cfb0a6715e53b373345b49e8ad94eb55fd777519dc539373d0634973c186e')
|
|
119
|
-
})
|
|
120
|
-
})
|
|
116
|
+
const output = canonicalJson('\u0001')
|
|
117
|
+
expect(output).toBe('"\\u0001"')
|
|
118
|
+
expect(hash(output)).toBe('b81cfb0a6715e53b373345b49e8ad94eb55fd777519dc539373d0634973c186e')
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
121
|
|
|
122
122
|
// ---------------------------------------------------------------------------
|
|
123
123
|
// Edge cases from Section 6.3 rules
|
|
@@ -125,89 +125,89 @@ describe('Section 6.3 — Vector 6: escaped control character', () => {
|
|
|
125
125
|
|
|
126
126
|
describe('Section 6.3 — Edge cases', () => {
|
|
127
127
|
it('negative zero inside an object serializes as 0', () => {
|
|
128
|
-
expect(canonicalJson({ value: -0 })).toBe('{"value":0}')
|
|
129
|
-
})
|
|
128
|
+
expect(canonicalJson({ value: -0 })).toBe('{"value":0}')
|
|
129
|
+
})
|
|
130
130
|
|
|
131
131
|
it('negative zero inside an array serializes as 0', () => {
|
|
132
|
-
expect(canonicalJson([-0])).toBe('[0]')
|
|
133
|
-
})
|
|
132
|
+
expect(canonicalJson([-0])).toBe('[0]')
|
|
133
|
+
})
|
|
134
134
|
|
|
135
135
|
it('NaN is rejected', () => {
|
|
136
|
-
expect(() => canonicalJson(NaN)).toThrow()
|
|
137
|
-
})
|
|
136
|
+
expect(() => canonicalJson(NaN)).toThrow()
|
|
137
|
+
})
|
|
138
138
|
|
|
139
139
|
it('Infinity is rejected', () => {
|
|
140
|
-
expect(() => canonicalJson(Infinity)).toThrow()
|
|
141
|
-
expect(() => canonicalJson(-Infinity)).toThrow()
|
|
142
|
-
})
|
|
140
|
+
expect(() => canonicalJson(Infinity)).toThrow()
|
|
141
|
+
expect(() => canonicalJson(-Infinity)).toThrow()
|
|
142
|
+
})
|
|
143
143
|
|
|
144
144
|
it('forward slash is NOT escaped', () => {
|
|
145
|
-
const output = canonicalJson('/')
|
|
146
|
-
expect(output).toBe('"/"')
|
|
147
|
-
})
|
|
145
|
+
const output = canonicalJson('/')
|
|
146
|
+
expect(output).toBe('"/"')
|
|
147
|
+
})
|
|
148
148
|
|
|
149
149
|
it('non-ASCII Unicode is output as literal UTF-8', () => {
|
|
150
|
-
expect(canonicalJson('中文')).toBe('"中文"')
|
|
151
|
-
})
|
|
150
|
+
expect(canonicalJson('中文')).toBe('"中文"')
|
|
151
|
+
})
|
|
152
152
|
|
|
153
153
|
it('control characters U+0000-U+001F use correct escape forms', () => {
|
|
154
154
|
// Short forms
|
|
155
|
-
expect(canonicalJson('\b')).toBe('"\\b"')
|
|
156
|
-
expect(canonicalJson('\t')).toBe('"\\t"')
|
|
157
|
-
expect(canonicalJson('\n')).toBe('"\\n"')
|
|
158
|
-
expect(canonicalJson('\f')).toBe('"\\f"')
|
|
159
|
-
expect(canonicalJson('\r')).toBe('"\\r"')
|
|
155
|
+
expect(canonicalJson('\b')).toBe('"\\b"')
|
|
156
|
+
expect(canonicalJson('\t')).toBe('"\\t"')
|
|
157
|
+
expect(canonicalJson('\n')).toBe('"\\n"')
|
|
158
|
+
expect(canonicalJson('\f')).toBe('"\\f"')
|
|
159
|
+
expect(canonicalJson('\r')).toBe('"\\r"')
|
|
160
160
|
|
|
161
161
|
// All other C0 controls use lowercase \\uXXXX
|
|
162
|
-
expect(canonicalJson('\x00')).toBe('"\\u0000"')
|
|
163
|
-
expect(canonicalJson('\x01')).toBe('"\\u0001"')
|
|
164
|
-
expect(canonicalJson('\x1f')).toBe('"\\u001f"')
|
|
165
|
-
})
|
|
162
|
+
expect(canonicalJson('\x00')).toBe('"\\u0000"')
|
|
163
|
+
expect(canonicalJson('\x01')).toBe('"\\u0001"')
|
|
164
|
+
expect(canonicalJson('\x1f')).toBe('"\\u001f"')
|
|
165
|
+
})
|
|
166
166
|
|
|
167
167
|
it('\\uXXXX escapes use lowercase hex digits only', () => {
|
|
168
168
|
for (let cp = 0; cp <= 0x1f; cp++) {
|
|
169
|
-
const result = canonicalJson(String.fromCharCode(cp))
|
|
169
|
+
const result = canonicalJson(String.fromCharCode(cp))
|
|
170
170
|
// Verify no uppercase hex digits in any \u escape
|
|
171
|
-
const matches = result.match(/\\u[0-9a-fA-F]{4}/g)
|
|
171
|
+
const matches = result.match(/\\u[0-9a-fA-F]{4}/g)
|
|
172
172
|
if (matches) {
|
|
173
173
|
for (const m of matches) {
|
|
174
|
-
expect(m).toBe(m.toLowerCase())
|
|
174
|
+
expect(m).toBe(m.toLowerCase())
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
})
|
|
178
|
+
})
|
|
179
179
|
|
|
180
180
|
it('no whitespace in structural output', () => {
|
|
181
181
|
const complex = {
|
|
182
182
|
z: [3, 1, 2],
|
|
183
183
|
a: { y: 'value', x: true },
|
|
184
184
|
m: null,
|
|
185
|
-
}
|
|
186
|
-
const output = canonicalJson(complex)
|
|
185
|
+
}
|
|
186
|
+
const output = canonicalJson(complex)
|
|
187
187
|
// Remove string content, then check structural chars only
|
|
188
|
-
const structural = output.replace(/"[^"]*"/g, '""')
|
|
189
|
-
expect(structural).not.toMatch(/\s/)
|
|
190
|
-
})
|
|
188
|
+
const structural = output.replace(/"[^"]*"/g, '""')
|
|
189
|
+
expect(structural).not.toMatch(/\s/)
|
|
190
|
+
})
|
|
191
191
|
|
|
192
192
|
it('array order is preserved (NOT sorted)', () => {
|
|
193
|
-
expect(canonicalJson([3, 1, 2])).toBe('[3,1,2]')
|
|
194
|
-
})
|
|
193
|
+
expect(canonicalJson([3, 1, 2])).toBe('[3,1,2]')
|
|
194
|
+
})
|
|
195
195
|
|
|
196
196
|
it('object keys are sorted alphabetically at every nesting level', () => {
|
|
197
|
-
const input = { z: { b: 2, a: 1 }, a: 0 }
|
|
198
|
-
expect(canonicalJson(input)).toBe('{"a":0,"z":{"a":1,"b":2}}')
|
|
199
|
-
})
|
|
197
|
+
const input = { z: { b: 2, a: 1 }, a: 0 }
|
|
198
|
+
expect(canonicalJson(input)).toBe('{"a":0,"z":{"a":1,"b":2}}')
|
|
199
|
+
})
|
|
200
200
|
|
|
201
201
|
it('no trailing .0 on whole-number floats', () => {
|
|
202
|
-
expect(canonicalJson(100.0)).toBe('100')
|
|
203
|
-
})
|
|
202
|
+
expect(canonicalJson(100.0)).toBe('100')
|
|
203
|
+
})
|
|
204
204
|
|
|
205
205
|
it('numbers have no leading zeroes or + prefix', () => {
|
|
206
|
-
expect(canonicalJson(42)).toBe('42')
|
|
207
|
-
expect(canonicalJson(0)).toBe('0')
|
|
208
|
-
expect(canonicalJson(-1)).toBe('-1')
|
|
209
|
-
})
|
|
210
|
-
})
|
|
206
|
+
expect(canonicalJson(42)).toBe('42')
|
|
207
|
+
expect(canonicalJson(0)).toBe('0')
|
|
208
|
+
expect(canonicalJson(-1)).toBe('-1')
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
211
|
|
|
212
212
|
// ---------------------------------------------------------------------------
|
|
213
213
|
// Idempotency
|
|
@@ -219,9 +219,9 @@ describe('Section 6.3 — Idempotency', () => {
|
|
|
219
219
|
methods: ['wallet_signTransaction', 'wallet_signMessage'],
|
|
220
220
|
events: ['accountsChanged', 'chainChanged'],
|
|
221
221
|
chains: ['eip155:1', 'eip155:137'],
|
|
222
|
-
}
|
|
223
|
-
const first = canonicalJson(input)
|
|
224
|
-
const reparsed = JSON.parse(first)
|
|
225
|
-
expect(canonicalJson(reparsed)).toBe(first)
|
|
226
|
-
})
|
|
227
|
-
})
|
|
222
|
+
}
|
|
223
|
+
const first = canonicalJson(input)
|
|
224
|
+
const reparsed = JSON.parse(first)
|
|
225
|
+
expect(canonicalJson(reparsed)).toBe(first)
|
|
226
|
+
})
|
|
227
|
+
})
|