ox 1.8.10 → 1.8.13
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 +30 -0
- 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/Siwe.d.ts.map +1 -1
- package/dist/core/Siwe.js +1 -1
- package/dist/core/Siwe.js.map +1 -1
- package/dist/core/TxEnvelope.d.ts.map +1 -1
- package/dist/core/TxEnvelope.js +5 -3
- package/dist/core/TxEnvelope.js.map +1 -1
- package/dist/core/TxEnvelopeEip1559.d.ts.map +1 -1
- package/dist/core/TxEnvelopeEip1559.js +6 -2
- package/dist/core/TxEnvelopeEip1559.js.map +1 -1
- package/dist/core/TxEnvelopeEip4844.d.ts.map +1 -1
- package/dist/core/TxEnvelopeEip4844.js +4 -0
- package/dist/core/TxEnvelopeEip4844.js.map +1 -1
- package/dist/core/TxEnvelopeEip7702.d.ts.map +1 -1
- package/dist/core/TxEnvelopeEip7702.js +4 -0
- package/dist/core/TxEnvelopeEip7702.js.map +1 -1
- package/dist/core/TypedData.d.ts +8 -1
- package/dist/core/TypedData.d.ts.map +1 -1
- package/dist/core/TypedData.js +51 -14
- package/dist/core/TypedData.js.map +1 -1
- package/dist/core/internal/abiItem.d.ts.map +1 -1
- package/dist/core/internal/abiItem.js +44 -13
- package/dist/core/internal/abiItem.js.map +1 -1
- package/dist/core/internal/abiParameters.d.ts +2 -2
- package/dist/core/internal/abiParameters.d.ts.map +1 -1
- package/dist/core/internal/abiParameters.js +24 -7
- package/dist/core/internal/abiParameters.js.map +1 -1
- package/dist/core/internal/rpcSchemas/eth.d.ts +3 -2
- package/dist/core/internal/rpcSchemas/eth.d.ts.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/KeyAuthorization.d.ts +21 -6
- package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/KeyAuthorization.js +7 -4
- package/dist/tempo/KeyAuthorization.js.map +1 -1
- package/dist/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/dist/tempo/TxEnvelopeTempo.js +6 -2
- package/dist/tempo/TxEnvelopeTempo.js.map +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 +5 -2
- package/src/core/Authorization.ts +43 -6
- package/src/core/Siwe.ts +1 -1
- package/src/core/TxEnvelope.ts +5 -3
- package/src/core/TxEnvelopeEip1559.ts +8 -2
- package/src/core/TxEnvelopeEip4844.ts +6 -0
- package/src/core/TxEnvelopeEip7702.ts +6 -0
- package/src/core/TypedData.ts +57 -13
- package/src/core/_test/AbiItem.test.ts +238 -4
- package/src/core/_test/AbiParameters.decode.test.ts +140 -1
- package/src/core/_test/Authorization.test.ts +166 -0
- package/src/core/_test/RpcSchema.test-d.ts +23 -1
- package/src/core/_test/Siwe.test.ts +53 -0
- package/src/core/_test/TransactionEnvelopeEip1559.test.ts +24 -0
- package/src/core/_test/TransactionEnvelopeEip4844.test.ts +13 -0
- package/src/core/_test/TransactionEnvelopeEip7702.test.ts +79 -0
- package/src/core/_test/TypedData.test.ts +188 -1
- package/src/core/internal/abiItem.ts +48 -17
- package/src/core/internal/abiParameters.ts +26 -8
- package/src/core/internal/rpcSchemas/eth.ts +2 -1
- 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/KeyAuthorization.test.ts +6 -4
- package/src/tempo/KeyAuthorization.ts +26 -8
- package/src/tempo/SignatureEnvelope.test.ts +1 -3
- package/src/tempo/TxEnvelopeTempo.test.ts +26 -0
- package/src/tempo/TxEnvelopeTempo.ts +8 -2
- package/src/tempo/configurableAccounts.e2e.test.ts +1 -2
- package/src/tempo/e2e.test.ts +73 -17
- 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
|
@@ -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()
|
|
@@ -27,6 +27,18 @@ describe('assert', () => {
|
|
|
27
27
|
)
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
+
test('tip higher than zero fee cap', () => {
|
|
31
|
+
expect(() =>
|
|
32
|
+
TxEnvelopeEip1559.assert({
|
|
33
|
+
maxFeePerGas: 0n,
|
|
34
|
+
maxPriorityFeePerGas: 1n,
|
|
35
|
+
chainId: 1,
|
|
36
|
+
}),
|
|
37
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
38
|
+
'[TransactionEnvelope.TipAboveFeeCapError: The provided tip (`maxPriorityFeePerGas` = 0.000000001 gwei) cannot be higher than the fee cap (`maxFeePerGas` = 0 gwei).]',
|
|
39
|
+
)
|
|
40
|
+
})
|
|
41
|
+
|
|
30
42
|
test('invalid chainId', () => {
|
|
31
43
|
expect(() =>
|
|
32
44
|
TxEnvelopeEip1559.assert({ chainId: 0 }),
|
|
@@ -225,6 +237,18 @@ describe('deserialize', () => {
|
|
|
225
237
|
})
|
|
226
238
|
|
|
227
239
|
describe('errors', () => {
|
|
240
|
+
test('tip above empty fee cap', () => {
|
|
241
|
+
const serialized = TxEnvelopeEip1559.serialize({
|
|
242
|
+
chainId: 1,
|
|
243
|
+
maxPriorityFeePerGas: 1n,
|
|
244
|
+
})
|
|
245
|
+
expect(() =>
|
|
246
|
+
TxEnvelopeEip1559.deserialize(serialized),
|
|
247
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
248
|
+
`[TransactionEnvelope.TipAboveFeeCapError: The provided tip (\`maxPriorityFeePerGas\` = 0.000000001 gwei) cannot be higher than the fee cap (\`maxFeePerGas\` = 0 gwei).]`,
|
|
249
|
+
)
|
|
250
|
+
})
|
|
251
|
+
|
|
228
252
|
test('invalid access list (invalid address)', () => {
|
|
229
253
|
expect(() =>
|
|
230
254
|
TxEnvelopeEip1559.deserialize(
|
|
@@ -203,6 +203,19 @@ describe('deserialize', () => {
|
|
|
203
203
|
})
|
|
204
204
|
|
|
205
205
|
describe('errors', () => {
|
|
206
|
+
test('tip above empty fee cap', () => {
|
|
207
|
+
const serialized = TxEnvelopeEip4844.serialize({
|
|
208
|
+
blobVersionedHashes,
|
|
209
|
+
chainId: 1,
|
|
210
|
+
maxPriorityFeePerGas: 1n,
|
|
211
|
+
})
|
|
212
|
+
expect(() =>
|
|
213
|
+
TxEnvelopeEip4844.deserialize(serialized),
|
|
214
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
215
|
+
`[TransactionEnvelope.TipAboveFeeCapError: The provided tip (\`maxPriorityFeePerGas\` = 0.000000001 gwei) cannot be higher than the fee cap (\`maxFeePerGas\` = 0 gwei).]`,
|
|
216
|
+
)
|
|
217
|
+
})
|
|
218
|
+
|
|
206
219
|
test('invalid access list (invalid address)', () => {
|
|
207
220
|
expect(() =>
|
|
208
221
|
TxEnvelopeEip4844.deserialize(
|
|
@@ -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([
|
|
@@ -277,6 +311,19 @@ describe('deserialize', () => {
|
|
|
277
311
|
})
|
|
278
312
|
|
|
279
313
|
describe('errors', () => {
|
|
314
|
+
test('tip above empty fee cap', () => {
|
|
315
|
+
const serialized = TxEnvelopeEip7702.serialize({
|
|
316
|
+
authorizationList: [],
|
|
317
|
+
chainId: 1,
|
|
318
|
+
maxPriorityFeePerGas: 1n,
|
|
319
|
+
})
|
|
320
|
+
expect(() =>
|
|
321
|
+
TxEnvelopeEip7702.deserialize(serialized),
|
|
322
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
323
|
+
`[TransactionEnvelope.TipAboveFeeCapError: The provided tip (\`maxPriorityFeePerGas\` = 0.000000001 gwei) cannot be higher than the fee cap (\`maxFeePerGas\` = 0 gwei).]`,
|
|
324
|
+
)
|
|
325
|
+
})
|
|
326
|
+
|
|
280
327
|
test('invalid access list (invalid address)', () => {
|
|
281
328
|
expect(() =>
|
|
282
329
|
TxEnvelopeEip7702.deserialize(
|
|
@@ -529,6 +576,38 @@ describe('hash', () => {
|
|
|
529
576
|
`"0xa5ccd813e459b9154917af19270091cf3312bd759af08e0e18cd2560ef586cf4"`,
|
|
530
577
|
)
|
|
531
578
|
})
|
|
579
|
+
|
|
580
|
+
test('behavior: authorization yParity above 1', () => {
|
|
581
|
+
// Mainnet transaction 0xaec8420968abd74e70ef5b3099d6ba7baa3f069c4f718d7c4d729d591cffcb09,
|
|
582
|
+
// whose authorization holds a `y_parity` of 28.
|
|
583
|
+
const envelope = TxEnvelopeEip7702.from({
|
|
584
|
+
authorizationList: [
|
|
585
|
+
{
|
|
586
|
+
address: '0xc498cea4bfedbb8de856491bdd9967e677775f4f',
|
|
587
|
+
chainId: 0,
|
|
588
|
+
nonce: 6n,
|
|
589
|
+
r: '0x44754b7f217808e21f1935cfa450380e17a3ab8f2c6996c0190d678f0c14a512',
|
|
590
|
+
s: '0x27a6b869798cbbbd8d538dbc95b9bb28783bf08f8ef88ad6bb82552d0b47b191',
|
|
591
|
+
yParity: 28,
|
|
592
|
+
},
|
|
593
|
+
],
|
|
594
|
+
chainId: 1,
|
|
595
|
+
gas: 100000n,
|
|
596
|
+
maxFeePerGas: 1082263230n,
|
|
597
|
+
maxPriorityFeePerGas: 1000000000n,
|
|
598
|
+
nonce: 2n,
|
|
599
|
+
r: '0x287d894b2ebb8a178a55810b242af186c73de663382358363d58b42628bbded3',
|
|
600
|
+
s: '0x678f589cd41571248f24df7e60912d05f14c76471b859f4abd956c612bf175ee',
|
|
601
|
+
to: '0xdb35624115a810cf541988294a57766cd02acc82',
|
|
602
|
+
value: 0n,
|
|
603
|
+
yParity: 0,
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
const hash = TxEnvelopeEip7702.hash(envelope)
|
|
607
|
+
expect(hash).toMatchInlineSnapshot(
|
|
608
|
+
`"0xaec8420968abd74e70ef5b3099d6ba7baa3f069c4f718d7c4d729d591cffcb09"`,
|
|
609
|
+
)
|
|
610
|
+
})
|
|
532
611
|
})
|
|
533
612
|
|
|
534
613
|
describe('serialize', () => {
|
|
@@ -425,7 +425,7 @@ describe('assert', () => {
|
|
|
425
425
|
).toThrowErrorMatchingInlineSnapshot(`
|
|
426
426
|
[TypedData.InvalidStructTypeError: Struct type "address" is invalid.
|
|
427
427
|
|
|
428
|
-
Struct type must not be a Solidity type.]
|
|
428
|
+
Struct type must contain only letters, digits, "_" and "$", and must not be a Solidity type.]
|
|
429
429
|
`)
|
|
430
430
|
})
|
|
431
431
|
|
|
@@ -498,6 +498,161 @@ describe('assert', () => {
|
|
|
498
498
|
[Hex.IntegerOutOfRangeError: Number \`256n\` is not in safe 8-bit unsigned integer range (\`0n\` to \`255n\`)]
|
|
499
499
|
`)
|
|
500
500
|
})
|
|
501
|
+
test.each([
|
|
502
|
+
'1Order',
|
|
503
|
+
'_Order',
|
|
504
|
+
'bytesInfo',
|
|
505
|
+
'Foo$Bar',
|
|
506
|
+
'intent',
|
|
507
|
+
'interval',
|
|
508
|
+
'uintPair',
|
|
509
|
+
])('struct type: %s', (type) => {
|
|
510
|
+
TypedData.assert({
|
|
511
|
+
types: {
|
|
512
|
+
Order: [{ name: 'inner', type }],
|
|
513
|
+
[type]: [{ name: 'amount', type: 'uint256' }],
|
|
514
|
+
},
|
|
515
|
+
primaryType: 'Order',
|
|
516
|
+
message: { inner: { amount: 1n } },
|
|
517
|
+
} as any)
|
|
518
|
+
})
|
|
519
|
+
|
|
520
|
+
test.each([
|
|
521
|
+
'address',
|
|
522
|
+
'bool',
|
|
523
|
+
'bytes',
|
|
524
|
+
'bytes32',
|
|
525
|
+
'bytes32[]',
|
|
526
|
+
'bytes33',
|
|
527
|
+
'Foo.Bar',
|
|
528
|
+
'int',
|
|
529
|
+
'int8',
|
|
530
|
+
'int8[2]',
|
|
531
|
+
'string',
|
|
532
|
+
'uint',
|
|
533
|
+
'uint256',
|
|
534
|
+
'uint256 ',
|
|
535
|
+
'uint256[]',
|
|
536
|
+
'uint7',
|
|
537
|
+
])('invalid struct type: %s', (type) => {
|
|
538
|
+
expect(() =>
|
|
539
|
+
TypedData.assert({
|
|
540
|
+
types: {
|
|
541
|
+
Order: [{ name: 'inner', type }],
|
|
542
|
+
[type]: [{ name: 'amount', type: 'uint256' }],
|
|
543
|
+
},
|
|
544
|
+
primaryType: 'Order',
|
|
545
|
+
message: { inner: { amount: 1n } },
|
|
546
|
+
} as any),
|
|
547
|
+
).toThrowError(`Struct type "${type}" is invalid.`)
|
|
548
|
+
})
|
|
549
|
+
|
|
550
|
+
test('invalid struct type: array element', () => {
|
|
551
|
+
expect(() =>
|
|
552
|
+
TypedData.assert({
|
|
553
|
+
types: {
|
|
554
|
+
Message: [{ name: 'contents', type: 'bytes32[]' }],
|
|
555
|
+
bytes32: [],
|
|
556
|
+
},
|
|
557
|
+
primaryType: 'Message',
|
|
558
|
+
message: { contents: [Hex.padLeft('0x11', 32)] },
|
|
559
|
+
} as any),
|
|
560
|
+
).toThrowErrorMatchingInlineSnapshot(`
|
|
561
|
+
[TypedData.InvalidStructTypeError: Struct type "bytes32" is invalid.
|
|
562
|
+
|
|
563
|
+
Struct type must contain only letters, digits, "_" and "$", and must not be a Solidity type.]
|
|
564
|
+
`)
|
|
565
|
+
})
|
|
566
|
+
|
|
567
|
+
test.each([
|
|
568
|
+
'bytes0',
|
|
569
|
+
'bytes33',
|
|
570
|
+
'int0',
|
|
571
|
+
'int7',
|
|
572
|
+
'uint08',
|
|
573
|
+
'uint7',
|
|
574
|
+
'uint7[]',
|
|
575
|
+
'uint264',
|
|
576
|
+
'uint264[2]',
|
|
577
|
+
])('invalid type: %s', (type) => {
|
|
578
|
+
expect(() =>
|
|
579
|
+
TypedData.assert({
|
|
580
|
+
types: {
|
|
581
|
+
Message: [{ name: 'amount', type }],
|
|
582
|
+
},
|
|
583
|
+
primaryType: 'Message',
|
|
584
|
+
message: {
|
|
585
|
+
amount: type.includes('[')
|
|
586
|
+
? [1n, 2n]
|
|
587
|
+
: type.startsWith('bytes')
|
|
588
|
+
? '0x'
|
|
589
|
+
: 1n,
|
|
590
|
+
},
|
|
591
|
+
} as any),
|
|
592
|
+
).toThrowError(`Type "${type}" is not a valid EIP-712 type.`)
|
|
593
|
+
})
|
|
594
|
+
|
|
595
|
+
test('invalid type: alias', () => {
|
|
596
|
+
expect(() =>
|
|
597
|
+
TypedData.assert({
|
|
598
|
+
types: {
|
|
599
|
+
Message: [{ name: 'amount', type: 'uint' }],
|
|
600
|
+
},
|
|
601
|
+
primaryType: 'Message',
|
|
602
|
+
message: { amount: 1n },
|
|
603
|
+
} as any),
|
|
604
|
+
).toThrowErrorMatchingInlineSnapshot(`
|
|
605
|
+
[TypedData.InvalidTypeError: Type "uint" is not a valid EIP-712 type.
|
|
606
|
+
|
|
607
|
+
Use "uint256" instead.]
|
|
608
|
+
`)
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
test('invalid type: undefined struct', () => {
|
|
612
|
+
expect(() =>
|
|
613
|
+
TypedData.assert({
|
|
614
|
+
types: {
|
|
615
|
+
Mail: [{ name: 'from', type: 'Person' }],
|
|
616
|
+
},
|
|
617
|
+
primaryType: 'Mail',
|
|
618
|
+
message: { from: { name: 'Cow' } },
|
|
619
|
+
} as any),
|
|
620
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
621
|
+
`[TypedData.InvalidTypeError: Type "Person" is not a valid EIP-712 type.]`,
|
|
622
|
+
)
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
test.each(['bytes0', 'Foo', 'uint', 'uint7', 'uint7[]'])(
|
|
626
|
+
'invalid type in array element struct: %s',
|
|
627
|
+
(type) => {
|
|
628
|
+
for (const to of ['Person[]', 'Person[][]', 'Person[2]'])
|
|
629
|
+
expect(() =>
|
|
630
|
+
TypedData.assert({
|
|
631
|
+
types: {
|
|
632
|
+
Mail: [{ name: 'to', type: to }],
|
|
633
|
+
Person: [{ name: 'x', type }],
|
|
634
|
+
},
|
|
635
|
+
primaryType: 'Mail',
|
|
636
|
+
message: { to: [] },
|
|
637
|
+
} as any),
|
|
638
|
+
).toThrowError(`Type "${type}" is not a valid EIP-712 type.`)
|
|
639
|
+
},
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
test.each(['__proto__', 'constructor', 'hasOwnProperty', 'toString'])(
|
|
643
|
+
'invalid type: %s',
|
|
644
|
+
(type) => {
|
|
645
|
+
expect(() =>
|
|
646
|
+
TypedData.assert({
|
|
647
|
+
types: {
|
|
648
|
+
Message: [{ name: 'value', type }],
|
|
649
|
+
},
|
|
650
|
+
primaryType: 'Message',
|
|
651
|
+
message: { value: {} },
|
|
652
|
+
} as any),
|
|
653
|
+
).toThrowError(`Type "${type}" is not a valid EIP-712 type.`)
|
|
654
|
+
},
|
|
655
|
+
)
|
|
501
656
|
})
|
|
502
657
|
|
|
503
658
|
describe('domainSeparator', () => {
|
|
@@ -1054,6 +1209,37 @@ describe('getSignPayload', () => {
|
|
|
1054
1209
|
`"0xd2669f23b7849020ad41bcbff5b51372793f91320e0f901641945568ed7322be"`,
|
|
1055
1210
|
)
|
|
1056
1211
|
})
|
|
1212
|
+
test('struct type: prefixed by a Solidity type', () => {
|
|
1213
|
+
expect(
|
|
1214
|
+
TypedData.getSignPayload({
|
|
1215
|
+
domain: { name: 'Domain' },
|
|
1216
|
+
types: {
|
|
1217
|
+
Message: [{ name: 'intent', type: 'intent' }],
|
|
1218
|
+
intent: [{ name: 'amount', type: 'uint256' }],
|
|
1219
|
+
},
|
|
1220
|
+
primaryType: 'Message',
|
|
1221
|
+
message: { intent: { amount: 1n } },
|
|
1222
|
+
}),
|
|
1223
|
+
).toMatchInlineSnapshot(
|
|
1224
|
+
`"0xcbeb995e95e87817f47e51736d0e5825644055cbcad196c2b26449ad78fd87ee"`,
|
|
1225
|
+
)
|
|
1226
|
+
})
|
|
1227
|
+
|
|
1228
|
+
test('struct type: with $', () => {
|
|
1229
|
+
expect(
|
|
1230
|
+
TypedData.getSignPayload({
|
|
1231
|
+
domain: { name: 'Domain' },
|
|
1232
|
+
types: {
|
|
1233
|
+
Message: [{ name: 'order', type: 'Order$1' }],
|
|
1234
|
+
Order$1: [{ name: 'amount', type: 'uint256' }],
|
|
1235
|
+
},
|
|
1236
|
+
primaryType: 'Message',
|
|
1237
|
+
message: { order: { amount: 1n } },
|
|
1238
|
+
}),
|
|
1239
|
+
).toMatchInlineSnapshot(
|
|
1240
|
+
`"0x97a137d1f96a18c6a214fd8e7b991ed7309ff25f223b68aa63a4670f9838a94a"`,
|
|
1241
|
+
)
|
|
1242
|
+
})
|
|
1057
1243
|
})
|
|
1058
1244
|
|
|
1059
1245
|
describe('hashDomain', () => {
|
|
@@ -1613,6 +1799,7 @@ test('exports', () => {
|
|
|
1613
1799
|
"InvalidDomainError",
|
|
1614
1800
|
"InvalidPrimaryTypeError",
|
|
1615
1801
|
"InvalidStructTypeError",
|
|
1802
|
+
"InvalidTypeError",
|
|
1616
1803
|
"InvalidArrayError",
|
|
1617
1804
|
"InvalidArrayLengthError",
|
|
1618
1805
|
"encodeData",
|
|
@@ -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 * as Hex from '../Hex.js'
|
|
7
8
|
import { getArrayComponents } from './abiParameters.js'
|
|
8
9
|
import type {
|
|
9
10
|
Compute,
|
|
@@ -601,11 +602,32 @@ export function isArgOfType(
|
|
|
601
602
|
},
|
|
602
603
|
)
|
|
603
604
|
|
|
604
|
-
if (integerTypeRegex.test(abiParameterType))
|
|
605
|
-
|
|
605
|
+
if (integerTypeRegex.test(abiParameterType)) {
|
|
606
|
+
if (typeof arg !== 'bigint' && typeof arg !== 'number') return false
|
|
607
|
+
if (typeof arg === 'number' && !Number.isInteger(arg)) return false
|
|
606
608
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
+
// An overload whose integer range excludes the value does not match.
|
|
610
|
+
const signed = abiParameterType.startsWith('int')
|
|
611
|
+
const bits = Number.parseInt(
|
|
612
|
+
abiParameterType.replace(/^u?int/, '') || '256',
|
|
613
|
+
10,
|
|
614
|
+
)
|
|
615
|
+
const max = 2n ** (BigInt(bits) - (signed ? 1n : 0n)) - 1n
|
|
616
|
+
const min = signed ? -max - 1n : 0n
|
|
617
|
+
return arg >= min && arg <= max
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (fixedBytesTypeRegex.test(abiParameterType)) {
|
|
621
|
+
if (typeof arg !== 'string' && !(arg instanceof Uint8Array))
|
|
622
|
+
return false
|
|
623
|
+
// An overload whose fixed size differs from the value does not match.
|
|
624
|
+
const bytes = abiParameterType.slice(5)
|
|
625
|
+
if (!bytes) return true
|
|
626
|
+
if (arg instanceof Uint8Array)
|
|
627
|
+
return arg.length === Number.parseInt(bytes, 10)
|
|
628
|
+
if (!Hex.validate(arg)) return false
|
|
629
|
+
return Hex.size(arg as Hex.Hex) === Number.parseInt(bytes, 10)
|
|
630
|
+
}
|
|
609
631
|
|
|
610
632
|
// Fixed-length (`<type>[M]`) and dynamic (`<type>[]`) arrays.
|
|
611
633
|
const arrayComponents = getArrayComponents(abiParameterType)
|
|
@@ -683,6 +705,15 @@ export function getAmbiguousTypes(
|
|
|
683
705
|
[(args[parameterIndex] as readonly unknown[] | undefined)?.[0]],
|
|
684
706
|
)
|
|
685
707
|
if (types) return types
|
|
708
|
+
// Same element type, different array shape: `uint256[]` and `uint256[2]`
|
|
709
|
+
// both accept `[1n, 1n]`, but one is dynamic and the other is static.
|
|
710
|
+
// Skip this when the element types differ. `string[]` vs `address[]`
|
|
711
|
+
// stays unambiguous for a non-address string.
|
|
712
|
+
if (
|
|
713
|
+
sourceArray[1] === targetArray[1] &&
|
|
714
|
+
sourceParameter.type !== targetParameter.type
|
|
715
|
+
)
|
|
716
|
+
return [sourceParameter.type, targetParameter.type]
|
|
686
717
|
continue
|
|
687
718
|
}
|
|
688
719
|
|
|
@@ -690,20 +721,20 @@ export function getAmbiguousTypes(
|
|
|
690
721
|
const targetType = targetParameter.type
|
|
691
722
|
const arg = args[parameterIndex] as Address.Address
|
|
692
723
|
|
|
693
|
-
|
|
694
|
-
if (
|
|
695
|
-
(sourceType === 'address' && targetType === 'bytes20') ||
|
|
696
|
-
(sourceType === 'bytes20' && targetType === 'address')
|
|
697
|
-
)
|
|
698
|
-
ambiguous = true
|
|
699
|
-
else if (
|
|
700
|
-
(sourceType === 'address' && targetType === 'string') ||
|
|
701
|
-
(sourceType === 'string' && targetType === 'address') ||
|
|
702
|
-
(sourceType === 'address' && targetType === 'bytes') ||
|
|
703
|
-
(sourceType === 'bytes' && targetType === 'address')
|
|
704
|
-
)
|
|
724
|
+
const ambiguous = (() => {
|
|
705
725
|
// An `undefined` value comes from an empty array, which matches both types.
|
|
706
|
-
|
|
726
|
+
// This stays above the catch-all: a non-address string is not ambiguous.
|
|
727
|
+
if (
|
|
728
|
+
(sourceType === 'address' && targetType === 'string') ||
|
|
729
|
+
(sourceType === 'string' && targetType === 'address') ||
|
|
730
|
+
(sourceType === 'address' && targetType === 'bytes') ||
|
|
731
|
+
(sourceType === 'bytes' && targetType === 'address')
|
|
732
|
+
)
|
|
733
|
+
return arg === undefined || Address.validate(arg, { strict: false })
|
|
734
|
+
// Distinct types that both matched cannot be distinguished at runtime
|
|
735
|
+
// (e.g. `bytes` vs `bytes32`, `uint256` vs `int256`, `address` vs `bytes20`).
|
|
736
|
+
return sourceType !== targetType
|
|
737
|
+
})()
|
|
707
738
|
|
|
708
739
|
if (ambiguous) return [sourceType, targetType]
|
|
709
740
|
}
|
|
@@ -178,6 +178,14 @@ export function decodeAddress(
|
|
|
178
178
|
) {
|
|
179
179
|
const { checksum = false } = options
|
|
180
180
|
const value = cursor.readBytes(32)
|
|
181
|
+
// An address occupies the low 20 bytes of the word; the high 12 bytes
|
|
182
|
+
// must be zero padding.
|
|
183
|
+
for (let i = 0; i < 12; i++)
|
|
184
|
+
if (value[i] !== 0)
|
|
185
|
+
throw new Address.InvalidAddressError({
|
|
186
|
+
address: Hex.fromBytes(value),
|
|
187
|
+
cause: new Address.InvalidInputError(),
|
|
188
|
+
})
|
|
181
189
|
const wrap = (address: Hex.Hex) =>
|
|
182
190
|
checksum ? Address.checksum(address) : address
|
|
183
191
|
return [wrap(Hex.fromBytes(Bytes.slice(value, -20))), 32]
|
|
@@ -185,6 +193,7 @@ export function decodeAddress(
|
|
|
185
193
|
|
|
186
194
|
export declare namespace decodeAddress {
|
|
187
195
|
type ErrorType =
|
|
196
|
+
| Address.InvalidAddressError
|
|
188
197
|
| Hex.fromBytes.ErrorType
|
|
189
198
|
| Bytes.slice.ErrorType
|
|
190
199
|
| Errors.GlobalErrorType
|
|
@@ -356,19 +365,28 @@ export function decodeNumber(
|
|
|
356
365
|
) {
|
|
357
366
|
const signed = param.type.startsWith('int')
|
|
358
367
|
const size = Number.parseInt(param.type.split('int')[1] || '256', 10)
|
|
359
|
-
const value = cursor.readBytes(32)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
368
|
+
const value = Bytes.toBigInt(cursor.readBytes(32), { signed })
|
|
369
|
+
// The word must hold a value that fits the declared size (zero-padded for
|
|
370
|
+
// unsigned, sign-extended for signed types).
|
|
371
|
+
if (size < 256) {
|
|
372
|
+
const max = (1n << BigInt(signed ? size - 1 : size)) - 1n
|
|
373
|
+
const min = signed ? -max - 1n : 0n
|
|
374
|
+
if (value > max || value < min)
|
|
375
|
+
throw new Hex.IntegerOutOfRangeError({
|
|
376
|
+
max: max.toString(),
|
|
377
|
+
min: min.toString(),
|
|
378
|
+
signed,
|
|
379
|
+
size: size / 8,
|
|
380
|
+
value: value.toString(),
|
|
381
|
+
})
|
|
382
|
+
}
|
|
383
|
+
return [size > 48 ? value : Number(value), 32]
|
|
366
384
|
}
|
|
367
385
|
|
|
368
386
|
export declare namespace decodeNumber {
|
|
369
387
|
type ErrorType =
|
|
370
|
-
| Bytes.toNumber.ErrorType
|
|
371
388
|
| Bytes.toBigInt.ErrorType
|
|
389
|
+
| Hex.IntegerOutOfRangeError
|
|
372
390
|
| Errors.GlobalErrorType
|
|
373
391
|
}
|
|
374
392
|
|
|
@@ -13,6 +13,7 @@ import type * as Transaction from '../../Transaction.js'
|
|
|
13
13
|
import type * as TransactionReceipt from '../../TransactionReceipt.js'
|
|
14
14
|
import type * as TransactionRequest from '../../TransactionRequest.js'
|
|
15
15
|
import type * as TxEnvelope from '../../TxEnvelope.js'
|
|
16
|
+
import type { OneOf, UnionCompute } from '../types.js'
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Union of all JSON-RPC Methods for the `eth_` namespace.
|
|
@@ -237,7 +238,7 @@ export type Eth = RpcSchema.From<
|
|
|
237
238
|
ReturnType: {
|
|
238
239
|
capabilities?: Record<string, unknown> | undefined
|
|
239
240
|
raw?: Hex.Hex | undefined
|
|
240
|
-
tx: TxEnvelope.Rpc & { from: Address.Address }
|
|
241
|
+
tx: UnionCompute<OneOf<TxEnvelope.Rpc> & { from: Address.Address }>
|
|
241
242
|
}
|
|
242
243
|
}
|
|
243
244
|
/**
|