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.
Files changed (134) hide show
  1. package/CHANGELOG.md +35 -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 +2 -2
  22. package/dist/core/Transaction.d.ts.map +1 -1
  23. package/dist/core/Transaction.js.map +1 -1
  24. package/dist/core/TransactionRequest.d.ts +5 -5
  25. package/dist/core/TransactionRequest.d.ts.map +1 -1
  26. package/dist/core/TransactionRequest.js +3 -3
  27. package/dist/core/TransactionRequest.js.map +1 -1
  28. package/dist/core/TxEnvelope.d.ts +3 -1
  29. package/dist/core/TxEnvelope.d.ts.map +1 -1
  30. package/dist/core/TxEnvelope.js +9 -1
  31. package/dist/core/TxEnvelope.js.map +1 -1
  32. package/dist/core/TxEnvelopeEip8141.d.ts +4 -4
  33. package/dist/core/TxEnvelopeEip8141.js +8 -8
  34. package/dist/core/TxEnvelopeEip8141.js.map +1 -1
  35. package/dist/core/internal/rpcSchemas/eth.d.ts +16 -4
  36. package/dist/core/internal/rpcSchemas/eth.d.ts.map +1 -1
  37. package/dist/index.d.ts +21 -5
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +21 -5
  40. package/dist/index.js.map +1 -1
  41. package/dist/zod/Block.d.ts +49 -37
  42. package/dist/zod/Block.d.ts.map +1 -1
  43. package/dist/zod/Frame.d.ts +14 -14
  44. package/dist/zod/Frame.js +5 -5
  45. package/dist/zod/Frame.js.map +1 -1
  46. package/dist/zod/FrameReceipt.d.ts +42 -2
  47. package/dist/zod/FrameReceipt.d.ts.map +1 -1
  48. package/dist/zod/FrameReceipt.js +11 -1
  49. package/dist/zod/FrameReceipt.js.map +1 -1
  50. package/dist/zod/FrameRequest.d.ts +40 -0
  51. package/dist/zod/FrameRequest.d.ts.map +1 -0
  52. package/dist/zod/FrameRequest.js +37 -0
  53. package/dist/zod/FrameRequest.js.map +1 -0
  54. package/dist/zod/FrameSignature.d.ts +21 -13
  55. package/dist/zod/FrameSignature.d.ts.map +1 -1
  56. package/dist/zod/FrameSignature.js +10 -7
  57. package/dist/zod/FrameSignature.js.map +1 -1
  58. package/dist/zod/RpcSchema.d.ts +447 -265
  59. package/dist/zod/RpcSchema.d.ts.map +1 -1
  60. package/dist/zod/Transaction.d.ts +98 -74
  61. package/dist/zod/Transaction.d.ts.map +1 -1
  62. package/dist/zod/Transaction.js +4 -4
  63. package/dist/zod/Transaction.js.map +1 -1
  64. package/dist/zod/TransactionEnvelope.d.ts +50 -34
  65. package/dist/zod/TransactionEnvelope.d.ts.map +1 -1
  66. package/dist/zod/TransactionReceipt.d.ts +42 -2
  67. package/dist/zod/TransactionReceipt.d.ts.map +1 -1
  68. package/dist/zod/TransactionRequest.d.ts +29 -21
  69. package/dist/zod/TransactionRequest.d.ts.map +1 -1
  70. package/dist/zod/TransactionRequest.js +3 -3
  71. package/dist/zod/TransactionRequest.js.map +1 -1
  72. package/dist/zod/TxEnvelopeEip8141.d.ts +37 -25
  73. package/dist/zod/TxEnvelopeEip8141.d.ts.map +1 -1
  74. package/dist/zod/internal/rpcSchemas/Eth.d.ts +449 -267
  75. package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
  76. package/dist/zod/internal/rpcSchemas/Eth.js +9 -0
  77. package/dist/zod/internal/rpcSchemas/Eth.js.map +1 -1
  78. package/dist/zod/internal/rpcSchemas/Wallet.d.ts +49 -21
  79. package/dist/zod/internal/rpcSchemas/Wallet.d.ts.map +1 -1
  80. package/dist/zod/tempo/RpcSchemaTempo.d.ts +64 -48
  81. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  82. package/dist/zod/tempo/Transaction.d.ts +49 -37
  83. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  84. package/dist/zod/z.d.ts +1 -0
  85. package/dist/zod/z.d.ts.map +1 -1
  86. package/dist/zod/z.js +1 -0
  87. package/dist/zod/z.js.map +1 -1
  88. package/package.json +11 -1
  89. package/src/core/Frame.ts +41 -33
  90. package/src/core/FrameReceipt.ts +30 -16
  91. package/src/core/FrameRequest.ts +78 -0
  92. package/src/core/FrameSignature.ts +64 -34
  93. package/src/core/Siwe.ts +21 -13
  94. package/src/core/Transaction.ts +35 -24
  95. package/src/core/TransactionRequest.ts +9 -7
  96. package/src/core/TxEnvelope.ts +9 -1
  97. package/src/core/TxEnvelopeEip8141.ts +8 -8
  98. package/src/core/_test/AbiConstructor.test.ts +2 -0
  99. package/src/core/_test/Frame.test-d.ts +16 -7
  100. package/src/core/_test/Frame.test.ts +52 -24
  101. package/src/core/_test/FrameReceipt.test.ts +15 -9
  102. package/src/core/_test/FrameRequest.test-d.ts +16 -0
  103. package/src/core/_test/FrameRequest.test.ts +41 -0
  104. package/src/core/_test/FrameRpc.test.ts +131 -0
  105. package/src/core/_test/FrameSignature.test-d.ts +20 -0
  106. package/src/core/_test/FrameSignature.test.ts +132 -11
  107. package/src/core/_test/RpcSchema.test-d.ts +45 -0
  108. package/src/core/_test/Siwe.test.ts +147 -0
  109. package/src/core/_test/TransactionEnvelope.test.ts +1 -1
  110. package/src/core/_test/TransactionReceipt.test.ts +32 -6
  111. package/src/core/_test/TransactionRequest.test-d.ts +10 -1
  112. package/src/core/_test/TransactionRequest.test.ts +3 -2
  113. package/src/core/_test/TxEnvelopeEip8141.test-d.ts +2 -2
  114. package/src/core/_test/TxEnvelopeEip8141.test.ts +92 -53
  115. package/src/core/_test/index.test.ts +1 -0
  116. package/src/core/internal/rpcSchemas/eth.ts +13 -4
  117. package/src/index.ts +22 -5
  118. package/src/version.ts +1 -1
  119. package/src/zod/Frame.ts +5 -5
  120. package/src/zod/FrameReceipt.ts +11 -1
  121. package/src/zod/FrameRequest.ts +46 -0
  122. package/src/zod/FrameSignature.ts +10 -7
  123. package/src/zod/Transaction.ts +4 -4
  124. package/src/zod/TransactionRequest.ts +3 -3
  125. package/src/zod/_test/Frame.test.ts +13 -11
  126. package/src/zod/_test/FrameReceipt.test.ts +11 -4
  127. package/src/zod/_test/FrameRequest.test-d.ts +15 -0
  128. package/src/zod/_test/FrameRequest.test.ts +44 -0
  129. package/src/zod/_test/FrameSignature.test.ts +73 -9
  130. package/src/zod/_test/RpcSchema.test.ts +106 -0
  131. package/src/zod/_test/TransactionRequest.test.ts +61 -0
  132. package/src/zod/_test/TxEnvelopeEip8141.test.ts +3 -3
  133. package/src/zod/internal/rpcSchemas/Eth.ts +20 -0
  134. package/src/zod/z.ts +1 -0
