ox 1.8.1 → 1.8.4

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.
Files changed (152) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/core/Frame.d.ts +23 -20
  3. package/dist/core/Frame.d.ts.map +1 -1
  4. package/dist/core/Frame.js +28 -23
  5. package/dist/core/Frame.js.map +1 -1
  6. package/dist/core/FrameReceipt.d.ts +23 -14
  7. package/dist/core/FrameReceipt.d.ts.map +1 -1
  8. package/dist/core/FrameReceipt.js +15 -11
  9. package/dist/core/FrameReceipt.js.map +1 -1
  10. package/dist/core/FrameRequest.d.ts +50 -0
  11. package/dist/core/FrameRequest.d.ts.map +1 -0
  12. package/dist/core/FrameRequest.js +59 -0
  13. package/dist/core/FrameRequest.js.map +1 -0
  14. package/dist/core/FrameSignature.d.ts +17 -16
  15. package/dist/core/FrameSignature.d.ts.map +1 -1
  16. package/dist/core/FrameSignature.js +38 -21
  17. package/dist/core/FrameSignature.js.map +1 -1
  18. package/dist/core/Siwe.d.ts.map +1 -1
  19. package/dist/core/Siwe.js +3 -3
  20. package/dist/core/Siwe.js.map +1 -1
  21. package/dist/core/Transaction.d.ts +7 -4
  22. package/dist/core/Transaction.d.ts.map +1 -1
  23. package/dist/core/Transaction.js +8 -0
  24. package/dist/core/Transaction.js.map +1 -1
  25. package/dist/core/TransactionRequest.d.ts +8 -5
  26. package/dist/core/TransactionRequest.d.ts.map +1 -1
  27. package/dist/core/TransactionRequest.js +11 -3
  28. package/dist/core/TransactionRequest.js.map +1 -1
  29. package/dist/core/TxEnvelope.d.ts +4 -2
  30. package/dist/core/TxEnvelope.d.ts.map +1 -1
  31. package/dist/core/TxEnvelope.js +11 -2
  32. package/dist/core/TxEnvelope.js.map +1 -1
  33. package/dist/core/TxEnvelopeEip8141.d.ts +22 -11
  34. package/dist/core/TxEnvelopeEip8141.d.ts.map +1 -1
  35. package/dist/core/TxEnvelopeEip8141.js +45 -16
  36. package/dist/core/TxEnvelopeEip8141.js.map +1 -1
  37. package/dist/core/internal/frameNonce.d.ts +10 -0
  38. package/dist/core/internal/frameNonce.d.ts.map +1 -0
  39. package/dist/core/internal/frameNonce.js +45 -0
  40. package/dist/core/internal/frameNonce.js.map +1 -0
  41. package/dist/core/internal/rpcSchemas/eth.d.ts +16 -4
  42. package/dist/core/internal/rpcSchemas/eth.d.ts.map +1 -1
  43. package/dist/index.d.ts +21 -5
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +21 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/zod/Block.d.ts +52 -37
  48. package/dist/zod/Block.d.ts.map +1 -1
  49. package/dist/zod/Frame.d.ts +14 -14
  50. package/dist/zod/Frame.js +5 -5
  51. package/dist/zod/Frame.js.map +1 -1
  52. package/dist/zod/FrameReceipt.d.ts +42 -2
  53. package/dist/zod/FrameReceipt.d.ts.map +1 -1
  54. package/dist/zod/FrameReceipt.js +11 -1
  55. package/dist/zod/FrameReceipt.js.map +1 -1
  56. package/dist/zod/FrameRequest.d.ts +40 -0
  57. package/dist/zod/FrameRequest.d.ts.map +1 -0
  58. package/dist/zod/FrameRequest.js +37 -0
  59. package/dist/zod/FrameRequest.js.map +1 -0
  60. package/dist/zod/FrameSignature.d.ts +21 -13
  61. package/dist/zod/FrameSignature.d.ts.map +1 -1
  62. package/dist/zod/FrameSignature.js +10 -7
  63. package/dist/zod/FrameSignature.js.map +1 -1
  64. package/dist/zod/RpcSchema.d.ts +470 -265
  65. package/dist/zod/RpcSchema.d.ts.map +1 -1
  66. package/dist/zod/Transaction.d.ts +104 -74
  67. package/dist/zod/Transaction.d.ts.map +1 -1
  68. package/dist/zod/Transaction.js +22 -15
  69. package/dist/zod/Transaction.js.map +1 -1
  70. package/dist/zod/TransactionEnvelope.d.ts +60 -34
  71. package/dist/zod/TransactionEnvelope.d.ts.map +1 -1
  72. package/dist/zod/TransactionReceipt.d.ts +42 -2
  73. package/dist/zod/TransactionReceipt.d.ts.map +1 -1
  74. package/dist/zod/TransactionRequest.d.ts +31 -21
  75. package/dist/zod/TransactionRequest.d.ts.map +1 -1
  76. package/dist/zod/TransactionRequest.js +10 -4
  77. package/dist/zod/TransactionRequest.js.map +1 -1
  78. package/dist/zod/TxEnvelopeEip8141.d.ts +44 -25
  79. package/dist/zod/TxEnvelopeEip8141.d.ts.map +1 -1
  80. package/dist/zod/TxEnvelopeEip8141.js +2 -0
  81. package/dist/zod/TxEnvelopeEip8141.js.map +1 -1
  82. package/dist/zod/internal/Frame.d.ts +8 -0
  83. package/dist/zod/internal/Frame.d.ts.map +1 -1
  84. package/dist/zod/internal/Frame.js +29 -0
  85. package/dist/zod/internal/Frame.js.map +1 -1
  86. package/dist/zod/internal/rpcSchemas/Eth.d.ts +472 -267
  87. package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
  88. package/dist/zod/internal/rpcSchemas/Eth.js +9 -0
  89. package/dist/zod/internal/rpcSchemas/Eth.js.map +1 -1
  90. package/dist/zod/internal/rpcSchemas/Wallet.d.ts +51 -21
  91. package/dist/zod/internal/rpcSchemas/Wallet.d.ts.map +1 -1
  92. package/dist/zod/tempo/RpcSchemaTempo.d.ts +68 -48
  93. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  94. package/dist/zod/tempo/Transaction.d.ts +52 -37
  95. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  96. package/dist/zod/z.d.ts +1 -0
  97. package/dist/zod/z.d.ts.map +1 -1
  98. package/dist/zod/z.js +1 -0
  99. package/dist/zod/z.js.map +1 -1
  100. package/package.json +11 -1
  101. package/src/core/Frame.ts +41 -33
  102. package/src/core/FrameReceipt.ts +30 -16
  103. package/src/core/FrameRequest.ts +78 -0
  104. package/src/core/FrameSignature.ts +64 -34
  105. package/src/core/Siwe.ts +21 -13
  106. package/src/core/Transaction.ts +52 -24
  107. package/src/core/TransactionRequest.ts +21 -7
  108. package/src/core/TxEnvelope.ts +16 -3
  109. package/src/core/TxEnvelopeEip8141.ts +69 -21
  110. package/src/core/_test/AbiConstructor.test.ts +2 -0
  111. package/src/core/_test/Frame.test-d.ts +16 -7
  112. package/src/core/_test/Frame.test.ts +52 -24
  113. package/src/core/_test/FrameReceipt.test.ts +15 -9
  114. package/src/core/_test/FrameRequest.test-d.ts +16 -0
  115. package/src/core/_test/FrameRequest.test.ts +41 -0
  116. package/src/core/_test/FrameRpc.test.ts +131 -0
  117. package/src/core/_test/FrameSignature.test-d.ts +20 -0
  118. package/src/core/_test/FrameSignature.test.ts +132 -11
  119. package/src/core/_test/RpcSchema.test-d.ts +45 -0
  120. package/src/core/_test/Siwe.test.ts +147 -0
  121. package/src/core/_test/TransactionEnvelope.test-d.ts +27 -0
  122. package/src/core/_test/TransactionEnvelope.test.ts +27 -1
  123. package/src/core/_test/TransactionReceipt.test.ts +32 -6
  124. package/src/core/_test/TransactionRequest.test-d.ts +10 -1
  125. package/src/core/_test/TransactionRequest.test.ts +31 -2
  126. package/src/core/_test/TxEnvelopeEip8141.test-d.ts +29 -2
  127. package/src/core/_test/TxEnvelopeEip8141.test.ts +548 -55
  128. package/src/core/_test/index.test.ts +1 -0
  129. package/src/core/internal/frameNonce.ts +61 -0
  130. package/src/core/internal/rpcSchemas/eth.ts +13 -4
  131. package/src/index.ts +22 -5
  132. package/src/version.ts +1 -1
  133. package/src/zod/Frame.ts +5 -5
  134. package/src/zod/FrameReceipt.ts +11 -1
  135. package/src/zod/FrameRequest.ts +46 -0
  136. package/src/zod/FrameSignature.ts +10 -7
  137. package/src/zod/Transaction.ts +46 -27
  138. package/src/zod/TransactionRequest.ts +33 -18
  139. package/src/zod/TxEnvelopeEip8141.ts +2 -0
  140. package/src/zod/_test/Frame.test.ts +13 -11
  141. package/src/zod/_test/FrameReceipt.test.ts +11 -4
  142. package/src/zod/_test/FrameRequest.test-d.ts +15 -0
  143. package/src/zod/_test/FrameRequest.test.ts +44 -0
  144. package/src/zod/_test/FrameSignature.test.ts +73 -9
  145. package/src/zod/_test/RpcSchema.test.ts +106 -0
  146. package/src/zod/_test/TransactionRequest.test.ts +61 -0
  147. package/src/zod/_test/TxEnvelopeEip8141.nonce.test.ts +174 -0
  148. package/src/zod/_test/TxEnvelopeEip8141.test-d.ts +29 -0
  149. package/src/zod/_test/TxEnvelopeEip8141.test.ts +3 -3
  150. package/src/zod/internal/Frame.ts +45 -0
  151. package/src/zod/internal/rpcSchemas/Eth.ts +20 -0
  152. package/src/zod/z.ts +1 -0
