ox 1.8.1 → 1.8.3
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 +35 -0
- package/dist/core/Frame.d.ts +23 -20
- package/dist/core/Frame.d.ts.map +1 -1
- package/dist/core/Frame.js +28 -23
- package/dist/core/Frame.js.map +1 -1
- package/dist/core/FrameReceipt.d.ts +23 -14
- package/dist/core/FrameReceipt.d.ts.map +1 -1
- package/dist/core/FrameReceipt.js +15 -11
- package/dist/core/FrameReceipt.js.map +1 -1
- package/dist/core/FrameRequest.d.ts +50 -0
- package/dist/core/FrameRequest.d.ts.map +1 -0
- package/dist/core/FrameRequest.js +59 -0
- package/dist/core/FrameRequest.js.map +1 -0
- package/dist/core/FrameSignature.d.ts +17 -16
- package/dist/core/FrameSignature.d.ts.map +1 -1
- package/dist/core/FrameSignature.js +38 -21
- package/dist/core/FrameSignature.js.map +1 -1
- package/dist/core/Siwe.d.ts.map +1 -1
- package/dist/core/Siwe.js +3 -3
- package/dist/core/Siwe.js.map +1 -1
- package/dist/core/Transaction.d.ts +2 -2
- package/dist/core/Transaction.d.ts.map +1 -1
- package/dist/core/Transaction.js.map +1 -1
- package/dist/core/TransactionRequest.d.ts +5 -5
- package/dist/core/TransactionRequest.d.ts.map +1 -1
- package/dist/core/TransactionRequest.js +3 -3
- package/dist/core/TransactionRequest.js.map +1 -1
- package/dist/core/TxEnvelope.d.ts +3 -1
- package/dist/core/TxEnvelope.d.ts.map +1 -1
- package/dist/core/TxEnvelope.js +9 -1
- package/dist/core/TxEnvelope.js.map +1 -1
- package/dist/core/TxEnvelopeEip8141.d.ts +4 -4
- package/dist/core/TxEnvelopeEip8141.js +8 -8
- package/dist/core/TxEnvelopeEip8141.js.map +1 -1
- package/dist/core/internal/rpcSchemas/eth.d.ts +16 -4
- package/dist/core/internal/rpcSchemas/eth.d.ts.map +1 -1
- package/dist/index.d.ts +21 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -5
- package/dist/index.js.map +1 -1
- package/dist/zod/Block.d.ts +49 -37
- package/dist/zod/Block.d.ts.map +1 -1
- package/dist/zod/Frame.d.ts +14 -14
- package/dist/zod/Frame.js +5 -5
- package/dist/zod/Frame.js.map +1 -1
- package/dist/zod/FrameReceipt.d.ts +42 -2
- package/dist/zod/FrameReceipt.d.ts.map +1 -1
- package/dist/zod/FrameReceipt.js +11 -1
- package/dist/zod/FrameReceipt.js.map +1 -1
- package/dist/zod/FrameRequest.d.ts +40 -0
- package/dist/zod/FrameRequest.d.ts.map +1 -0
- package/dist/zod/FrameRequest.js +37 -0
- package/dist/zod/FrameRequest.js.map +1 -0
- package/dist/zod/FrameSignature.d.ts +21 -13
- package/dist/zod/FrameSignature.d.ts.map +1 -1
- package/dist/zod/FrameSignature.js +10 -7
- package/dist/zod/FrameSignature.js.map +1 -1
- package/dist/zod/RpcSchema.d.ts +447 -265
- package/dist/zod/RpcSchema.d.ts.map +1 -1
- package/dist/zod/Transaction.d.ts +98 -74
- package/dist/zod/Transaction.d.ts.map +1 -1
- package/dist/zod/Transaction.js +4 -4
- package/dist/zod/Transaction.js.map +1 -1
- package/dist/zod/TransactionEnvelope.d.ts +50 -34
- package/dist/zod/TransactionEnvelope.d.ts.map +1 -1
- package/dist/zod/TransactionReceipt.d.ts +42 -2
- package/dist/zod/TransactionReceipt.d.ts.map +1 -1
- package/dist/zod/TransactionRequest.d.ts +29 -21
- package/dist/zod/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/TransactionRequest.js +3 -3
- package/dist/zod/TransactionRequest.js.map +1 -1
- package/dist/zod/TxEnvelopeEip8141.d.ts +37 -25
- package/dist/zod/TxEnvelopeEip8141.d.ts.map +1 -1
- package/dist/zod/internal/rpcSchemas/Eth.d.ts +449 -267
- package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
- package/dist/zod/internal/rpcSchemas/Eth.js +9 -0
- package/dist/zod/internal/rpcSchemas/Eth.js.map +1 -1
- package/dist/zod/internal/rpcSchemas/Wallet.d.ts +49 -21
- package/dist/zod/internal/rpcSchemas/Wallet.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +64 -48
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +49 -37
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/z.d.ts +1 -0
- package/dist/zod/z.d.ts.map +1 -1
- package/dist/zod/z.js +1 -0
- package/dist/zod/z.js.map +1 -1
- package/package.json +11 -1
- package/src/core/Frame.ts +41 -33
- package/src/core/FrameReceipt.ts +30 -16
- package/src/core/FrameRequest.ts +78 -0
- package/src/core/FrameSignature.ts +64 -34
- package/src/core/Siwe.ts +21 -13
- package/src/core/Transaction.ts +35 -24
- package/src/core/TransactionRequest.ts +9 -7
- package/src/core/TxEnvelope.ts +9 -1
- package/src/core/TxEnvelopeEip8141.ts +8 -8
- package/src/core/_test/AbiConstructor.test.ts +2 -0
- package/src/core/_test/Frame.test-d.ts +16 -7
- package/src/core/_test/Frame.test.ts +52 -24
- package/src/core/_test/FrameReceipt.test.ts +15 -9
- package/src/core/_test/FrameRequest.test-d.ts +16 -0
- package/src/core/_test/FrameRequest.test.ts +41 -0
- package/src/core/_test/FrameRpc.test.ts +131 -0
- package/src/core/_test/FrameSignature.test-d.ts +20 -0
- package/src/core/_test/FrameSignature.test.ts +132 -11
- package/src/core/_test/RpcSchema.test-d.ts +45 -0
- package/src/core/_test/Siwe.test.ts +147 -0
- package/src/core/_test/TransactionEnvelope.test.ts +1 -1
- package/src/core/_test/TransactionReceipt.test.ts +32 -6
- package/src/core/_test/TransactionRequest.test-d.ts +10 -1
- package/src/core/_test/TransactionRequest.test.ts +3 -2
- package/src/core/_test/TxEnvelopeEip8141.test-d.ts +2 -2
- package/src/core/_test/TxEnvelopeEip8141.test.ts +92 -53
- package/src/core/_test/index.test.ts +1 -0
- package/src/core/internal/rpcSchemas/eth.ts +13 -4
- package/src/index.ts +22 -5
- package/src/version.ts +1 -1
- package/src/zod/Frame.ts +5 -5
- package/src/zod/FrameReceipt.ts +11 -1
- package/src/zod/FrameRequest.ts +46 -0
- package/src/zod/FrameSignature.ts +10 -7
- package/src/zod/Transaction.ts +4 -4
- package/src/zod/TransactionRequest.ts +3 -3
- package/src/zod/_test/Frame.test.ts +13 -11
- package/src/zod/_test/FrameReceipt.test.ts +11 -4
- package/src/zod/_test/FrameRequest.test-d.ts +15 -0
- package/src/zod/_test/FrameRequest.test.ts +44 -0
- package/src/zod/_test/FrameSignature.test.ts +73 -9
- package/src/zod/_test/RpcSchema.test.ts +106 -0
- package/src/zod/_test/TransactionRequest.test.ts +61 -0
- package/src/zod/_test/TxEnvelopeEip8141.test.ts +3 -3
- package/src/zod/internal/rpcSchemas/Eth.ts +20 -0
- package/src/zod/z.ts +1 -0
|
@@ -3,8 +3,8 @@ import { Rlp, Frame } from 'ox'
|
|
|
3
3
|
|
|
4
4
|
const frame = {
|
|
5
5
|
data: '0x',
|
|
6
|
+
executionGas: 50_000n,
|
|
6
7
|
flags: 3,
|
|
7
|
-
gas: 50_000n,
|
|
8
8
|
mode: 1,
|
|
9
9
|
stateGas: 0n,
|
|
10
10
|
value: 0n,
|
|
@@ -38,8 +38,8 @@ describe('toTuple', () => {
|
|
|
38
38
|
expect(
|
|
39
39
|
Frame.toTuple({
|
|
40
40
|
data: undefined,
|
|
41
|
+
executionGas: undefined,
|
|
41
42
|
flags: undefined,
|
|
42
|
-
gas: undefined,
|
|
43
43
|
mode: undefined,
|
|
44
44
|
stateGas: undefined,
|
|
45
45
|
value: undefined,
|
|
@@ -47,8 +47,8 @@ describe('toTuple', () => {
|
|
|
47
47
|
).toEqual(Frame.toTuple({}))
|
|
48
48
|
expect(
|
|
49
49
|
Frame.toTuple({
|
|
50
|
+
executionGas: 50_000n,
|
|
50
51
|
flags: 'approveExecutionAndPayment',
|
|
51
|
-
gas: 50_000n,
|
|
52
52
|
mode: 'verify',
|
|
53
53
|
}),
|
|
54
54
|
).toEqual(tuple)
|
|
@@ -95,9 +95,25 @@ describe('toTuple', () => {
|
|
|
95
95
|
)
|
|
96
96
|
})
|
|
97
97
|
|
|
98
|
+
test('preserves separate execution and state gas budgets', () => {
|
|
99
|
+
const frame = Frame.from({ executionGas: 50_000n, stateGas: 12n })
|
|
100
|
+
const tuple = Frame.toTuple(frame)
|
|
101
|
+
expect(tuple).toEqual(['0x', '0x', '0x', ['0xc350', '0x0c'], '0x', '0x'])
|
|
102
|
+
expect(Frame.fromTuple(tuple)).toEqual({
|
|
103
|
+
data: '0x',
|
|
104
|
+
executionGas: 50_000n,
|
|
105
|
+
flags: 0,
|
|
106
|
+
mode: 0,
|
|
107
|
+
stateGas: 12n,
|
|
108
|
+
value: 0n,
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
98
112
|
test('encodes zero integers as empty bytes', () => {
|
|
99
113
|
expect(
|
|
100
|
-
Rlp.fromHex(
|
|
114
|
+
Rlp.fromHex(
|
|
115
|
+
Frame.toTuple({ ...frame, executionGas: 0n, flags: 0, mode: 0 }),
|
|
116
|
+
),
|
|
101
117
|
).toMatchInlineSnapshot('"0xc8808080c280808080"')
|
|
102
118
|
})
|
|
103
119
|
|
|
@@ -105,8 +121,8 @@ describe('toTuple', () => {
|
|
|
105
121
|
const input = {
|
|
106
122
|
...frame,
|
|
107
123
|
data: '0x0001',
|
|
124
|
+
executionGas: 2n ** 63n,
|
|
108
125
|
flags: 0,
|
|
109
|
-
gas: 2n ** 63n,
|
|
110
126
|
mode: 2,
|
|
111
127
|
stateGas: 2n ** 63n - 1n,
|
|
112
128
|
value: 2n ** 256n - 1n,
|
|
@@ -158,10 +174,20 @@ describe('fromTuple', () => {
|
|
|
158
174
|
})
|
|
159
175
|
|
|
160
176
|
describe('assert', () => {
|
|
177
|
+
test('reports the execution gas field', () => {
|
|
178
|
+
expect(() =>
|
|
179
|
+
Frame.assert({ executionGas: -1n }),
|
|
180
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
181
|
+
`[Frame.InvalidError: Invalid frame.
|
|
182
|
+
|
|
183
|
+
Details: executionGas must be an unsigned 64-bit integer.]`,
|
|
184
|
+
)
|
|
185
|
+
})
|
|
186
|
+
|
|
161
187
|
test.each([
|
|
162
188
|
{ data: null },
|
|
163
189
|
{ flags: null },
|
|
164
|
-
{
|
|
190
|
+
{ executionGas: null },
|
|
165
191
|
{ mode: null },
|
|
166
192
|
{ stateGas: null },
|
|
167
193
|
{ value: null },
|
|
@@ -185,11 +211,11 @@ describe('assert', () => {
|
|
|
185
211
|
{ value: 1n },
|
|
186
212
|
{ mode: 2, value: -1n },
|
|
187
213
|
{ mode: 2, value: 2n ** 256n },
|
|
188
|
-
{
|
|
189
|
-
{
|
|
214
|
+
{ executionGas: -1n },
|
|
215
|
+
{ executionGas: 2n ** 64n },
|
|
190
216
|
{ stateGas: -1n },
|
|
191
217
|
{ stateGas: 2n ** 64n },
|
|
192
|
-
{
|
|
218
|
+
{ executionGas: 2n ** 64n - 1n, stateGas: 1n },
|
|
193
219
|
{ data: '0x0' },
|
|
194
220
|
{ data: '0xgg' },
|
|
195
221
|
{ to: '0x01' },
|
|
@@ -221,19 +247,19 @@ describe('fromRpc', () => {
|
|
|
221
247
|
expect(
|
|
222
248
|
Frame.fromRpc({
|
|
223
249
|
data: '0xdeadbeef',
|
|
224
|
-
|
|
225
|
-
flags:
|
|
226
|
-
mode:
|
|
227
|
-
|
|
250
|
+
executionGas: '0x20000000000001',
|
|
251
|
+
flags: '0x0',
|
|
252
|
+
mode: '0x2',
|
|
253
|
+
stateGas: '0xc',
|
|
228
254
|
target: null,
|
|
229
255
|
value: '0x1',
|
|
230
256
|
}),
|
|
231
257
|
).toEqual({
|
|
232
258
|
data: '0xdeadbeef',
|
|
259
|
+
executionGas: 9007199254740993n,
|
|
233
260
|
flags: 0,
|
|
234
|
-
gas: 9007199254740993n,
|
|
235
261
|
mode: 2,
|
|
236
|
-
stateGas:
|
|
262
|
+
stateGas: 12n,
|
|
237
263
|
value: 1n,
|
|
238
264
|
})
|
|
239
265
|
})
|
|
@@ -247,20 +273,22 @@ describe('toRpc', () => {
|
|
|
247
273
|
),
|
|
248
274
|
).toEqual({
|
|
249
275
|
data: '0x',
|
|
250
|
-
|
|
251
|
-
flags:
|
|
252
|
-
mode:
|
|
253
|
-
|
|
276
|
+
executionGas: '0x0',
|
|
277
|
+
flags: '0x3',
|
|
278
|
+
mode: '0x1',
|
|
279
|
+
stateGas: '0x0',
|
|
254
280
|
value: '0x0',
|
|
255
281
|
})
|
|
256
282
|
})
|
|
257
283
|
test('accepts numberish budgets', () => {
|
|
258
|
-
expect(
|
|
284
|
+
expect(
|
|
285
|
+
Frame.toRpc({ executionGas: '0xc350', stateGas: 12, value: 0n }),
|
|
286
|
+
).toEqual({
|
|
259
287
|
data: '0x',
|
|
260
|
-
|
|
261
|
-
flags:
|
|
262
|
-
mode:
|
|
263
|
-
|
|
288
|
+
executionGas: '0xc350',
|
|
289
|
+
flags: '0x0',
|
|
290
|
+
mode: '0x0',
|
|
291
|
+
stateGas: '0xc',
|
|
264
292
|
value: '0x0',
|
|
265
293
|
})
|
|
266
294
|
})
|
|
@@ -14,13 +14,14 @@ test('exports', () => {
|
|
|
14
14
|
|
|
15
15
|
describe('fromRpc', () => {
|
|
16
16
|
test.each([
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[
|
|
17
|
+
['0x0', 'reverted'],
|
|
18
|
+
['0x1', 'success'],
|
|
19
|
+
['0x2', 'skipped'],
|
|
20
20
|
] as const)('status %s', (status, expected) => {
|
|
21
21
|
expect(
|
|
22
22
|
FrameReceipt.fromRpc({
|
|
23
23
|
executionGasUsed: '0x20000000000001',
|
|
24
|
+
gasUsed: '0x20000000000003',
|
|
24
25
|
logs: [
|
|
25
26
|
{
|
|
26
27
|
address: '0x1111111111111111111111111111111111111111',
|
|
@@ -32,7 +33,8 @@ describe('fromRpc', () => {
|
|
|
32
33
|
status,
|
|
33
34
|
}),
|
|
34
35
|
).toEqual({
|
|
35
|
-
|
|
36
|
+
executionGasUsed: 9007199254740993n,
|
|
37
|
+
gasUsed: 9007199254740995n,
|
|
36
38
|
logs: [
|
|
37
39
|
{
|
|
38
40
|
address: '0x1111111111111111111111111111111111111111',
|
|
@@ -48,18 +50,20 @@ describe('fromRpc', () => {
|
|
|
48
50
|
|
|
49
51
|
describe('toRpc', () => {
|
|
50
52
|
test.each([
|
|
51
|
-
['reverted',
|
|
52
|
-
['success',
|
|
53
|
-
['skipped',
|
|
53
|
+
['reverted', '0x0'],
|
|
54
|
+
['success', '0x1'],
|
|
55
|
+
['skipped', '0x2'],
|
|
54
56
|
] as const)('status %s', (status, expected) => {
|
|
55
57
|
const receipt = FrameReceipt.fromRpc({
|
|
56
58
|
executionGasUsed: '0x20000000000001',
|
|
59
|
+
gasUsed: '0x20000000000003',
|
|
57
60
|
logs: [],
|
|
58
61
|
stateGasUsed: '0x2',
|
|
59
62
|
status: expected,
|
|
60
63
|
})
|
|
61
64
|
expect(FrameReceipt.toRpc({ ...receipt, status })).toEqual({
|
|
62
65
|
executionGasUsed: '0x20000000000001',
|
|
66
|
+
gasUsed: '0x20000000000003',
|
|
63
67
|
logs: [],
|
|
64
68
|
stateGasUsed: '0x2',
|
|
65
69
|
status: expected,
|
|
@@ -69,7 +73,8 @@ describe('toRpc', () => {
|
|
|
69
73
|
test('numberish gas', () => {
|
|
70
74
|
expect(
|
|
71
75
|
FrameReceipt.toRpc({
|
|
72
|
-
|
|
76
|
+
executionGasUsed: '0x5208',
|
|
77
|
+
gasUsed: '0x520a',
|
|
73
78
|
logs: [],
|
|
74
79
|
stateGasUsed: 2,
|
|
75
80
|
status: 'success',
|
|
@@ -77,9 +82,10 @@ describe('toRpc', () => {
|
|
|
77
82
|
).toMatchInlineSnapshot(`
|
|
78
83
|
{
|
|
79
84
|
"executionGasUsed": "0x5208",
|
|
85
|
+
"gasUsed": "0x520a",
|
|
80
86
|
"logs": [],
|
|
81
87
|
"stateGasUsed": "0x2",
|
|
82
|
-
"status":
|
|
88
|
+
"status": "0x1",
|
|
83
89
|
}
|
|
84
90
|
`)
|
|
85
91
|
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FrameRequest } from 'ox'
|
|
2
|
+
import { expectTypeOf, test } from 'vp/test'
|
|
3
|
+
|
|
4
|
+
test('public frame request types', () => {
|
|
5
|
+
const rpc = { mode: '0x2' } as const satisfies FrameRequest.Rpc
|
|
6
|
+
const request = FrameRequest.fromRpc(rpc)
|
|
7
|
+
expectTypeOf(request).toEqualTypeOf<FrameRequest.FrameRequest>()
|
|
8
|
+
expectTypeOf(FrameRequest.toRpc(request)).toEqualTypeOf<FrameRequest.Rpc>()
|
|
9
|
+
const input = {
|
|
10
|
+
executionGas: '0x1',
|
|
11
|
+
stateGas: 0,
|
|
12
|
+
} as const satisfies FrameRequest.toRpc.Input
|
|
13
|
+
expectTypeOf(FrameRequest.toRpc(input)).toEqualTypeOf<FrameRequest.Rpc>()
|
|
14
|
+
expectTypeOf<FrameRequest.fromRpc.ErrorType>().not.toBeNever()
|
|
15
|
+
expectTypeOf<FrameRequest.toRpc.ErrorType>().not.toBeNever()
|
|
16
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FrameRequest } from 'ox'
|
|
2
|
+
import { describe, expect, test } from 'vp/test'
|
|
3
|
+
|
|
4
|
+
test('exports', () => {
|
|
5
|
+
expect(Object.keys(FrameRequest).sort()).toMatchInlineSnapshot(`
|
|
6
|
+
[
|
|
7
|
+
"fromRpc",
|
|
8
|
+
"toRpc",
|
|
9
|
+
]
|
|
10
|
+
`)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('fromRpc', () => {
|
|
14
|
+
test('preserves omitted execution gas and explicit zero state gas', () => {
|
|
15
|
+
expect(FrameRequest.fromRpc({ mode: '0x2', stateGas: '0x0' }))
|
|
16
|
+
.toMatchInlineSnapshot(`
|
|
17
|
+
{
|
|
18
|
+
"data": "0x",
|
|
19
|
+
"flags": 0,
|
|
20
|
+
"mode": 2,
|
|
21
|
+
"stateGas": 0n,
|
|
22
|
+
"value": 0n,
|
|
23
|
+
}
|
|
24
|
+
`)
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
describe('toRpc', () => {
|
|
29
|
+
test('preserves omitted gas and encodes numberish values', () => {
|
|
30
|
+
expect(FrameRequest.toRpc({ mode: 'sender', stateGas: 0, value: '0x1' }))
|
|
31
|
+
.toMatchInlineSnapshot(`
|
|
32
|
+
{
|
|
33
|
+
"data": "0x",
|
|
34
|
+
"flags": "0x0",
|
|
35
|
+
"mode": "0x2",
|
|
36
|
+
"stateGas": "0x0",
|
|
37
|
+
"value": "0x1",
|
|
38
|
+
}
|
|
39
|
+
`)
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Frame,
|
|
3
|
+
FrameSignature,
|
|
4
|
+
TransactionRequest,
|
|
5
|
+
TransactionEnvelope,
|
|
6
|
+
TxEnvelopeEip8141,
|
|
7
|
+
} from 'ox'
|
|
8
|
+
import { describe, expect, test } from 'vp/test'
|
|
9
|
+
|
|
10
|
+
describe('TransactionRequest.toRpc', () => {
|
|
11
|
+
test('preserves omitted frame gas and explicit zero limits', () => {
|
|
12
|
+
expect(
|
|
13
|
+
TransactionRequest.toRpc({
|
|
14
|
+
type: 'eip8141',
|
|
15
|
+
frames: [
|
|
16
|
+
{ mode: 'sender' },
|
|
17
|
+
{ mode: 'sender', executionGas: 0n },
|
|
18
|
+
{ mode: 'sender', stateGas: 0n },
|
|
19
|
+
{ mode: 'sender', executionGas: 50_000n, stateGas: 1_000n },
|
|
20
|
+
],
|
|
21
|
+
}),
|
|
22
|
+
).toEqual({
|
|
23
|
+
type: '0x6',
|
|
24
|
+
frames: [
|
|
25
|
+
{ mode: '0x2', flags: '0x0', data: '0x', value: '0x0' },
|
|
26
|
+
{
|
|
27
|
+
mode: '0x2',
|
|
28
|
+
flags: '0x0',
|
|
29
|
+
data: '0x',
|
|
30
|
+
value: '0x0',
|
|
31
|
+
executionGas: '0x0',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
mode: '0x2',
|
|
35
|
+
flags: '0x0',
|
|
36
|
+
data: '0x',
|
|
37
|
+
value: '0x0',
|
|
38
|
+
stateGas: '0x0',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
mode: '0x2',
|
|
42
|
+
flags: '0x0',
|
|
43
|
+
data: '0x',
|
|
44
|
+
value: '0x0',
|
|
45
|
+
executionGas: '0xc350',
|
|
46
|
+
stateGas: '0x3e8',
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('TransactionRequest.fromRpc', () => {
|
|
54
|
+
test('decodes minimal requests without assigning gas limits', () => {
|
|
55
|
+
expect(
|
|
56
|
+
TransactionRequest.fromRpc({
|
|
57
|
+
type: '0x6',
|
|
58
|
+
frames: [
|
|
59
|
+
{ mode: '0x2' },
|
|
60
|
+
{ mode: '0x2', executionGas: '0x0', stateGas: '0x0', target: null },
|
|
61
|
+
],
|
|
62
|
+
}),
|
|
63
|
+
).toEqual({
|
|
64
|
+
type: 'eip8141',
|
|
65
|
+
frames: [
|
|
66
|
+
{ mode: 2, flags: 0, data: '0x', value: 0n },
|
|
67
|
+
{
|
|
68
|
+
mode: 2,
|
|
69
|
+
flags: 0,
|
|
70
|
+
data: '0x',
|
|
71
|
+
value: 0n,
|
|
72
|
+
executionGas: 0n,
|
|
73
|
+
stateGas: 0n,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe('TxEnvelopeEip8141.toRpc', () => {
|
|
81
|
+
test('retains complete frame gas fields', () => {
|
|
82
|
+
const envelope = TxEnvelopeEip8141.toRpc(
|
|
83
|
+
TxEnvelopeEip8141.from({
|
|
84
|
+
chainId: 1,
|
|
85
|
+
frames: [Frame.from({ mode: 'sender' })],
|
|
86
|
+
sender: '0x1111111111111111111111111111111111111111',
|
|
87
|
+
}),
|
|
88
|
+
)
|
|
89
|
+
expect(envelope.frames).toEqual([
|
|
90
|
+
{
|
|
91
|
+
mode: '0x2',
|
|
92
|
+
flags: '0x0',
|
|
93
|
+
data: '0x',
|
|
94
|
+
value: '0x0',
|
|
95
|
+
executionGas: '0x0',
|
|
96
|
+
stateGas: '0x0',
|
|
97
|
+
},
|
|
98
|
+
])
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
describe('FrameSignature.fromRpc', () => {
|
|
103
|
+
test('accepts empty signers', () => {
|
|
104
|
+
expect(FrameSignature.fromRpc({ scheme: '0x1', signer: '0x' })).toEqual({
|
|
105
|
+
scheme: 'secp256k1',
|
|
106
|
+
payload: '0x',
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe('TransactionEnvelope.toTransactionRequest', () => {
|
|
112
|
+
test('retains committed zero defaults when converting envelopes to requests', () => {
|
|
113
|
+
const envelope = TxEnvelopeEip8141.from({
|
|
114
|
+
chainId: 1,
|
|
115
|
+
frames: [Frame.from({ mode: 'sender', executionGas: 50_000n })],
|
|
116
|
+
sender: '0x1111111111111111111111111111111111111111',
|
|
117
|
+
signatures: [FrameSignature.from('0xaabb')],
|
|
118
|
+
})
|
|
119
|
+
const request = TransactionEnvelope.toTransactionRequest(envelope)
|
|
120
|
+
expect(request.frames).toEqual([
|
|
121
|
+
{ mode: 'sender', executionGas: 50_000n, stateGas: 0n },
|
|
122
|
+
])
|
|
123
|
+
expect(
|
|
124
|
+
TxEnvelopeEip8141.getSignPayload(
|
|
125
|
+
TransactionRequest.toEnvelope(
|
|
126
|
+
request,
|
|
127
|
+
) as TxEnvelopeEip8141.TxEnvelopeEip8141,
|
|
128
|
+
),
|
|
129
|
+
).toBe(TxEnvelopeEip8141.getSignPayload(envelope))
|
|
130
|
+
})
|
|
131
|
+
})
|
|
@@ -91,3 +91,23 @@ test('from preserves structured signature types', () => {
|
|
|
91
91
|
signature: { r: '0x01', s: '0x02' },
|
|
92
92
|
})
|
|
93
93
|
})
|
|
94
|
+
|
|
95
|
+
test('preserves protocol hex signature types', () => {
|
|
96
|
+
const signature = '0x1234' as const
|
|
97
|
+
const secp = FrameSignature.from({ scheme: 'secp256k1', signature })
|
|
98
|
+
expectTypeOf(secp.signature).toEqualTypeOf<typeof signature>()
|
|
99
|
+
const p256 = FrameSignature.from({
|
|
100
|
+
publicKey: { prefix: 4, x: '0x01', y: '0x02' },
|
|
101
|
+
scheme: 'p256',
|
|
102
|
+
signature,
|
|
103
|
+
})
|
|
104
|
+
expectTypeOf(p256.signature).toEqualTypeOf<typeof signature>()
|
|
105
|
+
// @ts-expect-error P256 hex signatures also require a public key.
|
|
106
|
+
FrameSignature.from({ scheme: 'p256', signature })
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('preserves empty protocol signature placeholders', () => {
|
|
110
|
+
const entry = FrameSignature.from({ scheme: 'p256', signature: '0x' })
|
|
111
|
+
expectTypeOf(entry.signature).toEqualTypeOf<'0x'>()
|
|
112
|
+
expectTypeOf(entry).toExtend<FrameSignature.P256>()
|
|
113
|
+
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { p256 } from '@noble/curves/nist.js'
|
|
2
2
|
import { secp256k1 } from '@noble/curves/secp256k1.js'
|
|
3
|
-
import { FrameSignature, Hex, P256, Rlp, Secp256k1 } from 'ox'
|
|
3
|
+
import { FrameSignature, Hex, P256, Rlp, Secp256k1, Signature } from 'ox'
|
|
4
4
|
import { describe, expect, test } from 'vite-plus/test'
|
|
5
5
|
|
|
6
6
|
const arbitrary = {
|
|
@@ -21,6 +21,27 @@ const p256Bytes =
|
|
|
21
21
|
'0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000026b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5'
|
|
22
22
|
|
|
23
23
|
describe('from', () => {
|
|
24
|
+
test.each([
|
|
25
|
+
{ scheme: 'secp256k1', signature: '0x' },
|
|
26
|
+
{ scheme: 1, signature: '0x' },
|
|
27
|
+
{ scheme: 'p256', signature: '0x' },
|
|
28
|
+
{ scheme: 2, signature: '0x' },
|
|
29
|
+
] as const)('accepts empty unsigned signatures: $scheme', (input) => {
|
|
30
|
+
const entry = FrameSignature.from(input)
|
|
31
|
+
const { signature: _, ...unsigned } = input
|
|
32
|
+
expect(entry).toEqual({ payload: '0x', ...input })
|
|
33
|
+
expect(FrameSignature.toTuple(entry)).toEqual(
|
|
34
|
+
FrameSignature.toTuple(unsigned),
|
|
35
|
+
)
|
|
36
|
+
expect(FrameSignature.toRpc(entry)).toEqual(FrameSignature.toRpc(unsigned))
|
|
37
|
+
expect(FrameSignature.validate(entry)).toBe(true)
|
|
38
|
+
expect(FrameSignature.validate(entry, { signed: true })).toBe(false)
|
|
39
|
+
expect(() => FrameSignature.assert(entry, { signed: true }))
|
|
40
|
+
.toThrowErrorMatchingInlineSnapshot(`[FrameSignature.InvalidError: Invalid frame signature.
|
|
41
|
+
|
|
42
|
+
Details: Signature is required.]`)
|
|
43
|
+
})
|
|
44
|
+
|
|
24
45
|
test.each(['0x', '0xaabb'] as const)(
|
|
25
46
|
'constructs an arbitrary signature from %s',
|
|
26
47
|
(signature) => {
|
|
@@ -455,15 +476,19 @@ describe('fromRpc', () => {
|
|
|
455
476
|
expect(
|
|
456
477
|
FrameSignature.fromRpc({
|
|
457
478
|
msg: '0x',
|
|
458
|
-
scheme:
|
|
479
|
+
scheme: '0x0',
|
|
459
480
|
signature: '0xaabb',
|
|
460
|
-
signer:
|
|
481
|
+
signer: '0x',
|
|
461
482
|
}),
|
|
462
483
|
).toEqual(FrameSignature.from('0xaabb'))
|
|
463
484
|
})
|
|
464
485
|
test('secp256k1 signature', () => {
|
|
465
486
|
expect(
|
|
466
|
-
FrameSignature.fromRpc({
|
|
487
|
+
FrameSignature.fromRpc({
|
|
488
|
+
msg: '0x',
|
|
489
|
+
scheme: '0x1',
|
|
490
|
+
signature: secpBytes,
|
|
491
|
+
}),
|
|
467
492
|
).toEqual(
|
|
468
493
|
FrameSignature.from({
|
|
469
494
|
scheme: 'secp256k1',
|
|
@@ -478,24 +503,24 @@ describe('fromRpc', () => {
|
|
|
478
503
|
test('P-256 signature', () => {
|
|
479
504
|
const entry = FrameSignature.fromRpc({
|
|
480
505
|
msg: '0x',
|
|
481
|
-
scheme:
|
|
506
|
+
scheme: '0x2',
|
|
482
507
|
signature: p256Bytes,
|
|
483
508
|
})
|
|
484
509
|
expect(entry).toMatchObject({ payload: '0x', publicKey, scheme: 'p256' })
|
|
485
510
|
expect(FrameSignature.toRpc(entry)).toEqual({
|
|
486
511
|
msg: '0x',
|
|
487
|
-
scheme:
|
|
512
|
+
scheme: '0x2',
|
|
488
513
|
signature: p256Bytes,
|
|
489
514
|
})
|
|
490
515
|
})
|
|
491
516
|
test('unsigned placeholder', () => {
|
|
492
|
-
expect(
|
|
493
|
-
FrameSignature.
|
|
494
|
-
)
|
|
517
|
+
expect(FrameSignature.fromRpc({ scheme: '0x1' })).toEqual(
|
|
518
|
+
FrameSignature.from({ scheme: 'secp256k1' }),
|
|
519
|
+
)
|
|
495
520
|
})
|
|
496
521
|
test('malformed protocol signature', () => {
|
|
497
522
|
expect(() =>
|
|
498
|
-
FrameSignature.fromRpc({ msg: '0x', scheme:
|
|
523
|
+
FrameSignature.fromRpc({ msg: '0x', scheme: '0x1', signature: '0x01' }),
|
|
499
524
|
).toThrowErrorMatchingInlineSnapshot(
|
|
500
525
|
`[FrameSignature.InvalidError: Invalid frame signature.
|
|
501
526
|
|
|
@@ -505,6 +530,11 @@ Details: Invalid protocol signature length.]`,
|
|
|
505
530
|
})
|
|
506
531
|
|
|
507
532
|
describe('toRpc', () => {
|
|
533
|
+
test('omits an unsigned protocol signature', () => {
|
|
534
|
+
expect(
|
|
535
|
+
FrameSignature.toRpc(FrameSignature.from({ scheme: 'secp256k1' })),
|
|
536
|
+
).toEqual({ msg: '0x', scheme: '0x1' })
|
|
537
|
+
})
|
|
508
538
|
test('preserves explicit payload and signer', () => {
|
|
509
539
|
const entry = FrameSignature.from({
|
|
510
540
|
payload,
|
|
@@ -514,7 +544,7 @@ describe('toRpc', () => {
|
|
|
514
544
|
})
|
|
515
545
|
expect(FrameSignature.toRpc(entry)).toEqual({
|
|
516
546
|
msg: payload,
|
|
517
|
-
scheme:
|
|
547
|
+
scheme: '0x1',
|
|
518
548
|
signature: secpBytes,
|
|
519
549
|
signer: '0x1111111111111111111111111111111111111111',
|
|
520
550
|
})
|
|
@@ -552,3 +582,94 @@ describe('structured validation', () => {
|
|
|
552
582
|
expect(() => FrameSignature.from(entry)).toThrow()
|
|
553
583
|
})
|
|
554
584
|
})
|
|
585
|
+
|
|
586
|
+
describe('hex signatures', () => {
|
|
587
|
+
test.each(['secp256k1', 1] as const)('secp256k1 scheme %s', (scheme) => {
|
|
588
|
+
const signature = Secp256k1.sign({ payload, privateKey })
|
|
589
|
+
const hex = Signature.toHex(signature)
|
|
590
|
+
const entry = FrameSignature.from({ scheme, signature: hex })
|
|
591
|
+
const structured = FrameSignature.from({ scheme, signature })
|
|
592
|
+
|
|
593
|
+
expect(entry.signature).toBe(hex)
|
|
594
|
+
expect(FrameSignature.toRpc(entry)).toEqual(
|
|
595
|
+
FrameSignature.toRpc(structured),
|
|
596
|
+
)
|
|
597
|
+
expect(FrameSignature.toTuple(entry)).toEqual(
|
|
598
|
+
FrameSignature.toTuple(structured),
|
|
599
|
+
)
|
|
600
|
+
expect(FrameSignature.validate(entry, { signed: true })).toBe(true)
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
test.each(['p256', 2] as const)('P256 scheme %s', (scheme) => {
|
|
604
|
+
const signature = P256.sign({ payload, privateKey })
|
|
605
|
+
const hex = Signature.toHex({ r: signature.r, s: signature.s })
|
|
606
|
+
const entry = FrameSignature.from({ publicKey, scheme, signature: hex })
|
|
607
|
+
const structured = FrameSignature.from({ publicKey, scheme, signature })
|
|
608
|
+
|
|
609
|
+
expect(entry.signature).toBe(hex)
|
|
610
|
+
expect(FrameSignature.toRpc(entry)).toEqual(
|
|
611
|
+
FrameSignature.toRpc(structured),
|
|
612
|
+
)
|
|
613
|
+
expect(FrameSignature.toTuple(entry)).toEqual(
|
|
614
|
+
FrameSignature.toTuple(structured),
|
|
615
|
+
)
|
|
616
|
+
expect(FrameSignature.validate(entry, { signed: true })).toBe(true)
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
test.each(['0x01', `0x${'00'.repeat(65)}`, `0x${'gg'.repeat(65)}`] as const)(
|
|
620
|
+
'rejects invalid secp256k1 hex: %s',
|
|
621
|
+
(signature) => {
|
|
622
|
+
expect(() =>
|
|
623
|
+
FrameSignature.from({ scheme: 'secp256k1', signature }),
|
|
624
|
+
).toThrow()
|
|
625
|
+
},
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
test('requires secp256k1 recovery parity', () => {
|
|
629
|
+
const signature = Signature.toHex({
|
|
630
|
+
r: Hex.fromNumber(1, { size: 32 }),
|
|
631
|
+
s: Hex.fromNumber(2, { size: 32 }),
|
|
632
|
+
})
|
|
633
|
+
expect(() =>
|
|
634
|
+
FrameSignature.from({ scheme: 'secp256k1', signature }),
|
|
635
|
+
).toThrow()
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
test('requires a public key with P256 hex', () => {
|
|
639
|
+
const entry = {
|
|
640
|
+
scheme: 'p256',
|
|
641
|
+
signature: Signature.toHex({
|
|
642
|
+
r: Hex.fromNumber(1, { size: 32 }),
|
|
643
|
+
s: Hex.fromNumber(2, { size: 32 }),
|
|
644
|
+
}),
|
|
645
|
+
} as const
|
|
646
|
+
// @ts-expect-error P256 requires a public key.
|
|
647
|
+
expect(() => FrameSignature.from(entry)).toThrow(
|
|
648
|
+
'P-256 signatures require a public key.',
|
|
649
|
+
)
|
|
650
|
+
})
|
|
651
|
+
|
|
652
|
+
test('normalizes P256 scalars on encoding', () => {
|
|
653
|
+
const signature = {
|
|
654
|
+
r: Hex.fromNumber(1, { size: 32 }),
|
|
655
|
+
s: Hex.fromNumber(p256.Point.Fn.ORDER - 1n),
|
|
656
|
+
} as const
|
|
657
|
+
const entry = FrameSignature.from({
|
|
658
|
+
publicKey,
|
|
659
|
+
scheme: 'p256',
|
|
660
|
+
signature: Signature.toHex(signature),
|
|
661
|
+
})
|
|
662
|
+
expect(FrameSignature.toTuple(entry)).toEqual(
|
|
663
|
+
FrameSignature.toTuple(
|
|
664
|
+
FrameSignature.from({ publicKey, scheme: 'p256', signature }),
|
|
665
|
+
),
|
|
666
|
+
)
|
|
667
|
+
})
|
|
668
|
+
})
|
|
669
|
+
|
|
670
|
+
test('rejects malformed P256 hex bytes', () => {
|
|
671
|
+
const signature = `0x${'00'.repeat(64)}gg` as const
|
|
672
|
+
expect(
|
|
673
|
+
FrameSignature.validate({ publicKey, scheme: 'p256', signature }),
|
|
674
|
+
).toBe(false)
|
|
675
|
+
})
|