@@ -2,7 +2,7 @@
2
2
  import * as z_AccessList from './AccessList.js'
3
3
  import * as z_Address from './Address.js'
4
4
  import * as z_Authorization from './Authorization.js'
5
- import * as z_Frame from './Frame.js'
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
8
  import * as z_Number from './Number.js'
@@ -38,7 +38,7 @@ export const TransactionRequest = z.object(
38
38
  z_Uint.Uint,
39
39
  z_Number.Number,
40
40
  z_Authorization.ListSigned,
41
- z_Frame.Frame,
41
+ z_FrameRequest.FrameRequest,
42
42
  ),
43
43
  )
44
44
 
@@ -48,7 +48,7 @@ export const TransactionRequestToRpc = z.object(
48
48
  z_Uint.UintToRpc,
49
49
  z_Number.NumberToRpc,
50
50
  z_Authorization.ListSignedToRpc,
51
- z_Frame.FrameToRpc,
51
+ z_FrameRequest.FrameRequestToRpc,
52
52
  ),
53
53
  )
54
54
 
@@ -5,25 +5,25 @@ import { describe, expect, test } from 'vp/test'
5
5
  describe('Frame', () => {
6
6
  test('decodes RPC budgets and encodes named flags', () => {
7
7
  const frame = Frame.from({
8
+ executionGas: 50_000n,
8
9
  flags: 'approveExecutionAndPayment',
9
- gas: 50_000n,
10
10
  mode: 'verify',
11
11
  to: '0x1111111111111111111111111111111111111111',
12
12
  })
13
13
  const rpc = z.encode(z.Frame.Frame, frame)
14
14
  expect(rpc).toEqual({
15
15
  data: '0x',
16
- executionGasLimit: '0xc350',
17
- flags: 3,
18
- mode: 1,
19
- stateGasLimit: '0x0',
16
+ executionGas: '0xc350',
17
+ flags: '0x3',
18
+ mode: '0x1',
19
+ stateGas: '0x0',
20
20
  target: '0x1111111111111111111111111111111111111111',
21
21
  value: '0x0',
22
22
  })
23
23
  expect(z.decode(z.Frame.Frame, rpc)).toEqual({
24
24
  data: '0x',
25
+ executionGas: 50_000n,
25
26
  flags: 3,
26
- gas: 50_000n,
27
27
  mode: 1,
28
28
  stateGas: 0n,
29
29
  to: '0x1111111111111111111111111111111111111111',
@@ -32,12 +32,14 @@ describe('Frame', () => {
32
32
  })
33
33
  test('rejects invalid mode and budget', () => {
34
34
  expect(z.safeParse(z.Frame.Decoded, { mode: 3 }).success).toBe(false)
35
- expect(z.safeParse(z.Frame.Decoded, { gas: -1n }).success).toBe(false)
35
+ expect(z.safeParse(z.Frame.Decoded, { executionGas: -1n }).success).toBe(
36
+ false,
37
+ )
36
38
  })
37
39
  test('encodes numberish values', () => {
38
40
  expect(
39
- z.encode(z.Frame.FrameToRpc, { gas: '0xc350', stateGas: 0 })
40
- .executionGasLimit,
41
+ z.encode(z.Frame.FrameToRpc, { executionGas: '0xc350', stateGas: 0 })
42
+ .executionGas,
41
43
  ).toBe('0xc350')
42
44
  })
43
45
  })
@@ -46,9 +48,9 @@ describe('FrameToRpc validation', () => {
46
48
  test.each([
47
49
  { mode: 3 },
48
50
  { flags: 8 },
49
- { gas: 2n ** 64n },
51
+ { executionGas: 2n ** 64n },
50
52
  { stateGas: -1 },
51
- { gas: '0x10000000000000000' },
53
+ { executionGas: '0x10000000000000000' },
52
54
  ])('rejects invalid frame %#', (frame) => {
53
55
  expect(
54
56
  z.safeEncode(z.Frame.FrameToRpc, frame as Frame.toRpc.Input).success,
@@ -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
+ })
@@ -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,
@@ -5,6 +5,7 @@ import * as z_Block from '../../Block.js'
5
5
  import * as z_BlockOverrides from '../../BlockOverrides.js'
6
6
  import * as z_Fee from '../../Fee.js'
7
7
  import * as z_Filter from '../../Filter.js'
8
+ import * as z_FrameReceipt from '../../FrameReceipt.js'
8
9
  import * as z_Hex from '../../Hex.js'
9
10
  import * as z_Log from '../../Log.js'
10
11
  import * as z_Number from '../../Number.js'
@@ -322,6 +323,25 @@ export const eth_simulateV1 = from({
322
323
  message: z.string(),
323
324
  }),
324
325
  ),
326
+ frameResults: z.optional(
327
+ z.readonly(
328
+ z.array(
329
+ z.intersection(
330
+ z_FrameReceipt.FrameReceipt,
331
+ z.object({
332
+ error: z.optional(
333
+ z.object({
334
+ code: z.number(),
335
+ message: z.string(),
336
+ }),
337
+ ),
338
+ returnData: z_Hex.Hex,
339
+ }),
340
+ ),
341
+ ),
342
+ ),
343
+ ),
344
+ payer: z.optional(z_Address.Address),
325
345
  logs: z.optional(z.array(z_Log.Log)),
326
346
  gasUsed: z_Hex.Hex,
327
347
  returnData: z_Hex.Hex,
package/src/zod/z.ts CHANGED
@@ -22,6 +22,7 @@ export * as Filter from './Filter.js'
22
22
  export * as Hash from './Hash.js'
23
23
  export * as Frame from './Frame.js'
24
24
  export * as FrameReceipt from './FrameReceipt.js'
25
+ export * as FrameRequest from './FrameRequest.js'
25
26
  export * as FrameSignature from './FrameSignature.js'
26
27
  export * as Hex from './Hex.js'
27
28
  export * as Log from './Log.js'