@@ -7,13 +7,15 @@ describe('FrameReceipt', () => {
7
7
  expect(
8
8
  z.safeDecode(z.FrameReceipt.FrameReceipt, {
9
9
  executionGasUsed: '0x',
10
+ gasUsed: '0x',
10
11
  logs: [],
11
12
  stateGasUsed: '0x0',
12
- status: 1,
13
+ status: '0x1',
13
14
  }).success,
14
15
  ).toBe(false)
15
16
  expect(
16
17
  z.safeEncode(z.FrameReceipt.FrameReceipt, {
18
+ executionGasUsed: 0n,
17
19
  gasUsed: -1n,
18
20
  logs: [],
19
21
  stateGasUsed: 0n,
@@ -23,6 +25,7 @@ describe('FrameReceipt', () => {
23
25
  for (const gasUsed of [-1, Number.MAX_SAFE_INTEGER + 1, '0x'] as const)
24
26
  expect(
25
27
  z.safeEncode(z.FrameReceipt.FrameReceiptToRpc, {
28
+ executionGasUsed: 0n,
26
29
  gasUsed,
27
30
  logs: [],
28
31
  stateGasUsed: 0n,
@@ -34,9 +37,10 @@ describe('FrameReceipt', () => {
34
37
  test('roundtrip', () => {
35
38
  const rpc = {
36
39
  executionGasUsed: '0x20000000000001',
40
+ gasUsed: '0x20000000000001',
37
41
  logs: [],
38
42
  stateGasUsed: '0x0',
39
- status: 2,
43
+ status: '0x2',
40
44
  } as const
41
45
  const receipt = FrameReceipt.fromRpc(rpc)
42
46
  expect(z.decode(z.FrameReceipt.FrameReceipt, rpc)).toEqual(receipt)
@@ -47,10 +51,11 @@ describe('FrameReceipt', () => {
47
51
  expect(
48
52
  z.safeDecode(z.FrameReceipt.FrameReceipt, {
49
53
  executionGasUsed: '0x0',
54
+ gasUsed: '0x0',
50
55
  logs: [],
51
56
  stateGasUsed: '0x0',
52
57
  // @ts-expect-error Invalid RPC status.
53
- status: 3,
58
+ status: '0x3',
54
59
  }).success,
55
60
  ).toBe(false)
56
61
  })
@@ -60,6 +65,7 @@ describe('FrameReceiptToRpc', () => {
60
65
  test('numberish gas', () => {
61
66
  expect(
62
67
  z.encode(z.FrameReceipt.FrameReceiptToRpc, {
68
+ executionGasUsed: 21000,
63
69
  gasUsed: 21000,
64
70
  logs: [],
65
71
  stateGasUsed: '0x0',
@@ -67,9 +73,10 @@ describe('FrameReceiptToRpc', () => {
67
73
  }),
68
74
  ).toEqual({
69
75
  executionGasUsed: '0x5208',
76
+ gasUsed: '0x5208',
70
77
  logs: [],
71
78
  stateGasUsed: '0x0',
72
- status: 1,
79
+ status: '0x1',
73
80
  })
74
81
  })
75
82
  })
@@ -0,0 +1,15 @@
1
+ import type { FrameRequest } from 'ox'
2
+ import { z } from 'ox/zod'
3
+ import { expectTypeOf, test } from 'vp/test'
4
+
5
+ test('public frame request codecs preserve input and output types', () => {
6
+ expectTypeOf<
7
+ z.input<typeof z.FrameRequest.FrameRequest>
8
+ >().toEqualTypeOf<FrameRequest.Rpc>()
9
+ expectTypeOf<
10
+ z.output<typeof z.FrameRequest.FrameRequest>
11
+ >().toEqualTypeOf<FrameRequest.FrameRequest>()
12
+ expectTypeOf<
13
+ z.output<typeof z.FrameRequest.FrameRequestToRpc>
14
+ >().toEqualTypeOf<FrameRequest.toRpc.Input>()
15
+ })
@@ -0,0 +1,44 @@
1
+ import { z } from 'ox/zod'
2
+ import { describe, expect, test } from 'vp/test'
3
+
4
+ describe('FrameRequest', () => {
5
+ test('round-trips omitted gas through the public codec', () => {
6
+ const frame = z.decode(z.FrameRequest.FrameRequest, {
7
+ mode: '0x2',
8
+ stateGas: '0x0',
9
+ })
10
+ expect(frame).toEqual({
11
+ mode: 2,
12
+ flags: 0,
13
+ stateGas: 0n,
14
+ value: 0n,
15
+ data: '0x',
16
+ })
17
+ expect(z.encode(z.FrameRequest.FrameRequest, frame)).toEqual({
18
+ mode: '0x2',
19
+ flags: '0x0',
20
+ stateGas: '0x0',
21
+ value: '0x0',
22
+ data: '0x',
23
+ })
24
+ })
25
+ })
26
+
27
+ describe('FrameRequestToRpc', () => {
28
+ test('encodes numberish values through the public codec', () => {
29
+ expect(
30
+ z.encode(z.FrameRequest.FrameRequestToRpc, {
31
+ mode: 'sender',
32
+ executionGas: 50000,
33
+ stateGas: '0x0',
34
+ }),
35
+ ).toEqual({
36
+ mode: '0x2',
37
+ flags: '0x0',
38
+ executionGas: '0xc350',
39
+ stateGas: '0x0',
40
+ value: '0x0',
41
+ data: '0x',
42
+ })
43
+ })
44
+ })
@@ -1,4 +1,4 @@
1
- import { FrameSignature, P256, Secp256k1, Hash } from 'ox'
1
+ import { FrameSignature, P256, Secp256k1, Hash, Signature } from 'ox'
2
2
  import { z } from 'ox/zod'
3
3
  import { describe, expect, test } from 'vp/test'
4
4
  import { accounts } from '../../../test/constants/accounts.js'
@@ -8,7 +8,7 @@ describe('FrameSignature', () => {
8
8
  expect(
9
9
  z.safeDecode(z.FrameSignature.FrameSignature, {
10
10
  msg: '0x',
11
- scheme: 1,
11
+ scheme: '0x1',
12
12
  signature: '0x01',
13
13
  }).success,
14
14
  ).toBe(false)
@@ -20,27 +20,27 @@ describe('FrameSignature', () => {
20
20
  scheme: 'arbitrary',
21
21
  signature: '0xaabb',
22
22
  }),
23
- ).toEqual({ msg: '0x', scheme: 0, signature: '0xaabb' })
23
+ ).toEqual({ msg: '0x', scheme: '0x0', signature: '0xaabb' })
24
24
  expect(
25
25
  z.encode(z.FrameSignature.FrameSignature, {
26
26
  scheme: 'secp256k1',
27
27
  }),
28
- ).toEqual({ msg: '0x', scheme: 1, signature: '0x' })
28
+ ).toEqual({ msg: '0x', scheme: '0x1' })
29
29
  expect(
30
30
  z.encode(z.FrameSignature.FrameSignature, {
31
31
  scheme: 'p256',
32
32
  }),
33
- ).toEqual({ msg: '0x', scheme: 2, signature: '0x' })
33
+ ).toEqual({ msg: '0x', scheme: '0x2' })
34
34
  })
35
35
 
36
36
  test('arbitrary bytes', () => {
37
37
  expect(
38
38
  z.encode(z.FrameSignature.FrameSignature, FrameSignature.from('0xaabb')),
39
- ).toEqual({ msg: '0x', scheme: 0, signature: '0xaabb' })
39
+ ).toEqual({ msg: '0x', scheme: '0x0', signature: '0xaabb' })
40
40
  expect(
41
41
  z.decode(z.FrameSignature.FrameSignature, {
42
42
  msg: '0x',
43
- scheme: 0,
43
+ scheme: '0x0',
44
44
  signature: '0xaabb',
45
45
  }),
46
46
  ).toEqual(FrameSignature.from('0xaabb'))
@@ -54,7 +54,7 @@ describe('FrameSignature', () => {
54
54
  }),
55
55
  })
56
56
  const rpc = z.encode(z.FrameSignature.FrameSignature, entry)
57
- expect(rpc.scheme).toBe(1)
57
+ expect(rpc.scheme).toBe('0x1')
58
58
  expect(z.decode(z.FrameSignature.FrameSignature, rpc)).toEqual(entry)
59
59
  })
60
60
  test('P-256 signature', () => {
@@ -68,7 +68,7 @@ describe('FrameSignature', () => {
68
68
  }),
69
69
  })
70
70
  const rpc = z.encode(z.FrameSignature.FrameSignature, entry)
71
- expect(rpc.scheme).toBe(2)
71
+ expect(rpc.scheme).toBe('0x2')
72
72
  expect(z.decode(z.FrameSignature.FrameSignature, rpc)).toEqual({
73
73
  ...entry,
74
74
  signature: { r: entry.signature.r, s: entry.signature.s },
@@ -92,3 +92,67 @@ describe('FrameSignature', () => {
92
92
  ).toBe(false)
93
93
  })
94
94
  })
95
+
96
+ test('encodes protocol hex signatures', () => {
97
+ const payload = Hash.keccak256('0xdeadbeef')
98
+ const privateKey = accounts[0].privateKey
99
+ const secp = FrameSignature.from({
100
+ scheme: 'secp256k1',
101
+ signature: Secp256k1.sign({ payload, privateKey }),
102
+ })
103
+ const p256 = FrameSignature.from({
104
+ publicKey: P256.getPublicKey({ privateKey }),
105
+ scheme: 'p256',
106
+ signature: P256.sign({ payload, privateKey }),
107
+ })
108
+
109
+ expect(
110
+ z.encode(z.FrameSignature.FrameSignature, {
111
+ ...secp,
112
+ signature: Signature.toHex(secp.signature),
113
+ }),
114
+ ).toEqual(FrameSignature.toRpc(secp))
115
+ expect(
116
+ z.encode(z.FrameSignature.FrameSignature, {
117
+ ...p256,
118
+ signature: Signature.toHex(p256.signature),
119
+ }),
120
+ ).toEqual(FrameSignature.toRpc(p256))
121
+ })
122
+
123
+ describe('RPC signers', () => {
124
+ test.each(['0x0', '0x1', '0x2'] as const)(
125
+ 'accepts an empty signer for scheme %s',
126
+ (scheme) => {
127
+ expect(
128
+ z.safeDecode(z.FrameSignature.FrameSignature, {
129
+ scheme,
130
+ signer: '0x',
131
+ msg: '0x',
132
+ ...(scheme === '0x0' ? { signature: '0x' } : {}),
133
+ }).success,
134
+ ).toBe(true)
135
+ },
136
+ )
137
+ test('rejects a null signer', () => {
138
+ expect(
139
+ z.safeParse(z.FrameSignature.FrameSignature, {
140
+ scheme: '0x1',
141
+ signer: null,
142
+ }).success,
143
+ ).toBe(false)
144
+ })
145
+ })
146
+
147
+ describe('unsigned placeholders', () => {
148
+ test.each([
149
+ { scheme: 'secp256k1', signature: '0x' },
150
+ { scheme: 'p256', signature: '0x' },
151
+ ] as const)('$scheme', (entry) => {
152
+ const { signature: _, ...unsigned } = entry
153
+ expect(z.safeParse(z.FrameSignature.Decoded, entry).success).toBe(true)
154
+ expect(z.encode(z.FrameSignature.FrameSignature, entry)).toEqual(
155
+ FrameSignature.toRpc(unsigned),
156
+ )
157
+ })
158
+ })
@@ -423,3 +423,109 @@ describe('from', () => {
423
423
  expect(schema.abe_foo.method).toMatchInlineSnapshot(`"abe_foo"`)
424
424
  })
425
425
  })
426
+
427
+ describe('frame simulation results', () => {
428
+ test('preserves payer, per-frame output, errors, and gas through the codec', () => {
429
+ const block = {
430
+ difficulty: '0x0',
431
+ extraData: '0x',
432
+ gasLimit: '0x100000',
433
+ gasUsed: '0x5208',
434
+ hash,
435
+ logsBloom: '0x',
436
+ miner: address,
437
+ mixHash: hash,
438
+ nonce: '0x0000000000000000',
439
+ number: '0x1',
440
+ parentHash: hash,
441
+ receiptsRoot: hash,
442
+ sha3Uncles: hash,
443
+ size: '0x1',
444
+ stateRoot: hash,
445
+ timestamp: '0x1',
446
+ transactions: [],
447
+ transactionsRoot: hash,
448
+ uncles: [],
449
+ calls: [
450
+ {
451
+ payer: address,
452
+ gasUsed: '0x5208',
453
+ returnData: '0xdead',
454
+ status: '0x0',
455
+ logs: [],
456
+ frameResults: [
457
+ {
458
+ status: '0x1',
459
+ gasUsed: '0x2',
460
+ executionGasUsed: '0x1',
461
+ stateGasUsed: '0x1',
462
+ logs: [],
463
+ returnData: '0xabcd',
464
+ },
465
+ {
466
+ status: '0x0',
467
+ gasUsed: '0x1',
468
+ executionGasUsed: '0x1',
469
+ stateGasUsed: '0x0',
470
+ logs: [],
471
+ returnData: '0xdead',
472
+ error: { code: 3, message: 'execution reverted' },
473
+ },
474
+ {
475
+ status: '0x2',
476
+ gasUsed: '0x0',
477
+ executionGasUsed: '0x0',
478
+ stateGasUsed: '0x0',
479
+ logs: [],
480
+ returnData: '0x',
481
+ },
482
+ ],
483
+ },
484
+ ],
485
+ } satisfies z.input<typeof z_RpcSchema.Eth.eth_simulateV1.returns>[number]
486
+ const decoded = z_RpcSchema.decodeReturns(
487
+ z_RpcSchema.Eth,
488
+ 'eth_simulateV1',
489
+ [block],
490
+ )
491
+ expect(decoded[0]?.calls).toEqual([
492
+ {
493
+ payer: address,
494
+ gasUsed: '0x5208',
495
+ returnData: '0xdead',
496
+ status: '0x0',
497
+ logs: [],
498
+ frameResults: [
499
+ {
500
+ status: 'success',
501
+ gasUsed: 2n,
502
+ executionGasUsed: 1n,
503
+ stateGasUsed: 1n,
504
+ logs: [],
505
+ returnData: '0xabcd',
506
+ },
507
+ {
508
+ status: 'reverted',
509
+ gasUsed: 1n,
510
+ executionGasUsed: 1n,
511
+ stateGasUsed: 0n,
512
+ logs: [],
513
+ returnData: '0xdead',
514
+ error: { code: 3, message: 'execution reverted' },
515
+ },
516
+ {
517
+ status: 'skipped',
518
+ gasUsed: 0n,
519
+ executionGasUsed: 0n,
520
+ stateGasUsed: 0n,
521
+ logs: [],
522
+ returnData: '0x',
523
+ },
524
+ ],
525
+ },
526
+ ])
527
+ expect(
528
+ z_RpcSchema.encodeReturns(z_RpcSchema.Eth, 'eth_simulateV1', decoded),
529
+ ).toEqual([block])
530
+ })
531
+ })
@@ -220,3 +220,64 @@ describe('TransactionRequest', () => {
220
220
  ).toBe(false)
221
221
  })
222
222
  })
223
+
224
+ describe('frame requests', () => {
225
+ test.each([
226
+ z_TransactionRequest.TransactionRequest,
227
+ z_TransactionRequest.TransactionRequestToRpc,
228
+ ])('preserves gas omission and zero through the request codec', (schema) => {
229
+ const decoded = z.decode(schema, {
230
+ type: '0x6',
231
+ frames: [{ mode: '0x2' }, { mode: '0x2', stateGas: '0x0' }],
232
+ })
233
+ expect(decoded).toEqual({
234
+ type: 'eip8141',
235
+ frames: [
236
+ { mode: 2, flags: 0, data: '0x', value: 0n },
237
+ { mode: 2, flags: 0, data: '0x', value: 0n, stateGas: 0n },
238
+ ],
239
+ })
240
+ expect(z.encode(schema, decoded)).toEqual({
241
+ type: '0x6',
242
+ frames: [
243
+ { mode: '0x2', flags: '0x0', data: '0x', value: '0x0' },
244
+ {
245
+ mode: '0x2',
246
+ flags: '0x0',
247
+ data: '0x',
248
+ value: '0x0',
249
+ stateGas: '0x0',
250
+ },
251
+ ],
252
+ })
253
+ })
254
+ test('encodes numberish frame limits', () => {
255
+ expect(
256
+ z.encode(z_TransactionRequest.TransactionRequestToRpc, {
257
+ frames: [{ mode: 'sender', executionGas: 50000, stateGas: '0x0' }],
258
+ }),
259
+ ).toEqual({
260
+ frames: [
261
+ {
262
+ mode: '0x2',
263
+ flags: '0x0',
264
+ data: '0x',
265
+ value: '0x0',
266
+ executionGas: '0xc350',
267
+ stateGas: '0x0',
268
+ },
269
+ ],
270
+ })
271
+ })
272
+ test.each([
273
+ { mode: '0x3' },
274
+ { mode: '0x2', executionGas: '0x00' },
275
+ { mode: '0x2', stateGas: null },
276
+ {},
277
+ ])('rejects malformed frame request %j', (frame) => {
278
+ expect(
279
+ z.safeParse(z_TransactionRequest.TransactionRequest, { frames: [frame] })
280
+ .success,
281
+ ).toBe(false)
282
+ })
283
+ })
@@ -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
+ })
@@ -75,10 +75,10 @@ describe('TxEnvelopeEip8141', () => {
75
75
  expect(
76
76
  z.encode(z.TxEnvelopeEip8141.TxEnvelopeEip8141ToRpc, {
77
77
  chainId: '0x1',
78
- frames: [{ gas: 50_000 }],
78
+ frames: [{ executionGas: 50_000 }],
79
79
  sender: accounts[0].address,
80
80
  type: 'eip8141',
81
- }).frames[0]!.executionGasLimit,
81
+ }).frames[0]!.executionGas,
82
82
  ).toBe('0xc350')
83
83
  })
84
84
  })
@@ -107,7 +107,7 @@ describe('invalid envelopes', () => {
107
107
  test('rejects malformed nested RPC signatures without throwing', () => {
108
108
  const rpc = {
109
109
  ...TxEnvelopeEip8141.toRpc(envelope),
110
- signatures: [{ msg: '0x', scheme: 1, signature: '0x01' }],
110
+ signatures: [{ msg: '0x', scheme: '0x1', signature: '0x01' }],
111
111
  } as const
112
112
  expect(
113
113
  z.safeDecode(z.TxEnvelopeEip8141.TxEnvelopeEip8141, rpc).success,