ox 1.8.9 → 1.8.12
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/CHANGELOG.md +32 -0
- package/dist/core/AbiEvent.d.ts.map +1 -1
- package/dist/core/AbiEvent.js +32 -10
- package/dist/core/AbiEvent.js.map +1 -1
- package/dist/core/Authorization.d.ts.map +1 -1
- package/dist/core/Authorization.js +44 -6
- package/dist/core/Authorization.js.map +1 -1
- package/dist/core/Hex.d.ts +13 -1
- package/dist/core/Hex.d.ts.map +1 -1
- package/dist/core/Hex.js +5 -3
- package/dist/core/Hex.js.map +1 -1
- package/dist/core/RpcTransport.d.ts.map +1 -1
- package/dist/core/RpcTransport.js +21 -14
- package/dist/core/RpcTransport.js.map +1 -1
- package/dist/core/Signature.d.ts +1 -1
- package/dist/core/Signature.d.ts.map +1 -1
- package/dist/core/Signature.js +3 -2
- package/dist/core/Signature.js.map +1 -1
- package/dist/core/Siwe.d.ts.map +1 -1
- package/dist/core/Siwe.js +1 -1
- package/dist/core/Siwe.js.map +1 -1
- package/dist/core/TypedData.js +1 -1
- package/dist/core/TypedData.js.map +1 -1
- package/dist/core/internal/abiItem.d.ts.map +1 -1
- package/dist/core/internal/abiItem.js +45 -19
- package/dist/core/internal/abiItem.js.map +1 -1
- package/dist/tempo/AccountConfig.d.ts +7 -17
- package/dist/tempo/AccountConfig.d.ts.map +1 -1
- package/dist/tempo/AccountConfig.js +8 -26
- package/dist/tempo/AccountConfig.js.map +1 -1
- package/dist/tempo/AccountOperation.d.ts +1 -1
- package/dist/tempo/AccountOperation.js +1 -1
- package/dist/tempo/index.d.ts +1 -3
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +1 -3
- package/dist/tempo/index.js.map +1 -1
- package/dist/zod/Authorization.d.ts.map +1 -1
- package/dist/zod/Authorization.js +6 -9
- package/dist/zod/Authorization.js.map +1 -1
- package/package.json +1 -1
- package/src/core/AbiEvent.ts +61 -22
- package/src/core/Authorization.ts +43 -6
- package/src/core/Hex.ts +17 -3
- package/src/core/RpcTransport.ts +20 -16
- package/src/core/Signature.ts +5 -2
- package/src/core/Siwe.ts +1 -1
- package/src/core/TypedData.ts +1 -1
- package/src/core/_test/AbiEvent.test.ts +232 -1
- package/src/core/_test/AbiItem.test.ts +342 -0
- package/src/core/_test/Authorization.test.ts +166 -0
- package/src/core/_test/Hex.test.ts +31 -0
- package/src/core/_test/RpcTransport.test.ts +48 -0
- package/src/core/_test/Signature.test.ts +42 -1
- package/src/core/_test/Siwe.test.ts +53 -0
- package/src/core/_test/TransactionEnvelopeEip7702.test.ts +66 -0
- package/src/core/_test/TypedData.test.ts +29 -0
- package/src/core/internal/abiItem.ts +59 -25
- package/src/tempo/AccountConfig.test.ts +34 -47
- package/src/tempo/AccountConfig.ts +9 -37
- package/src/tempo/AccountOperation.test.ts +36 -37
- package/src/tempo/AccountOperation.ts +1 -1
- package/src/tempo/SignatureEnvelope.test.ts +1 -3
- package/src/tempo/configurableAccounts.e2e.test.ts +1 -2
- package/src/tempo/index.ts +1 -3
- package/src/version.ts +1 -1
- package/src/zod/Authorization.ts +6 -24
- package/src/zod/_test/Authorization.test.ts +35 -2
- package/src/zod/_test/TransactionRequest.test.ts +1 -1
|
@@ -151,6 +151,43 @@ describe('fromRpc', () => {
|
|
|
151
151
|
}
|
|
152
152
|
`)
|
|
153
153
|
})
|
|
154
|
+
|
|
155
|
+
test('behavior: yParity above 1', () => {
|
|
156
|
+
expect(
|
|
157
|
+
Authorization.fromRpc({
|
|
158
|
+
address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
159
|
+
chainId: '0x0',
|
|
160
|
+
nonce: '0x6',
|
|
161
|
+
r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
162
|
+
s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
163
|
+
yParity: '0x1c',
|
|
164
|
+
}),
|
|
165
|
+
).toMatchInlineSnapshot(`
|
|
166
|
+
{
|
|
167
|
+
"address": "0xc498cea4bfedbb8de856491bdd9967e677775f4f",
|
|
168
|
+
"chainId": 0,
|
|
169
|
+
"nonce": 6n,
|
|
170
|
+
"r": "0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512",
|
|
171
|
+
"s": "0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191",
|
|
172
|
+
"yParity": 28,
|
|
173
|
+
}
|
|
174
|
+
`)
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
test('error: yParity above 2**8 - 1', () => {
|
|
178
|
+
expect(() =>
|
|
179
|
+
Authorization.fromRpc({
|
|
180
|
+
address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
181
|
+
chainId: '0x0',
|
|
182
|
+
nonce: '0x6',
|
|
183
|
+
r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
184
|
+
s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
185
|
+
yParity: '0x100',
|
|
186
|
+
}),
|
|
187
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
188
|
+
`[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
|
|
189
|
+
)
|
|
190
|
+
})
|
|
154
191
|
})
|
|
155
192
|
|
|
156
193
|
describe('fromRpcList', () => {
|
|
@@ -251,6 +288,44 @@ describe('fromTuple', () => {
|
|
|
251
288
|
}
|
|
252
289
|
`)
|
|
253
290
|
})
|
|
291
|
+
|
|
292
|
+
test('behavior: yParity above 1', () => {
|
|
293
|
+
const tuple = [
|
|
294
|
+
'0x',
|
|
295
|
+
'0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
296
|
+
'0x06',
|
|
297
|
+
'0x1c',
|
|
298
|
+
'0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
299
|
+
'0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
300
|
+
] as const satisfies Authorization.Tuple
|
|
301
|
+
const authorization = Authorization.fromTuple(tuple)
|
|
302
|
+
expect(authorization).toMatchInlineSnapshot(`
|
|
303
|
+
{
|
|
304
|
+
"address": "0xc498cea4bfedbb8de856491bdd9967e677775f4f",
|
|
305
|
+
"chainId": 0,
|
|
306
|
+
"nonce": 6n,
|
|
307
|
+
"r": "0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512",
|
|
308
|
+
"s": "0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191",
|
|
309
|
+
"yParity": 28,
|
|
310
|
+
}
|
|
311
|
+
`)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('error: yParity above 2**8 - 1', () => {
|
|
315
|
+
const tuple = [
|
|
316
|
+
'0x',
|
|
317
|
+
'0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
318
|
+
'0x06',
|
|
319
|
+
'0x0100',
|
|
320
|
+
'0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
321
|
+
'0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
322
|
+
] as const satisfies Authorization.Tuple
|
|
323
|
+
expect(() =>
|
|
324
|
+
Authorization.fromTuple(tuple),
|
|
325
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
326
|
+
`[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
|
|
327
|
+
)
|
|
328
|
+
})
|
|
254
329
|
})
|
|
255
330
|
|
|
256
331
|
describe('fromTupleList', () => {
|
|
@@ -454,6 +529,43 @@ describe('toRpc', () => {
|
|
|
454
529
|
expect(fromBigint).toEqual(fromNumber)
|
|
455
530
|
expect(fromBigint).toEqual(fromHex)
|
|
456
531
|
})
|
|
532
|
+
|
|
533
|
+
test('behavior: yParity above 1', () => {
|
|
534
|
+
expect(
|
|
535
|
+
Authorization.toRpc({
|
|
536
|
+
address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
537
|
+
chainId: 0,
|
|
538
|
+
nonce: 6n,
|
|
539
|
+
r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
540
|
+
s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
541
|
+
yParity: 28,
|
|
542
|
+
}),
|
|
543
|
+
).toMatchInlineSnapshot(`
|
|
544
|
+
{
|
|
545
|
+
"address": "0xc498cea4bfedbb8de856491bdd9967e677775f4f",
|
|
546
|
+
"chainId": "0x0",
|
|
547
|
+
"nonce": "0x6",
|
|
548
|
+
"r": "0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512",
|
|
549
|
+
"s": "0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191",
|
|
550
|
+
"yParity": "0x1c",
|
|
551
|
+
}
|
|
552
|
+
`)
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
test('error: yParity above 2**8 - 1', () => {
|
|
556
|
+
expect(() =>
|
|
557
|
+
Authorization.toRpc({
|
|
558
|
+
address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
559
|
+
chainId: 0,
|
|
560
|
+
nonce: 6n,
|
|
561
|
+
r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
562
|
+
s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
563
|
+
yParity: 256,
|
|
564
|
+
}),
|
|
565
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
566
|
+
`[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
|
|
567
|
+
)
|
|
568
|
+
})
|
|
457
569
|
})
|
|
458
570
|
|
|
459
571
|
describe('toRpcList', () => {
|
|
@@ -540,6 +652,60 @@ describe('toTuple', () => {
|
|
|
540
652
|
`)
|
|
541
653
|
}
|
|
542
654
|
})
|
|
655
|
+
|
|
656
|
+
test('behavior: yParity above 1', () => {
|
|
657
|
+
const authorization = Authorization.from({
|
|
658
|
+
address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
659
|
+
chainId: 0,
|
|
660
|
+
nonce: 6n,
|
|
661
|
+
r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
662
|
+
s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
663
|
+
yParity: 28,
|
|
664
|
+
})
|
|
665
|
+
const tuple = Authorization.toTuple(authorization)
|
|
666
|
+
expect(tuple).toMatchInlineSnapshot(`
|
|
667
|
+
[
|
|
668
|
+
"0x",
|
|
669
|
+
"0xc498cea4bfedbb8de856491bdd9967e677775f4f",
|
|
670
|
+
"0x6",
|
|
671
|
+
"0x1c",
|
|
672
|
+
"0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512",
|
|
673
|
+
"0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191",
|
|
674
|
+
]
|
|
675
|
+
`)
|
|
676
|
+
})
|
|
677
|
+
|
|
678
|
+
test('error: yParity above 2**8 - 1', () => {
|
|
679
|
+
const authorization = Authorization.from({
|
|
680
|
+
address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
681
|
+
chainId: 0,
|
|
682
|
+
nonce: 6n,
|
|
683
|
+
r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
684
|
+
s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
685
|
+
yParity: 256,
|
|
686
|
+
})
|
|
687
|
+
expect(() =>
|
|
688
|
+
Authorization.toTuple(authorization),
|
|
689
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
690
|
+
`[Hex.IntegerOutOfRangeError: Number \`256\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
|
|
691
|
+
)
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
test('error: yParity not an integer', () => {
|
|
695
|
+
const authorization = Authorization.from({
|
|
696
|
+
address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
697
|
+
chainId: 0,
|
|
698
|
+
nonce: 6n,
|
|
699
|
+
r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
700
|
+
s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
701
|
+
yParity: Number.NaN,
|
|
702
|
+
})
|
|
703
|
+
expect(() =>
|
|
704
|
+
Authorization.toTuple(authorization),
|
|
705
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
706
|
+
`[Hex.IntegerOutOfRangeError: Number \`NaN\` is not in safe 8-bit unsigned integer range (\`0\` to \`255\`)]`,
|
|
707
|
+
)
|
|
708
|
+
})
|
|
543
709
|
})
|
|
544
710
|
|
|
545
711
|
describe('toTupleList', () => {
|
|
@@ -26,6 +26,23 @@ describe('assert', () => {
|
|
|
26
26
|
Hex values must start with \`"0x"\` and contain only hexadecimal characters (0-9, a-f, A-F).]
|
|
27
27
|
`)
|
|
28
28
|
})
|
|
29
|
+
|
|
30
|
+
test('options: even', () => {
|
|
31
|
+
expect(() => Hex.assert('0x0', { even: true }))
|
|
32
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
33
|
+
[Hex.InvalidLengthError: Hex value \`"0x0"\` is an odd length (1 nibbles).
|
|
34
|
+
|
|
35
|
+
It must be an even length.]
|
|
36
|
+
`)
|
|
37
|
+
expect(() => Hex.assert('0x400', { even: true }))
|
|
38
|
+
.toThrowErrorMatchingInlineSnapshot(`
|
|
39
|
+
[Hex.InvalidLengthError: Hex value \`"0x400"\` is an odd length (3 nibbles).
|
|
40
|
+
|
|
41
|
+
It must be an even length.]
|
|
42
|
+
`)
|
|
43
|
+
expect(() => Hex.assert('0x00', { even: true })).not.toThrow()
|
|
44
|
+
expect(() => Hex.assert('0x0', { even: false })).not.toThrow()
|
|
45
|
+
})
|
|
29
46
|
})
|
|
30
47
|
|
|
31
48
|
describe('concat', () => {
|
|
@@ -750,6 +767,20 @@ describe('validate', () => {
|
|
|
750
767
|
expect(Hex.validate({ foo: 'bar' })).toBeFalsy()
|
|
751
768
|
expect(Hex.validate(undefined)).toBeFalsy()
|
|
752
769
|
})
|
|
770
|
+
|
|
771
|
+
test('options: even', () => {
|
|
772
|
+
expect(Hex.validate('0x', { even: true })).toMatchInlineSnapshot(`true`)
|
|
773
|
+
expect(Hex.validate('0x00', { even: true })).toMatchInlineSnapshot(`true`)
|
|
774
|
+
expect(Hex.validate('0x0400', { even: true })).toMatchInlineSnapshot(`true`)
|
|
775
|
+
expect(Hex.validate('0x0', { even: true })).toMatchInlineSnapshot(`false`)
|
|
776
|
+
expect(Hex.validate('0x400', { even: true })).toMatchInlineSnapshot(`false`)
|
|
777
|
+
expect(Hex.validate('0x0g', { even: true })).toMatchInlineSnapshot(`true`)
|
|
778
|
+
expect(
|
|
779
|
+
Hex.validate('0x0g', { even: true, strict: true }),
|
|
780
|
+
).toMatchInlineSnapshot(`false`)
|
|
781
|
+
expect(Hex.validate('0xg', { even: true })).toMatchInlineSnapshot(`false`)
|
|
782
|
+
expect(Hex.validate('0x0', { even: false })).toMatchInlineSnapshot(`true`)
|
|
783
|
+
})
|
|
753
784
|
})
|
|
754
785
|
|
|
755
786
|
test('exports', () => {
|
|
@@ -242,6 +242,54 @@ describe('fromHttp', () => {
|
|
|
242
242
|
|
|
243
243
|
await server.close()
|
|
244
244
|
})
|
|
245
|
+
|
|
246
|
+
test('behavior: caller signal without `AbortSignal.any`', async () => {
|
|
247
|
+
const server = await createHttpServer((_, res) => {
|
|
248
|
+
res.end(JSON.stringify({ id: 0, jsonrpc: '2.0', result: 'wagmi' }))
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
const transport = RpcTransport.fromHttp(server.url, {
|
|
252
|
+
timeout: 50,
|
|
253
|
+
fetchOptions: {
|
|
254
|
+
signal: new AbortController().signal,
|
|
255
|
+
},
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
const descriptor = Object.getOwnPropertyDescriptor(AbortSignal, 'any')!
|
|
259
|
+
Object.defineProperty(AbortSignal, 'any', {
|
|
260
|
+
configurable: true,
|
|
261
|
+
value: undefined,
|
|
262
|
+
})
|
|
263
|
+
try {
|
|
264
|
+
await expect(
|
|
265
|
+
transport.request({ method: 'eth_accounts' }),
|
|
266
|
+
).resolves.toMatchInlineSnapshot(`"wagmi"`)
|
|
267
|
+
} finally {
|
|
268
|
+
Object.defineProperty(AbortSignal, 'any', descriptor)
|
|
269
|
+
await server.close()
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
test('behavior: timeout covers a response body that stalls after its headers', async () => {
|
|
274
|
+
const server = await createHttpServer(async (_, res) => {
|
|
275
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
276
|
+
res.write('{"id":0,"jsonrpc":"2.0",')
|
|
277
|
+
await setTimeout(200)
|
|
278
|
+
res.end('"result":"wagmi"}')
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
const transport = RpcTransport.fromHttp(server.url, {
|
|
282
|
+
timeout: 50,
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
await expect(() =>
|
|
286
|
+
transport.request({ method: 'eth_accounts' }),
|
|
287
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
288
|
+
`[Promise.TimeoutError: Operation timed out.]`,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
await server.close()
|
|
292
|
+
})
|
|
245
293
|
})
|
|
246
294
|
|
|
247
295
|
test('exports', () => {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Bytes,
|
|
3
|
+
Hex,
|
|
4
|
+
Secp256k1,
|
|
5
|
+
Signature,
|
|
6
|
+
Solidity,
|
|
7
|
+
TxEnvelopeEip1559,
|
|
8
|
+
} from 'ox'
|
|
2
9
|
import { describe, expect, test } from 'vp/test'
|
|
3
10
|
|
|
4
11
|
describe('assert', () => {
|
|
@@ -488,6 +495,40 @@ describe('serialize', () => {
|
|
|
488
495
|
`"0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8"`,
|
|
489
496
|
)
|
|
490
497
|
})
|
|
498
|
+
|
|
499
|
+
test('behavior: `r` and `s` shorter than 32 bytes', () => {
|
|
500
|
+
const signature = Signature.toHex({
|
|
501
|
+
r: '0x1cdb3cbf94c7059e0e16d2ba046daef144d77384995a66f379194b638fc07e',
|
|
502
|
+
s: '0xc3d1ccd2c334876b9f8ef0ba3725c30dd5e1def225df36efa406d881ed610b',
|
|
503
|
+
yParity: 1,
|
|
504
|
+
})
|
|
505
|
+
expect(signature).toMatchInlineSnapshot(
|
|
506
|
+
`"0x001cdb3cbf94c7059e0e16d2ba046daef144d77384995a66f379194b638fc07e00c3d1ccd2c334876b9f8ef0ba3725c30dd5e1def225df36efa406d881ed610b1c"`,
|
|
507
|
+
)
|
|
508
|
+
expect(Hex.size(signature)).toMatchInlineSnapshot(`65`)
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
test('behavior: short `s` round-trips through recovery', () => {
|
|
512
|
+
const payload =
|
|
513
|
+
'0x44caf484852acee09d52b2802dba26d3c8ab3d28c7abcc1398b87898115633ec'
|
|
514
|
+
const signature = Signature.fromHex(
|
|
515
|
+
Signature.toHex({
|
|
516
|
+
r: '0xdf1cdb3cbf94c7059e0e16d2ba046daef144d77384995a66f379194b638fc07e',
|
|
517
|
+
s: '0xc3d1ccd2c334876b9f8ef0ba3725c30dd5e1def225df36efa406d881ed610b',
|
|
518
|
+
yParity: 1,
|
|
519
|
+
}),
|
|
520
|
+
)
|
|
521
|
+
expect(signature).toMatchInlineSnapshot(`
|
|
522
|
+
{
|
|
523
|
+
"r": "0xdf1cdb3cbf94c7059e0e16d2ba046daef144d77384995a66f379194b638fc07e",
|
|
524
|
+
"s": "0x00c3d1ccd2c334876b9f8ef0ba3725c30dd5e1def225df36efa406d881ed610b",
|
|
525
|
+
"yParity": 1,
|
|
526
|
+
}
|
|
527
|
+
`)
|
|
528
|
+
expect(
|
|
529
|
+
Secp256k1.recoverAddress({ payload, signature }),
|
|
530
|
+
).toMatchInlineSnapshot(`"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"`)
|
|
531
|
+
})
|
|
491
532
|
})
|
|
492
533
|
|
|
493
534
|
describe('toBytes', () => {
|
|
@@ -61,6 +61,40 @@ describe('createMessage', () => {
|
|
|
61
61
|
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
URI: https://example.com/path
|
|
65
|
+
Version: 1
|
|
66
|
+
Chain ID: 1
|
|
67
|
+
Nonce: foobarbaz
|
|
68
|
+
Issued At: 2023-02-01T00:00:00.000Z"
|
|
69
|
+
`)
|
|
70
|
+
|
|
71
|
+
expect(
|
|
72
|
+
Siwe.createMessage({
|
|
73
|
+
...message,
|
|
74
|
+
domain: 'example.xn--p1ai',
|
|
75
|
+
}),
|
|
76
|
+
).toMatchInlineSnapshot(`
|
|
77
|
+
"example.xn--p1ai wants you to sign in with your Ethereum account:
|
|
78
|
+
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
URI: https://example.com/path
|
|
82
|
+
Version: 1
|
|
83
|
+
Chain ID: 1
|
|
84
|
+
Nonce: foobarbaz
|
|
85
|
+
Issued At: 2023-02-01T00:00:00.000Z"
|
|
86
|
+
`)
|
|
87
|
+
|
|
88
|
+
expect(
|
|
89
|
+
Siwe.createMessage({
|
|
90
|
+
...message,
|
|
91
|
+
domain: 'example.XN--P1AI',
|
|
92
|
+
}),
|
|
93
|
+
).toMatchInlineSnapshot(`
|
|
94
|
+
"example.XN--P1AI wants you to sign in with your Ethereum account:
|
|
95
|
+
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e
|
|
96
|
+
|
|
97
|
+
|
|
64
98
|
URI: https://example.com/path
|
|
65
99
|
Version: 1
|
|
66
100
|
Chain ID: 1
|
|
@@ -431,6 +465,12 @@ describe('createMessage', () => {
|
|
|
431
465
|
'example.com:3000',
|
|
432
466
|
'localhost:3000',
|
|
433
467
|
'127.0.0.1:3000',
|
|
468
|
+
'example.xN--p1ai',
|
|
469
|
+
'example.xn--p1ai:443',
|
|
470
|
+
'example.xn--vermgensberater-ctb',
|
|
471
|
+
// 63 octets: "xn--" plus 59 LDH characters.
|
|
472
|
+
`example.xn--${'a'.repeat(59)}`,
|
|
473
|
+
`example.${'a'.repeat(63)}`,
|
|
434
474
|
])('valid domain `%s`', (domain) => {
|
|
435
475
|
expect(
|
|
436
476
|
Siwe.createMessage({
|
|
@@ -449,6 +489,13 @@ describe('createMessage', () => {
|
|
|
449
489
|
'http://127.0.0.1:3000',
|
|
450
490
|
'foobarbaz',
|
|
451
491
|
'-example.com',
|
|
492
|
+
'example.xn--',
|
|
493
|
+
'example.xn--p1ai-',
|
|
494
|
+
'example.xx--p1ai',
|
|
495
|
+
'xn--p1ai',
|
|
496
|
+
// 64 octets: one past the maximum label length.
|
|
497
|
+
`example.xn--${'a'.repeat(60)}`,
|
|
498
|
+
`example.${'a'.repeat(64)}`,
|
|
452
499
|
])('invalid domain `%s`', (domain) => {
|
|
453
500
|
expect(() =>
|
|
454
501
|
Siwe.createMessage({
|
|
@@ -459,6 +506,12 @@ describe('createMessage', () => {
|
|
|
459
506
|
})
|
|
460
507
|
})
|
|
461
508
|
|
|
509
|
+
describe('domainRegex', () => {
|
|
510
|
+
test('behavior: captures the port in group 3', () => {
|
|
511
|
+
expect(Siwe.domainRegex.exec('example.xn--p1ai:443')?.[3]).toBe(':443')
|
|
512
|
+
})
|
|
513
|
+
})
|
|
514
|
+
|
|
462
515
|
describe('generateNonce', () => {
|
|
463
516
|
test('default', () => {
|
|
464
517
|
const nonce = Siwe.generateNonce()
|
|
@@ -181,6 +181,40 @@ describe('deserialize', () => {
|
|
|
181
181
|
})
|
|
182
182
|
})
|
|
183
183
|
|
|
184
|
+
test('behavior: authorization yParity above 1', () => {
|
|
185
|
+
// Mainnet transaction 0xaec8420968abd74e70ef5b3099d6ba7baa3f069c4f718d7c4d729d591cffcb09,
|
|
186
|
+
// whose authorization holds a `y_parity` of 28.
|
|
187
|
+
const serialized =
|
|
188
|
+
'0x04f8c90102843b9aca0084408206be830186a094db35624115a810cf541988294a57766cd02acc828080c0f85cf85a8094c498cea4bfedbb8de856491bdd9967e677775f4f061ca044754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512a027a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b19180a0287d894b2ebb8a178a55810b242af186c73de663382358363d58b42628bbded3a0678f589cd41571248f24df7e60912d05f14c76471b859f4abd956c612bf175ee'
|
|
189
|
+
|
|
190
|
+
const envelope = TxEnvelopeEip7702.deserialize(serialized)
|
|
191
|
+
expect(envelope).toMatchInlineSnapshot(`
|
|
192
|
+
{
|
|
193
|
+
"authorizationList": [
|
|
194
|
+
{
|
|
195
|
+
"address": "0xc498cea4bfedbb8de856491bdd9967e677775f4f",
|
|
196
|
+
"chainId": 0,
|
|
197
|
+
"nonce": 6n,
|
|
198
|
+
"r": "0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512",
|
|
199
|
+
"s": "0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191",
|
|
200
|
+
"yParity": 28,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
"chainId": 1,
|
|
204
|
+
"gas": 100000n,
|
|
205
|
+
"maxFeePerGas": 1082263230n,
|
|
206
|
+
"maxPriorityFeePerGas": 1000000000n,
|
|
207
|
+
"nonce": 2n,
|
|
208
|
+
"r": "0x287d894b2ebb8a178a55810b242af186c73de663382358363d58b42628bbded3",
|
|
209
|
+
"s": "0x678f589cd41571248f24df7e60912d05f14c76471b859f4abd956c612bf175ee",
|
|
210
|
+
"to": "0xdb35624115a810cf541988294a57766cd02acc82",
|
|
211
|
+
"type": "eip7702",
|
|
212
|
+
"yParity": 0,
|
|
213
|
+
}
|
|
214
|
+
`)
|
|
215
|
+
expect(TxEnvelopeEip7702.serialize(envelope)).toBe(serialized)
|
|
216
|
+
})
|
|
217
|
+
|
|
184
218
|
describe('raw', () => {
|
|
185
219
|
test('default', () => {
|
|
186
220
|
const serialized = `0x04${Rlp.fromHex([
|
|
@@ -529,6 +563,38 @@ describe('hash', () => {
|
|
|
529
563
|
`"0xa5ccd813e459b9154917af19270091cf3312bd759af08e0e18cd2560ef586cf4"`,
|
|
530
564
|
)
|
|
531
565
|
})
|
|
566
|
+
|
|
567
|
+
test('behavior: authorization yParity above 1', () => {
|
|
568
|
+
// Mainnet transaction 0xaec8420968abd74e70ef5b3099d6ba7baa3f069c4f718d7c4d729d591cffcb09,
|
|
569
|
+
// whose authorization holds a `y_parity` of 28.
|
|
570
|
+
const envelope = TxEnvelopeEip7702.from({
|
|
571
|
+
authorizationList: [
|
|
572
|
+
{
|
|
573
|
+
address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
574
|
+
chainId: 0,
|
|
575
|
+
nonce: 6n,
|
|
576
|
+
r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
577
|
+
s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
578
|
+
yParity: 28,
|
|
579
|
+
},
|
|
580
|
+
],
|
|
581
|
+
chainId: 1,
|
|
582
|
+
gas: 100000n,
|
|
583
|
+
maxFeePerGas: 1082263230n,
|
|
584
|
+
maxPriorityFeePerGas: 1000000000n,
|
|
585
|
+
nonce: 2n,
|
|
586
|
+
r: '0x287d894b2ebb8a178a55810b242af186c73de663382358363d58b42628bbded3',
|
|
587
|
+
s: '0x678f589cd41571248f24df7e60912d05f14c76471b859f4abd956c612bf175ee',
|
|
588
|
+
to: '0xdb35624115a810cf541988294a57766cd02acc82',
|
|
589
|
+
value: 0n,
|
|
590
|
+
yParity: 0,
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
const hash = TxEnvelopeEip7702.hash(envelope)
|
|
594
|
+
expect(hash).toMatchInlineSnapshot(
|
|
595
|
+
`"0xaec8420968abd74e70ef5b3099d6ba7baa3f069c4f718d7c4d729d591cffcb09"`,
|
|
596
|
+
)
|
|
597
|
+
})
|
|
532
598
|
})
|
|
533
599
|
|
|
534
600
|
describe('serialize', () => {
|
|
@@ -791,6 +791,23 @@ describe('extractEip712DomainTypes', () => {
|
|
|
791
791
|
test('empty', () => {
|
|
792
792
|
expect(TypedData.extractEip712DomainTypes({})).toMatchInlineSnapshot('[]')
|
|
793
793
|
})
|
|
794
|
+
|
|
795
|
+
test('empty version', () => {
|
|
796
|
+
expect(
|
|
797
|
+
TypedData.extractEip712DomainTypes({ name: 'Ether Mail', version: '' }),
|
|
798
|
+
).toMatchInlineSnapshot(`
|
|
799
|
+
[
|
|
800
|
+
{
|
|
801
|
+
"name": "name",
|
|
802
|
+
"type": "string",
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
"name": "version",
|
|
806
|
+
"type": "string",
|
|
807
|
+
},
|
|
808
|
+
]
|
|
809
|
+
`)
|
|
810
|
+
})
|
|
794
811
|
})
|
|
795
812
|
|
|
796
813
|
describe('getSignPayload', () => {
|
|
@@ -849,6 +866,18 @@ describe('getSignPayload', () => {
|
|
|
849
866
|
)
|
|
850
867
|
})
|
|
851
868
|
|
|
869
|
+
test('domain: empty version', () => {
|
|
870
|
+
expect(
|
|
871
|
+
TypedData.getSignPayload({
|
|
872
|
+
...typedData.complex,
|
|
873
|
+
domain: { name: 'Ether Mail', version: '' },
|
|
874
|
+
primaryType: 'Mail',
|
|
875
|
+
}),
|
|
876
|
+
).toMatchInlineSnapshot(
|
|
877
|
+
`"0xa437e12ee2fd4391429d9714be617951b7e69fb61dc520ad6fb8230d03b7508c"`,
|
|
878
|
+
)
|
|
879
|
+
})
|
|
880
|
+
|
|
852
881
|
test('domain: bigint chainId produces same hash as number chainId', () => {
|
|
853
882
|
const typedDataWithNumberChainId = {
|
|
854
883
|
domain: {
|
|
@@ -4,6 +4,7 @@ import type * as AbiItem from '../AbiItem.js'
|
|
|
4
4
|
import type * as AbiParameters from '../AbiParameters.js'
|
|
5
5
|
import * as Address from '../Address.js'
|
|
6
6
|
import * as Errors from '../Errors.js'
|
|
7
|
+
import { getArrayComponents } from './abiParameters.js'
|
|
7
8
|
import type {
|
|
8
9
|
Compute,
|
|
9
10
|
IsNever,
|
|
@@ -570,20 +571,6 @@ const integerTypeRegex =
|
|
|
570
571
|
*/
|
|
571
572
|
const fixedBytesTypeRegex = /^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/
|
|
572
573
|
|
|
573
|
-
/**
|
|
574
|
-
* Fixed-length (`<type>[M]`) / dynamic (`<type>[]`) array regex.
|
|
575
|
-
*
|
|
576
|
-
* @internal
|
|
577
|
-
*/
|
|
578
|
-
const arrayTypeRegex = /[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* Strips a single trailing `[...]` array suffix from a type.
|
|
582
|
-
*
|
|
583
|
-
* @internal
|
|
584
|
-
*/
|
|
585
|
-
const arraySuffixStripRegex = /(\[[0-9]{0,}\])$/
|
|
586
|
-
|
|
587
574
|
/** @internal */
|
|
588
575
|
export function isArgOfType(
|
|
589
576
|
arg: unknown,
|
|
@@ -604,9 +591,12 @@ export function isArgOfType(
|
|
|
604
591
|
if (abiParameterType === 'tuple' && 'components' in abiParameter)
|
|
605
592
|
return Object.values(abiParameter.components).every(
|
|
606
593
|
(component, index) => {
|
|
607
|
-
return
|
|
608
|
-
|
|
609
|
-
|
|
594
|
+
return (
|
|
595
|
+
argType === 'object' &&
|
|
596
|
+
isArgOfType(
|
|
597
|
+
getComponentValue(arg, component, index),
|
|
598
|
+
component as AbiParameters.Parameter,
|
|
599
|
+
)
|
|
610
600
|
)
|
|
611
601
|
},
|
|
612
602
|
)
|
|
@@ -617,14 +607,17 @@ export function isArgOfType(
|
|
|
617
607
|
if (fixedBytesTypeRegex.test(abiParameterType))
|
|
618
608
|
return argType === 'string' || arg instanceof Uint8Array
|
|
619
609
|
|
|
620
|
-
|
|
610
|
+
// Fixed-length (`<type>[M]`) and dynamic (`<type>[]`) arrays.
|
|
611
|
+
const arrayComponents = getArrayComponents(abiParameterType)
|
|
612
|
+
if (arrayComponents) {
|
|
613
|
+
const [length, type] = arrayComponents
|
|
621
614
|
return (
|
|
622
615
|
Array.isArray(arg) &&
|
|
616
|
+
(length === null || arg.length === length) &&
|
|
623
617
|
arg.every((x: unknown) =>
|
|
624
618
|
isArgOfType(x, {
|
|
625
619
|
...abiParameter,
|
|
626
|
-
|
|
627
|
-
type: abiParameterType.replace(arraySuffixStripRegex, ''),
|
|
620
|
+
type,
|
|
628
621
|
} as AbiParameters.Parameter),
|
|
629
622
|
)
|
|
630
623
|
)
|
|
@@ -649,19 +642,49 @@ export function getAmbiguousTypes(
|
|
|
649
642
|
parameterIndex++
|
|
650
643
|
) {
|
|
651
644
|
const sourceParameter = sourceParameters[parameterIndex]!
|
|
652
|
-
const targetParameter = targetParameters[parameterIndex]
|
|
645
|
+
const targetParameter = targetParameters[parameterIndex]
|
|
646
|
+
if (!targetParameter) continue
|
|
653
647
|
|
|
654
648
|
if (
|
|
655
649
|
sourceParameter.type === 'tuple' &&
|
|
656
650
|
targetParameter.type === 'tuple' &&
|
|
657
651
|
'components' in sourceParameter &&
|
|
658
652
|
'components' in targetParameter
|
|
659
|
-
)
|
|
660
|
-
|
|
653
|
+
) {
|
|
654
|
+
const value = args[parameterIndex]
|
|
655
|
+
const types = getAmbiguousTypes(
|
|
661
656
|
sourceParameter.components,
|
|
662
657
|
targetParameter.components,
|
|
663
|
-
(
|
|
658
|
+
sourceParameter.components.map((component, index) =>
|
|
659
|
+
getComponentValue(value, component, index),
|
|
660
|
+
),
|
|
661
|
+
)
|
|
662
|
+
if (types) return types
|
|
663
|
+
continue
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const sourceArray = getArrayComponents(sourceParameter.type)
|
|
667
|
+
const targetArray = getArrayComponents(targetParameter.type)
|
|
668
|
+
if (sourceArray && targetArray) {
|
|
669
|
+
// Every item matches both element types, so the first item (or `undefined` for an empty array) decides.
|
|
670
|
+
const types = getAmbiguousTypes(
|
|
671
|
+
[
|
|
672
|
+
{
|
|
673
|
+
...sourceParameter,
|
|
674
|
+
type: sourceArray[1],
|
|
675
|
+
} as AbiParameters.Parameter,
|
|
676
|
+
],
|
|
677
|
+
[
|
|
678
|
+
{
|
|
679
|
+
...targetParameter,
|
|
680
|
+
type: targetArray[1],
|
|
681
|
+
} as AbiParameters.Parameter,
|
|
682
|
+
],
|
|
683
|
+
[(args[parameterIndex] as readonly unknown[] | undefined)?.[0]],
|
|
664
684
|
)
|
|
685
|
+
if (types) return types
|
|
686
|
+
continue
|
|
687
|
+
}
|
|
665
688
|
|
|
666
689
|
const sourceType = sourceParameter.type
|
|
667
690
|
const targetType = targetParameter.type
|
|
@@ -679,10 +702,21 @@ export function getAmbiguousTypes(
|
|
|
679
702
|
(sourceType === 'address' && targetType === 'bytes') ||
|
|
680
703
|
(sourceType === 'bytes' && targetType === 'address')
|
|
681
704
|
)
|
|
682
|
-
|
|
705
|
+
// An `undefined` value comes from an empty array, which matches both types.
|
|
706
|
+
ambiguous = arg === undefined || Address.validate(arg, { strict: false })
|
|
683
707
|
|
|
684
708
|
if (ambiguous) return [sourceType, targetType]
|
|
685
709
|
}
|
|
686
710
|
|
|
687
711
|
return
|
|
688
712
|
}
|
|
713
|
+
|
|
714
|
+
// Reads a tuple component's value by index from arrays and by name from objects, like `AbiParameters.encode`.
|
|
715
|
+
function getComponentValue(
|
|
716
|
+
arg: unknown,
|
|
717
|
+
component: AbiParameters.Parameter,
|
|
718
|
+
index: number,
|
|
719
|
+
): unknown {
|
|
720
|
+
if (Array.isArray(arg)) return arg[index]
|
|
721
|
+
return (arg as Record<string, unknown> | undefined)?.[component.name!]
|
|
722
|
+
}
|