ox 1.8.3 → 1.8.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/CHANGELOG.md +31 -0
- package/dist/core/Transaction.d.ts +5 -2
- package/dist/core/Transaction.d.ts.map +1 -1
- package/dist/core/Transaction.js +8 -0
- package/dist/core/Transaction.js.map +1 -1
- package/dist/core/TransactionRequest.d.ts +5 -2
- package/dist/core/TransactionRequest.d.ts.map +1 -1
- package/dist/core/TransactionRequest.js +8 -0
- package/dist/core/TransactionRequest.js.map +1 -1
- package/dist/core/TxEnvelope.d.ts +1 -1
- package/dist/core/TxEnvelope.d.ts.map +1 -1
- package/dist/core/TxEnvelope.js +2 -1
- package/dist/core/TxEnvelope.js.map +1 -1
- package/dist/core/TxEnvelopeEip8141.d.ts +18 -7
- package/dist/core/TxEnvelopeEip8141.d.ts.map +1 -1
- package/dist/core/TxEnvelopeEip8141.js +37 -8
- package/dist/core/TxEnvelopeEip8141.js.map +1 -1
- package/dist/core/internal/frameNonce.d.ts +10 -0
- package/dist/core/internal/frameNonce.d.ts.map +1 -0
- package/dist/core/internal/frameNonce.js +45 -0
- package/dist/core/internal/frameNonce.js.map +1 -0
- package/dist/core/internal/rpcSchemas/eth.d.ts +11 -2
- package/dist/core/internal/rpcSchemas/eth.d.ts.map +1 -1
- package/dist/zod/Block.d.ts +3 -0
- package/dist/zod/Block.d.ts.map +1 -1
- package/dist/zod/RpcSchema.d.ts +24 -1
- package/dist/zod/RpcSchema.d.ts.map +1 -1
- package/dist/zod/Transaction.d.ts +6 -0
- package/dist/zod/Transaction.d.ts.map +1 -1
- package/dist/zod/Transaction.js +18 -11
- package/dist/zod/Transaction.js.map +1 -1
- package/dist/zod/TransactionEnvelope.d.ts +10 -0
- package/dist/zod/TransactionEnvelope.d.ts.map +1 -1
- package/dist/zod/TransactionRequest.d.ts +2 -0
- package/dist/zod/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/TransactionRequest.js +9 -3
- package/dist/zod/TransactionRequest.js.map +1 -1
- package/dist/zod/TxEnvelopeEip8141.d.ts +7 -0
- package/dist/zod/TxEnvelopeEip8141.d.ts.map +1 -1
- package/dist/zod/TxEnvelopeEip8141.js +2 -0
- package/dist/zod/TxEnvelopeEip8141.js.map +1 -1
- package/dist/zod/internal/Frame.d.ts +8 -0
- package/dist/zod/internal/Frame.d.ts.map +1 -1
- package/dist/zod/internal/Frame.js +29 -0
- package/dist/zod/internal/Frame.js.map +1 -1
- package/dist/zod/internal/rpcSchemas/Eth.d.ts +24 -1
- package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
- package/dist/zod/internal/rpcSchemas/Eth.js +6 -1
- package/dist/zod/internal/rpcSchemas/Eth.js.map +1 -1
- package/dist/zod/internal/rpcSchemas/Wallet.d.ts +2 -0
- package/dist/zod/internal/rpcSchemas/Wallet.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +4 -0
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +3 -0
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/Transaction.ts +17 -0
- package/src/core/TransactionRequest.ts +12 -0
- package/src/core/TxEnvelope.ts +7 -2
- package/src/core/TxEnvelopeEip8141.ts +61 -13
- package/src/core/_test/RpcSchema.test-d.ts +31 -1
- package/src/core/_test/TransactionEnvelope.test-d.ts +27 -0
- package/src/core/_test/TransactionEnvelope.test.ts +26 -0
- package/src/core/_test/TransactionRequest.test.ts +28 -0
- package/src/core/_test/TxEnvelopeEip8141.test-d.ts +27 -0
- package/src/core/_test/TxEnvelopeEip8141.test.ts +456 -2
- package/src/core/internal/frameNonce.ts +61 -0
- package/src/core/internal/rpcSchemas/eth.ts +10 -1
- package/src/version.ts +1 -1
- package/src/zod/Transaction.ts +42 -23
- package/src/zod/TransactionRequest.ts +32 -17
- package/src/zod/TxEnvelopeEip8141.ts +2 -0
- package/src/zod/_test/RpcSchema.test-d.ts +18 -0
- package/src/zod/_test/RpcSchema.test.ts +157 -0
- package/src/zod/_test/TxEnvelopeEip8141.nonce.test.ts +174 -0
- package/src/zod/_test/TxEnvelopeEip8141.test-d.ts +29 -0
- package/src/zod/internal/Frame.ts +45 -0
- package/src/zod/internal/rpcSchemas/Eth.ts +6 -1
|
@@ -5,6 +5,7 @@ import * as z_Authorization from './Authorization.js'
|
|
|
5
5
|
import * as z_FrameRequest from './FrameRequest.js'
|
|
6
6
|
import * as z_FrameSignature from './FrameSignature.js'
|
|
7
7
|
import * as z_Hex from './Hex.js'
|
|
8
|
+
import { nonceKeys, nonceKeysToRpc, validNonce } from './internal/Frame.js'
|
|
8
9
|
import * as z_Number from './Number.js'
|
|
9
10
|
import * as z_Uint from './Uint.js'
|
|
10
11
|
import * as z from 'zod/mini'
|
|
@@ -33,31 +34,44 @@ const type = z.codec(z.string(), z.string(), {
|
|
|
33
34
|
})
|
|
34
35
|
|
|
35
36
|
/** Transaction request schema. */
|
|
36
|
-
export const TransactionRequest = z
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
export const TransactionRequest = z
|
|
38
|
+
.object(
|
|
39
|
+
fields(
|
|
40
|
+
z_Uint.Uint,
|
|
41
|
+
z_Number.Number,
|
|
42
|
+
z_Authorization.ListSigned,
|
|
43
|
+
z_FrameRequest.FrameRequest,
|
|
44
|
+
nonceKeys,
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
.check(z.refine(validNonce, 'Invalid keyed nonce'))
|
|
44
48
|
|
|
45
49
|
/** Encode-only transaction request schema accepting numberish `toRpc` inputs. */
|
|
46
|
-
export const TransactionRequestToRpc = z
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
export const TransactionRequestToRpc = z
|
|
51
|
+
.object(
|
|
52
|
+
fields(
|
|
53
|
+
z_Uint.UintToRpc,
|
|
54
|
+
z_Number.NumberToRpc,
|
|
55
|
+
z_Authorization.ListSignedToRpc,
|
|
56
|
+
z_FrameRequest.FrameRequestToRpc,
|
|
57
|
+
nonceKeysToRpc,
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
.check(z.refine(validNonce, 'Invalid keyed nonce'))
|
|
54
61
|
|
|
55
62
|
function fields<
|
|
56
63
|
uint extends z.ZodMiniType,
|
|
57
64
|
num extends z.ZodMiniType,
|
|
58
65
|
authorizationList extends z.ZodMiniType,
|
|
59
66
|
frame extends z.ZodMiniType,
|
|
60
|
-
|
|
67
|
+
keys extends z.ZodMiniType,
|
|
68
|
+
>(
|
|
69
|
+
uint: uint,
|
|
70
|
+
num: num,
|
|
71
|
+
authorizationList: authorizationList,
|
|
72
|
+
frame: frame,
|
|
73
|
+
keys: keys,
|
|
74
|
+
) {
|
|
61
75
|
return {
|
|
62
76
|
accessList: z.optional(z_AccessList.AccessList),
|
|
63
77
|
authorizationList: z.optional(authorizationList),
|
|
@@ -74,6 +88,7 @@ function fields<
|
|
|
74
88
|
maxFeePerGas: z.optional(uint),
|
|
75
89
|
maxPriorityFeePerGas: z.optional(uint),
|
|
76
90
|
nonce: z.optional(uint),
|
|
91
|
+
nonceKeys: z.optional(keys),
|
|
77
92
|
signatures: z.optional(
|
|
78
93
|
z.readonly(z.array(z_FrameSignature.FrameSignature)),
|
|
79
94
|
),
|
|
@@ -25,6 +25,7 @@ export const Rpc = z
|
|
|
25
25
|
maxFeePerGas: z_Hex.Hex,
|
|
26
26
|
maxPriorityFeePerGas: z_Hex.Hex,
|
|
27
27
|
nonce: z_Hex.Hex,
|
|
28
|
+
nonceKeys: z.optional(z.readonly(z.array(z_Hex.Hex))),
|
|
28
29
|
signatures: z.readonly(z.array(z_FrameSignature.Rpc)),
|
|
29
30
|
type: z.literal('0x6'),
|
|
30
31
|
})
|
|
@@ -94,6 +95,7 @@ function fields<
|
|
|
94
95
|
maxFeePerGas: z.optional(uint),
|
|
95
96
|
maxPriorityFeePerGas: z.optional(uint),
|
|
96
97
|
nonce: z.optional(uint),
|
|
98
|
+
nonceKeys: z.optional(z.readonly(z.array(uint))),
|
|
97
99
|
sender: z_Address.Address,
|
|
98
100
|
sidecars: z.optional(z_TxEnvelopeEip4844.Sidecars),
|
|
99
101
|
signatures: z.optional(z.readonly(z.array(z_FrameSignature.Decoded))),
|
|
@@ -82,6 +82,7 @@ test('encode helpers infer params/returns by method', () => {
|
|
|
82
82
|
| core_Block.Tag
|
|
83
83
|
| core_Block.Identifier<core_Hex.Hex>
|
|
84
84
|
),
|
|
85
|
+
(readonly core_Hex.Hex[] | undefined)?,
|
|
85
86
|
]
|
|
86
87
|
>()
|
|
87
88
|
})
|
|
@@ -103,6 +104,7 @@ test('codecs accept a resolved item and infer params/returns', () => {
|
|
|
103
104
|
| core_Block.Tag
|
|
104
105
|
| core_Block.Identifier<core_Hex.Hex>
|
|
105
106
|
),
|
|
107
|
+
(readonly core_Hex.Hex[] | undefined)?,
|
|
106
108
|
]
|
|
107
109
|
>()
|
|
108
110
|
|
|
@@ -141,3 +143,19 @@ test('from: single method returns an Item', () => {
|
|
|
141
143
|
})
|
|
142
144
|
expectTypeOf(item.method).toEqualTypeOf<'abe_foo'>()
|
|
143
145
|
})
|
|
146
|
+
|
|
147
|
+
test('eth_getTransactionCount nonce keys preserve wire and decoded types', () => {
|
|
148
|
+
type Params = typeof z_RpcSchema.Eth.eth_getTransactionCount.params
|
|
149
|
+
expectTypeOf<z.input<Params>[2]>().toEqualTypeOf<
|
|
150
|
+
readonly core_Hex.Hex[] | undefined
|
|
151
|
+
>()
|
|
152
|
+
expectTypeOf<z.output<Params>[2]>().toEqualTypeOf<
|
|
153
|
+
readonly (core_Hex.Hex | bigint | number)[] | undefined
|
|
154
|
+
>()
|
|
155
|
+
expectTypeOf<
|
|
156
|
+
z.input<typeof z_RpcSchema.Eth.eth_getTransactionCount.returns>
|
|
157
|
+
>().toEqualTypeOf<core_Hex.Hex>()
|
|
158
|
+
expectTypeOf<
|
|
159
|
+
z.output<typeof z_RpcSchema.Eth.eth_getTransactionCount.returns>
|
|
160
|
+
>().toEqualTypeOf<number>()
|
|
161
|
+
})
|
|
@@ -7,6 +7,131 @@ const hash = `0x${'11'.repeat(32)}` as const
|
|
|
7
7
|
const topic = `0x${'33'.repeat(32)}` as const
|
|
8
8
|
|
|
9
9
|
describe('decodeParams', () => {
|
|
10
|
+
test.each([
|
|
11
|
+
'latest',
|
|
12
|
+
'pending',
|
|
13
|
+
'0x10',
|
|
14
|
+
{ blockHash: hash },
|
|
15
|
+
{ blockNumber: '0x10' },
|
|
16
|
+
] as const)('decodes keyed nonce queries at %j', (block) => {
|
|
17
|
+
const params = z_RpcSchema.decodeParams(
|
|
18
|
+
z_RpcSchema.Eth,
|
|
19
|
+
'eth_getTransactionCount',
|
|
20
|
+
[address, block, ['0x1', '0xa']],
|
|
21
|
+
)
|
|
22
|
+
expect(params[2]).toMatchInlineSnapshot(`
|
|
23
|
+
[
|
|
24
|
+
1n,
|
|
25
|
+
10n,
|
|
26
|
+
]
|
|
27
|
+
`)
|
|
28
|
+
expect(
|
|
29
|
+
z_RpcSchema.encodeParams(
|
|
30
|
+
z_RpcSchema.Eth,
|
|
31
|
+
'eth_getTransactionCount',
|
|
32
|
+
params,
|
|
33
|
+
),
|
|
34
|
+
).toEqual([address, block, ['0x1', '0xa']])
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('preserves legacy nonce query params', () => {
|
|
38
|
+
expect(
|
|
39
|
+
z_RpcSchema.decodeParams(z_RpcSchema.Eth, 'eth_getTransactionCount', [
|
|
40
|
+
address,
|
|
41
|
+
'pending',
|
|
42
|
+
]),
|
|
43
|
+
).toMatchInlineSnapshot(`
|
|
44
|
+
[
|
|
45
|
+
"0x0000000000000000000000000000000000000000",
|
|
46
|
+
"pending",
|
|
47
|
+
]
|
|
48
|
+
`)
|
|
49
|
+
expect(
|
|
50
|
+
z_RpcSchema.decodeParams(z_RpcSchema.Eth, 'eth_getTransactionCount', [
|
|
51
|
+
address,
|
|
52
|
+
'pending',
|
|
53
|
+
['0x0'],
|
|
54
|
+
]),
|
|
55
|
+
).toMatchInlineSnapshot(`
|
|
56
|
+
[
|
|
57
|
+
"0x0000000000000000000000000000000000000000",
|
|
58
|
+
"pending",
|
|
59
|
+
[
|
|
60
|
+
0n,
|
|
61
|
+
],
|
|
62
|
+
]
|
|
63
|
+
`)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('accepts nonce key limits', () => {
|
|
67
|
+
expect(
|
|
68
|
+
z_RpcSchema.decodeParams(z_RpcSchema.Eth, 'eth_getTransactionCount', [
|
|
69
|
+
address,
|
|
70
|
+
'latest',
|
|
71
|
+
[`0x${'f'.repeat(64)}`],
|
|
72
|
+
])[2],
|
|
73
|
+
).toMatchInlineSnapshot(`
|
|
74
|
+
[
|
|
75
|
+
115792089237316195423570985008687907853269984665640564039457584007913129639935n,
|
|
76
|
+
]
|
|
77
|
+
`)
|
|
78
|
+
expect(
|
|
79
|
+
z_RpcSchema.decodeParams(z_RpcSchema.Eth, 'eth_getTransactionCount', [
|
|
80
|
+
address,
|
|
81
|
+
'latest',
|
|
82
|
+
Array.from(
|
|
83
|
+
{ length: 16 },
|
|
84
|
+
(_, i) => `0x${(i + 1).toString(16)}` as const,
|
|
85
|
+
),
|
|
86
|
+
])[2]?.length,
|
|
87
|
+
).toMatchInlineSnapshot(`16`)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test.each(
|
|
91
|
+
[
|
|
92
|
+
[],
|
|
93
|
+
['0x1', '0x1'],
|
|
94
|
+
['0x2', '0x1'],
|
|
95
|
+
['0x0', '0x1'],
|
|
96
|
+
['0x01'],
|
|
97
|
+
['0x'],
|
|
98
|
+
['0xg'],
|
|
99
|
+
[`0x1${'0'.repeat(64)}`],
|
|
100
|
+
Array.from(
|
|
101
|
+
{ length: 17 },
|
|
102
|
+
(_, i) => `0x${(i + 1).toString(16)}` as const,
|
|
103
|
+
),
|
|
104
|
+
[1],
|
|
105
|
+
'0x1',
|
|
106
|
+
null,
|
|
107
|
+
].map((keys) => ({ keys })),
|
|
108
|
+
)('rejects invalid nonce keys $keys', ({ keys }) => {
|
|
109
|
+
expect(
|
|
110
|
+
z.safeParse(z_RpcSchema.Eth.eth_getTransactionCount.params, [
|
|
111
|
+
address,
|
|
112
|
+
'latest',
|
|
113
|
+
keys,
|
|
114
|
+
]).success,
|
|
115
|
+
).toMatchInlineSnapshot(`false`)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test('rejects misplaced nonce keys and extra parameters', () => {
|
|
119
|
+
expect(
|
|
120
|
+
z.safeParse(z_RpcSchema.Eth.eth_getTransactionCount.params, [
|
|
121
|
+
address,
|
|
122
|
+
['0x1'],
|
|
123
|
+
]).success,
|
|
124
|
+
).toMatchInlineSnapshot(`false`)
|
|
125
|
+
expect(
|
|
126
|
+
z.safeParse(z_RpcSchema.Eth.eth_getTransactionCount.params, [
|
|
127
|
+
address,
|
|
128
|
+
'latest',
|
|
129
|
+
['0x1'],
|
|
130
|
+
'0x2',
|
|
131
|
+
]).success,
|
|
132
|
+
).toMatchInlineSnapshot(`false`)
|
|
133
|
+
})
|
|
134
|
+
|
|
10
135
|
test('decodes params (block number / tag coercion)', () => {
|
|
11
136
|
expect(
|
|
12
137
|
z_RpcSchema.decodeParams(z_RpcSchema.Eth, 'eth_getBlockByNumber', [
|
|
@@ -149,6 +274,38 @@ describe('decodeReturns', () => {
|
|
|
149
274
|
})
|
|
150
275
|
|
|
151
276
|
describe('encodeParams', () => {
|
|
277
|
+
test('encodes nonce keys as canonical quantities', () => {
|
|
278
|
+
expect(
|
|
279
|
+
z_RpcSchema.encodeParams(z_RpcSchema.Eth, 'eth_getTransactionCount', [
|
|
280
|
+
address,
|
|
281
|
+
'pending',
|
|
282
|
+
[1n, 10, '0x10'],
|
|
283
|
+
]),
|
|
284
|
+
).toMatchInlineSnapshot(`
|
|
285
|
+
[
|
|
286
|
+
"0x0000000000000000000000000000000000000000",
|
|
287
|
+
"pending",
|
|
288
|
+
[
|
|
289
|
+
"0x1",
|
|
290
|
+
"0xa",
|
|
291
|
+
"0x10",
|
|
292
|
+
],
|
|
293
|
+
]
|
|
294
|
+
`)
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
test('rejects unordered nonce keys when encoding', () => {
|
|
298
|
+
expect(() =>
|
|
299
|
+
z_RpcSchema.encodeParams(z_RpcSchema.Eth, 'eth_getTransactionCount', [
|
|
300
|
+
address,
|
|
301
|
+
'pending',
|
|
302
|
+
[2n, 1n],
|
|
303
|
+
]),
|
|
304
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
305
|
+
`[FrameNonce.InvalidError: Nonce keys must be strictly increasing.]`,
|
|
306
|
+
)
|
|
307
|
+
})
|
|
308
|
+
|
|
152
309
|
test('encodes params (native → wire)', () => {
|
|
153
310
|
expect(
|
|
154
311
|
z_RpcSchema.encodeParams(z_RpcSchema.Eth, 'eth_getBlockByNumber', [
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { TxEnvelopeEip8141 } from 'ox'
|
|
2
|
+
import { z } from 'ox/zod'
|
|
3
|
+
import { describe, expect, test } from 'vp/test'
|
|
4
|
+
|
|
5
|
+
const envelope = TxEnvelopeEip8141.from({
|
|
6
|
+
chainId: 1,
|
|
7
|
+
frames: [{}],
|
|
8
|
+
sender: '0x1111111111111111111111111111111111111111',
|
|
9
|
+
nonceKeys: [1n, 2n ** 256n - 1n],
|
|
10
|
+
nonce: 2n ** 64n - 2n,
|
|
11
|
+
})
|
|
12
|
+
const rpc = TxEnvelopeEip8141.toRpc(envelope)
|
|
13
|
+
|
|
14
|
+
describe('TxEnvelopeEip8141', () => {
|
|
15
|
+
test('retains keyed nonces through envelope codecs', () => {
|
|
16
|
+
const decoded = z.decode(z.TransactionEnvelope.TransactionEnvelope, rpc)
|
|
17
|
+
expect(decoded).toEqual(TxEnvelopeEip8141.fromRpc(rpc))
|
|
18
|
+
expect(
|
|
19
|
+
z.encode(z.TransactionEnvelope.TransactionEnvelope, envelope),
|
|
20
|
+
).toEqual(rpc)
|
|
21
|
+
expect(
|
|
22
|
+
z.encode(z.TransactionEnvelope.TransactionEnvelopeToRpc, {
|
|
23
|
+
...envelope,
|
|
24
|
+
nonceKeys: ['0x1', 2n ** 256n - 1n],
|
|
25
|
+
nonce: '0xfffffffffffffffe',
|
|
26
|
+
}),
|
|
27
|
+
).toEqual(rpc)
|
|
28
|
+
const serialized = z.encode(z.TransactionEnvelope.serialized, envelope)
|
|
29
|
+
expect(z.decode(z.TransactionEnvelope.serialized, serialized)).toEqual(
|
|
30
|
+
decoded,
|
|
31
|
+
)
|
|
32
|
+
expect(z.encode(z.TransactionEnvelope.Signed, envelope)).toEqual(rpc)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test.each([
|
|
36
|
+
{ nonceKeys: [] },
|
|
37
|
+
{ nonceKeys: ['0x0', '0x1'] },
|
|
38
|
+
{ nonceKeys: ['0x2', '0x1'] },
|
|
39
|
+
{ nonceKeys: ['0x1', '0x1'] },
|
|
40
|
+
{ nonceKeys: ['0x01'] },
|
|
41
|
+
{ nonceKeys: ['0x0'], nonce: '0xffffffffffffffff' },
|
|
42
|
+
{ nonce: '0x10000000000000000' },
|
|
43
|
+
{ nonce: '0x00' },
|
|
44
|
+
{ nonce: undefined },
|
|
45
|
+
{ nonceKeys: null },
|
|
46
|
+
])('rejects invalid keyed RPC envelope %#', (fields) => {
|
|
47
|
+
expect(
|
|
48
|
+
z.safeDecode(z.TxEnvelopeEip8141.TxEnvelopeEip8141, {
|
|
49
|
+
...rpc,
|
|
50
|
+
...fields,
|
|
51
|
+
} as never).success,
|
|
52
|
+
).toBe(false)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('rejects invalid decoded and numberish keys', () => {
|
|
56
|
+
for (const keys of [[], [0n, 1n], [2n, 1n], [1n, 1n], [2n ** 256n]]) {
|
|
57
|
+
expect(
|
|
58
|
+
z.safeEncode(z.TxEnvelopeEip8141.TxEnvelopeEip8141, {
|
|
59
|
+
...envelope,
|
|
60
|
+
nonceKeys: keys,
|
|
61
|
+
}).success,
|
|
62
|
+
).toBe(false)
|
|
63
|
+
expect(
|
|
64
|
+
z.safeEncode(z.TxEnvelopeEip8141.TxEnvelopeEip8141ToRpc, {
|
|
65
|
+
...envelope,
|
|
66
|
+
nonceKeys: keys,
|
|
67
|
+
}).success,
|
|
68
|
+
).toBe(false)
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('TransactionRequest', () => {
|
|
74
|
+
test('retains keys while leaving sequence and frame limits unassigned', () => {
|
|
75
|
+
const input = {
|
|
76
|
+
type: '0x6',
|
|
77
|
+
nonceKeys: ['0x1', '0x2'],
|
|
78
|
+
frames: [{ mode: '0x2' }, { mode: '0x2', executionGas: '0x0' }],
|
|
79
|
+
} as const
|
|
80
|
+
const decoded = z.decode(z.TransactionRequest.TransactionRequest, input)
|
|
81
|
+
expect(decoded.nonceKeys).toMatchInlineSnapshot(`
|
|
82
|
+
[
|
|
83
|
+
1n,
|
|
84
|
+
2n,
|
|
85
|
+
]
|
|
86
|
+
`)
|
|
87
|
+
expect(decoded.nonce).toBeUndefined()
|
|
88
|
+
expect(decoded.frames?.map((frame) => frame.executionGas)).toEqual([
|
|
89
|
+
undefined,
|
|
90
|
+
0n,
|
|
91
|
+
])
|
|
92
|
+
const encoded = z.encode(z.TransactionRequest.TransactionRequest, decoded)
|
|
93
|
+
expect(encoded.nonceKeys).toEqual(input.nonceKeys)
|
|
94
|
+
expect(encoded.nonce).toBeUndefined()
|
|
95
|
+
expect(
|
|
96
|
+
z.encode(z.TransactionRequest.TransactionRequestToRpc, {
|
|
97
|
+
nonceKeys: [1, '0x2'],
|
|
98
|
+
nonce: 0,
|
|
99
|
+
}).nonceKeys,
|
|
100
|
+
).toEqual(input.nonceKeys)
|
|
101
|
+
})
|
|
102
|
+
test.each([[], ['0x0', '0x1'], ['0x2', '0x1'], ['0x01'], ['0x1', '0x1']])(
|
|
103
|
+
'rejects invalid keys %#',
|
|
104
|
+
(...nonceKeys) => {
|
|
105
|
+
expect(
|
|
106
|
+
z.safeDecode(z.TransactionRequest.TransactionRequest, {
|
|
107
|
+
nonceKeys,
|
|
108
|
+
} as never).success,
|
|
109
|
+
).toBe(false)
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
test('rejects an exhausted sequence', () => {
|
|
113
|
+
expect(
|
|
114
|
+
z.safeDecode(z.TransactionRequest.TransactionRequest, {
|
|
115
|
+
nonceKeys: ['0x1'],
|
|
116
|
+
nonce: '0xffffffffffffffff',
|
|
117
|
+
}).success,
|
|
118
|
+
).toBe(false)
|
|
119
|
+
expect(
|
|
120
|
+
z.safeEncode(z.TransactionRequest.TransactionRequestToRpc, {
|
|
121
|
+
nonceKeys: [1],
|
|
122
|
+
nonce: '0xffffffffffffffff',
|
|
123
|
+
}).success,
|
|
124
|
+
).toBe(false)
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
describe('Transaction', () => {
|
|
129
|
+
const pending = {
|
|
130
|
+
...rpc,
|
|
131
|
+
blockHash: null,
|
|
132
|
+
blockNumber: null,
|
|
133
|
+
transactionIndex: null,
|
|
134
|
+
hash: `0x${'11'.repeat(32)}`,
|
|
135
|
+
} as const
|
|
136
|
+
const mined = {
|
|
137
|
+
...pending,
|
|
138
|
+
blockHash: `0x${'22'.repeat(32)}`,
|
|
139
|
+
blockNumber: '0x1',
|
|
140
|
+
transactionIndex: '0x0',
|
|
141
|
+
} as const
|
|
142
|
+
test('preserves pending and mined keyed transaction results', () => {
|
|
143
|
+
const decoded = z.decode(z.Transaction.PendingEip8141, pending)
|
|
144
|
+
expect(decoded.nonceKeys).toEqual(envelope.nonceKeys)
|
|
145
|
+
expect(z.encode(z.Transaction.PendingEip8141, decoded)).toEqual(pending)
|
|
146
|
+
const result = z.decode(z.Transaction.Transaction, mined)
|
|
147
|
+
expect('nonceKeys' in result && result.nonceKeys).toEqual(
|
|
148
|
+
envelope.nonceKeys,
|
|
149
|
+
)
|
|
150
|
+
expect(z.encode(z.Transaction.Transaction, result)).toEqual(mined)
|
|
151
|
+
expect(z.encode(z.Transaction.TransactionToRpc, result)).toEqual(mined)
|
|
152
|
+
})
|
|
153
|
+
test('rejects invalid keyed transaction results', () => {
|
|
154
|
+
for (const fields of [
|
|
155
|
+
{ nonceKeys: ['0x2', '0x1'] },
|
|
156
|
+
{ nonceKeys: ['0x0', '0x1'] },
|
|
157
|
+
{ nonce: '0xffffffffffffffff' },
|
|
158
|
+
{ nonce: undefined },
|
|
159
|
+
]) {
|
|
160
|
+
expect(
|
|
161
|
+
z.safeDecode(z.Transaction.Transaction, {
|
|
162
|
+
...mined,
|
|
163
|
+
...fields,
|
|
164
|
+
} as never).success,
|
|
165
|
+
).toBe(false)
|
|
166
|
+
expect(
|
|
167
|
+
z.safeDecode(z.Transaction.PendingEip8141, {
|
|
168
|
+
...pending,
|
|
169
|
+
...fields,
|
|
170
|
+
} as never).success,
|
|
171
|
+
).toBe(false)
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
})
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Transaction, TransactionRequest, TxEnvelopeEip8141 } from 'ox'
|
|
2
|
+
import { z } from 'ox/zod'
|
|
3
|
+
import { describe, expectTypeOf, test } from 'vp/test'
|
|
4
|
+
|
|
5
|
+
describe('TxEnvelopeEip8141', () => {
|
|
6
|
+
test('nonce keys preserve decoded, RPC, and numberish types', () => {
|
|
7
|
+
expectTypeOf<
|
|
8
|
+
z.output<typeof z.TxEnvelopeEip8141.Decoded>
|
|
9
|
+
>().toEqualTypeOf<TxEnvelopeEip8141.TxEnvelopeEip8141>()
|
|
10
|
+
expectTypeOf<
|
|
11
|
+
z.input<typeof z.TxEnvelopeEip8141.TxEnvelopeEip8141>
|
|
12
|
+
>().toEqualTypeOf<TxEnvelopeEip8141.Rpc>()
|
|
13
|
+
expectTypeOf<
|
|
14
|
+
z.output<typeof z.TxEnvelopeEip8141.TxEnvelopeEip8141ToRpc>
|
|
15
|
+
>().toMatchTypeOf<TxEnvelopeEip8141.toRpc.Input>()
|
|
16
|
+
expectTypeOf<
|
|
17
|
+
z.output<typeof z.TransactionRequest.TransactionRequest>['nonceKeys']
|
|
18
|
+
>().toEqualTypeOf<TransactionRequest.TransactionRequest['nonceKeys']>()
|
|
19
|
+
expectTypeOf<
|
|
20
|
+
z.input<typeof z.TransactionRequest.TransactionRequest>['nonceKeys']
|
|
21
|
+
>().toEqualTypeOf<TransactionRequest.Rpc['nonceKeys']>()
|
|
22
|
+
expectTypeOf<
|
|
23
|
+
z.output<typeof z.Transaction.Eip8141>['nonceKeys']
|
|
24
|
+
>().toEqualTypeOf<Transaction.Eip8141['nonceKeys']>()
|
|
25
|
+
expectTypeOf<
|
|
26
|
+
z.input<typeof z.Transaction.Eip8141>['nonceKeys']
|
|
27
|
+
>().toEqualTypeOf<Transaction.Eip8141Rpc['nonceKeys']>()
|
|
28
|
+
})
|
|
29
|
+
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable jsdoc-js/require-jsdoc, jsdoc-js/require-description, jsdoc-js/require-example */
|
|
2
2
|
import * as Hex from '../../core/Hex.js'
|
|
3
|
+
import * as FrameNonce from '../../core/internal/frameNonce.js'
|
|
3
4
|
import * as Quantity from '../../core/internal/quantity.js'
|
|
4
5
|
import { quantityHex, uintBigint, uintNumber } from './Integer.js'
|
|
5
6
|
import * as z from 'zod/mini'
|
|
@@ -24,3 +25,47 @@ export const chainIdToRpc = z.codec(
|
|
|
24
25
|
encode: (value) => Quantity.fromNumberish(value),
|
|
25
26
|
},
|
|
26
27
|
)
|
|
28
|
+
|
|
29
|
+
export const nonceKeysRpc = z.readonly(z.array(quantityHex(256))).check(
|
|
30
|
+
z.refine((keys) => {
|
|
31
|
+
try {
|
|
32
|
+
FrameNonce.fromRpc(keys)
|
|
33
|
+
return true
|
|
34
|
+
} catch {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
}, 'Invalid nonce keys'),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
export const nonceKeys = z.codec(
|
|
41
|
+
nonceKeysRpc,
|
|
42
|
+
z.readonly(z.array(uintBigint(256))),
|
|
43
|
+
{
|
|
44
|
+
decode: (keys) => FrameNonce.fromRpc(keys),
|
|
45
|
+
encode: (keys) => FrameNonce.toRpc(keys),
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
export const nonceKeysToRpc = z.codec(
|
|
50
|
+
nonceKeysRpc,
|
|
51
|
+
z.readonly(
|
|
52
|
+
z.array(z.union([quantityHex(256), uintBigint(256), uintNumber()])),
|
|
53
|
+
),
|
|
54
|
+
{
|
|
55
|
+
decode: (keys) => FrameNonce.fromRpc(keys),
|
|
56
|
+
encode: (keys) => FrameNonce.toRpc(keys),
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
export function validNonce(value: {
|
|
61
|
+
nonceKeys?: readonly (Hex.Hex | bigint | number)[] | undefined
|
|
62
|
+
nonce?: Hex.Hex | bigint | number | undefined
|
|
63
|
+
}): boolean {
|
|
64
|
+
if (value.nonceKeys === undefined) return true
|
|
65
|
+
try {
|
|
66
|
+
FrameNonce.toRpc(value.nonceKeys, value.nonce)
|
|
67
|
+
return true
|
|
68
|
+
} catch {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -15,6 +15,7 @@ import * as z_TransactionReceipt from '../../TransactionReceipt.js'
|
|
|
15
15
|
import * as z_TransactionRequest from '../../TransactionRequest.js'
|
|
16
16
|
import * as z_Uint from '../../Uint.js'
|
|
17
17
|
import * as z from 'zod/mini'
|
|
18
|
+
import * as Frame from '../Frame.js'
|
|
18
19
|
import { from } from './from.js'
|
|
19
20
|
|
|
20
21
|
const NoParams = z.optional(z.tuple([]))
|
|
@@ -214,7 +215,11 @@ export const eth_getTransactionByHash = from({
|
|
|
214
215
|
|
|
215
216
|
export const eth_getTransactionCount = from({
|
|
216
217
|
method: 'eth_getTransactionCount',
|
|
217
|
-
params: z.tuple([
|
|
218
|
+
params: z.tuple([
|
|
219
|
+
z_Address.Address,
|
|
220
|
+
BlockNumberOrTagOrIdentifier,
|
|
221
|
+
z.optional(Frame.nonceKeysToRpc),
|
|
222
|
+
]),
|
|
218
223
|
returns: z_Number.Number,
|
|
219
224
|
})
|
|
220
225
|
|