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
@@ -6,12 +6,18 @@ import * as Frame from './Frame.js'
6
6
  import * as FrameSignature from './FrameSignature.js'
7
7
  import * as Hash from './Hash.js'
8
8
  import * as Hex from './Hex.js'
9
+ import * as FrameNonce from './internal/frameNonce.js'
9
10
  import * as Quantity from './internal/quantity.js'
10
11
  import type { Assign, Compute, PartialBy } from './internal/types.js'
11
12
  import * as Rlp from './Rlp.js'
12
13
  import type * as TxEnvelopeEip4844 from './TxEnvelopeEip4844.js'
13
14
 
14
- /** An EIP-8141 transaction containing independently budgeted call frames. */
15
+ /**
16
+ * An EIP-8141 transaction containing independently budgeted call frames.
17
+ *
18
+ * Omitted `nonceKeys` preserves the original EIP-8141 encoding. Supplying keys,
19
+ * including `[0n]`, selects EIP-8250 encoding and changes the signing hash.
20
+ */
15
21
  export type TxEnvelopeEip8141 = {
16
22
  /** Versioned blob hashes. @default [] */
17
23
  blobVersionedHashes?: readonly Hex.Hex[] | undefined
@@ -25,8 +31,10 @@ export type TxEnvelopeEip8141 = {
25
31
  maxFeePerGas?: bigint | undefined
26
32
  /** Maximum priority fee per gas, in wei. @default 0n */
27
33
  maxPriorityFeePerGas?: bigint | undefined
28
- /** Sender nonce. @default 0n */
34
+ /** Sender nonce or shared sequence. With `nonceKeys`, the sequence must be less than 2^64 - 1. @default 0n */
29
35
  nonce?: bigint | undefined
36
+ /** EIP-8250 domains: 1–16 strictly increasing uint256 keys. `[0n]` selects the legacy account nonce; zero cannot accompany other keys. */
37
+ nonceKeys?: readonly bigint[] | undefined
30
38
  /** Account authorizing execution. */
31
39
  sender: Address.Address
32
40
  /** PeerDAS sidecars. Excluded from transaction and signing hashes. */
@@ -53,8 +61,10 @@ export type Rpc = {
53
61
  maxFeePerGas: Hex.Hex
54
62
  /** Maximum priority fee per gas. */
55
63
  maxPriorityFeePerGas: Hex.Hex
56
- /** Sender nonce. */
64
+ /** Sender nonce or shared EIP-8250 sequence. */
57
65
  nonce: Hex.Hex
66
+ /** EIP-8250 domains. Omitted for the original EIP-8141 encoding. */
67
+ nonceKeys?: readonly Hex.Hex[] | undefined
58
68
  /** Frame signature entries. */
59
69
  signatures: readonly FrameSignature.Rpc[]
60
70
  /** RPC transaction type. */
@@ -90,7 +100,7 @@ export type Type = typeof type
90
100
  *
91
101
  * TxEnvelopeEip8141.assert({
92
102
  * chainId: 1,
93
- * frames: [{ gas: 50_000n, mode: 'verify' }],
103
+ * frames: [{ executionGas: 50_000n, mode: 'verify' }],
94
104
  * sender: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8'
95
105
  * })
96
106
  * ```
@@ -106,6 +116,7 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
106
116
  maxFeePerGas = 0n,
107
117
  maxPriorityFeePerGas = 0n,
108
118
  nonce = 0n,
119
+ nonceKeys,
109
120
  sender,
110
121
  signatures = [],
111
122
  } = envelope
@@ -118,6 +129,7 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
118
129
  throw new InvalidError(
119
130
  'chainId must be an unsigned 256-bit integer; use bigint for large IDs.',
120
131
  )
132
+ if (nonceKeys !== undefined) FrameNonce.assert(nonceKeys, nonce)
121
133
  Address.assert(sender, { strict: false })
122
134
  for (const [field, value, bits] of [
123
135
  ['maxFeePerBlobGas', maxFeePerBlobGas, 256n],
@@ -146,7 +158,7 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
146
158
  }
147
159
  if (blobVersionedHashes.length === 0 && maxFeePerBlobGas !== 0n)
148
160
  throw new InvalidError('maxFeePerBlobGas must be zero without blobs.')
149
- let gas = 0n
161
+ let executionGas = 0n
150
162
  let stateGas = 0n
151
163
  let previousBatch = false
152
164
  let expiry = false
@@ -157,6 +169,18 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
157
169
  for (const byte of Hex.toBytes(data)) tokens += byte === 0 ? 1n : 4n
158
170
  size += BigInt(Hex.size(data))
159
171
  }
172
+ if (nonceKeys !== undefined) {
173
+ count(
174
+ Rlp.fromHex(
175
+ nonceKeys.map((key) =>
176
+ key === 0n ? '0x' : Hex.fromBytes(Bytes.fromNumber(key)),
177
+ ),
178
+ ),
179
+ )
180
+ count(
181
+ Rlp.fromHex(nonce === 0n ? '0x' : Hex.fromBytes(Bytes.fromNumber(nonce))),
182
+ )
183
+ }
160
184
  for (const [index, frame] of (frames as readonly Frame.Frame[]).entries()) {
161
185
  Frame.assert(frame)
162
186
  const flags =
@@ -193,7 +217,7 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
193
217
  throw new InvalidError('Invalid or duplicate expiry verifier frame.')
194
218
  expiry = true
195
219
  }
196
- gas += frame.gas ?? 0n
220
+ executionGas += frame.executionGas ?? 0n
197
221
  stateGas += frame.stateGas ?? 0n
198
222
  count(frame.data ?? '0x')
199
223
  if (
@@ -210,10 +234,10 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
210
234
  count(payload)
211
235
  count(signature)
212
236
  }
213
- if (gas + stateGas >= 2n ** 64n)
237
+ if (executionGas + stateGas >= 2n ** 64n)
214
238
  throw new InvalidError('Combined frame gas must be less than 2^64.')
215
239
  if (
216
- intrinsic + tokens * 4n + gas > 16_777_216n ||
240
+ intrinsic + tokens * 4n + executionGas > 16_777_216n ||
217
241
  intrinsic + size * 64n > 16_777_216n
218
242
  )
219
243
  throw new InvalidError('Transaction execution gas exceeds 16777216.')
@@ -221,6 +245,7 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
221
245
  export declare namespace assert {
222
246
  type ErrorType =
223
247
  | InvalidError
248
+ | FrameNonce.InvalidError
224
249
  | Frame.assert.ErrorType
225
250
  | FrameSignature.toTuple.ErrorType
226
251
  | Address.assert.ErrorType
@@ -258,9 +283,14 @@ export function deserialize(serialized: Serialized): TxEnvelopeEip8141 {
258
283
  throw new InvalidError('Expected a transaction list.')
259
284
  const wrapped = Array.isArray(decoded[0])
260
285
  const body = wrapped ? decoded[0] : decoded
261
- if (!Array.isArray(body) || body.length !== 7)
262
- throw new InvalidError('Expected seven transaction fields.')
263
- const [chainId, nonce, sender, frames, signatures, fees, hashes] = body
286
+ if (!Array.isArray(body) || (body.length !== 7 && body.length !== 8))
287
+ throw new InvalidError('Expected seven or eight transaction fields.')
288
+ const keyed = body.length === 8
289
+ const [chainId, ...fields] = body
290
+ const keys = keyed ? fields.shift() : undefined
291
+ if (keyed && !Array.isArray(keys))
292
+ throw new InvalidError('Expected a nonce key list.')
293
+ const [nonce, sender, frames, signatures, fees, hashes] = fields
264
294
  if (
265
295
  typeof sender !== 'string' ||
266
296
  !Array.isArray(frames) ||
@@ -288,6 +318,7 @@ export function deserialize(serialized: Serialized): TxEnvelopeEip8141 {
288
318
  maxFeePerGas: integer(fees[1]),
289
319
  maxPriorityFeePerGas: integer(fees[0]),
290
320
  nonce: integer(nonce),
321
+ ...(Array.isArray(keys) ? { nonceKeys: keys.map(integer) } : {}),
291
322
  sender: sender as Address.Address,
292
323
  signatures: signatures.map((entry) =>
293
324
  FrameSignature.fromTuple(entry as FrameSignature.Tuple),
@@ -337,8 +368,8 @@ export declare namespace deserialize {
337
368
  * chainId: 1,
338
369
  * frames: [
339
370
  * {
371
+ * executionGas: 50_000n,
340
372
  * flags: 'approveExecutionAndPayment',
341
- * gas: 50_000n,
342
373
  * mode: 'verify'
343
374
  * }
344
375
  * ],
@@ -403,6 +434,9 @@ export function fromRpc(envelope: Rpc): TxEnvelopeEip8141 {
403
434
  maxFeePerGas: Hex.toBigInt(envelope.maxFeePerGas),
404
435
  maxPriorityFeePerGas: Hex.toBigInt(envelope.maxPriorityFeePerGas),
405
436
  nonce: Hex.toBigInt(envelope.nonce),
437
+ ...(envelope.nonceKeys !== undefined
438
+ ? { nonceKeys: FrameNonce.fromRpc(envelope.nonceKeys, envelope.nonce) }
439
+ : {}),
406
440
  sender: envelope.from,
407
441
  signatures: envelope.signatures.map(FrameSignature.fromRpc),
408
442
  })
@@ -410,6 +444,7 @@ export function fromRpc(envelope: Rpc): TxEnvelopeEip8141 {
410
444
 
411
445
  export declare namespace fromRpc {
412
446
  type ErrorType =
447
+ | FrameNonce.InvalidError
413
448
  | from.ErrorType
414
449
  | Frame.fromRpc.ErrorType
415
450
  | FrameSignature.fromRpc.ErrorType
@@ -448,6 +483,9 @@ export function toRpc(envelope: toRpc.Input): Rpc {
448
483
  envelope.maxPriorityFeePerGas ?? 0n,
449
484
  ),
450
485
  nonce: Quantity.fromNumberish(envelope.nonce ?? 0n),
486
+ ...(envelope.nonceKeys !== undefined
487
+ ? { nonceKeys: FrameNonce.toRpc(envelope.nonceKeys, envelope.nonce) }
488
+ : {}),
451
489
  signatures: (envelope.signatures ?? []).map(FrameSignature.toRpc),
452
490
  type: '0x6',
453
491
  }
@@ -462,6 +500,7 @@ export declare namespace toRpc {
462
500
  | 'maxFeePerGas'
463
501
  | 'maxPriorityFeePerGas'
464
502
  | 'nonce'
503
+ | 'nonceKeys'
465
504
  | 'type'
466
505
  > & {
467
506
  chainId: Hex.Hex | bigint | number
@@ -470,9 +509,11 @@ export declare namespace toRpc {
470
509
  maxFeePerGas?: Hex.Hex | bigint | number | undefined
471
510
  maxPriorityFeePerGas?: Hex.Hex | bigint | number | undefined
472
511
  nonce?: Hex.Hex | bigint | number | undefined
512
+ nonceKeys?: readonly (Hex.Hex | bigint | number)[] | undefined
473
513
  type?: Type | undefined
474
514
  }
475
515
  type ErrorType =
516
+ | FrameNonce.InvalidError
476
517
  | Frame.toRpc.ErrorType
477
518
  | FrameSignature.toRpc.ErrorType
478
519
  | Hex.fromNumber.ErrorType
@@ -503,8 +544,8 @@ export declare namespace toRpc {
503
544
  * chainId: 1,
504
545
  * frames: [
505
546
  * {
547
+ * executionGas: 50_000n,
506
548
  * flags: 'approveExecutionAndPayment',
507
- * gas: 50_000n,
508
549
  * mode: 'verify'
509
550
  * }
510
551
  * ],
@@ -560,10 +601,12 @@ export function hash(
560
601
  ): Hex.Hex {
561
602
  assert(envelope)
562
603
  const body = toTuple(envelope)
563
- if (options.presign)
564
- body[4] = body[4].map((entry) =>
565
- entry[2] === '0x' ? [entry[0], entry[1], entry[2], '0x'] : entry,
566
- )
604
+ if (options.presign) {
605
+ const signatures = body.length === 8 ? body[5] : body[4]
606
+ for (const [index, entry] of signatures.entries())
607
+ if (entry[2] === '0x')
608
+ signatures[index] = [entry[0], entry[1], entry[2], '0x']
609
+ }
567
610
  return Hash.keccak256(Hex.concat(serializedType, Rlp.fromHex(body)))
568
611
  }
569
612
  export declare namespace hash {
@@ -589,7 +632,7 @@ export declare namespace hash {
589
632
  *
590
633
  * const envelope = TxEnvelopeEip8141.from({
591
634
  * chainId: 1,
592
- * frames: [{ gas: 50_000n, mode: 'sender' }],
635
+ * frames: [{ executionGas: 50_000n, mode: 'sender' }],
593
636
  * sender: '0x70997970c51812dc3a010c7d01b50e0d17dc79c8'
594
637
  * })
595
638
  * const serialized = TxEnvelopeEip8141.serialize(envelope)
@@ -690,8 +733,7 @@ export class InvalidError extends Errors.BaseError {
690
733
  function toTuple(envelope: PartialBy<TxEnvelopeEip8141, 'type'>) {
691
734
  const quantity = (value: bigint | number | undefined): Hex.Hex =>
692
735
  value ? Hex.fromBytes(Bytes.fromNumber(value)) : '0x'
693
- return [
694
- quantity(envelope.chainId),
736
+ const fields = [
695
737
  quantity(envelope.nonce),
696
738
  envelope.sender,
697
739
  envelope.frames.map((frame) => Frame.toTuple(frame)),
@@ -703,7 +745,6 @@ function toTuple(envelope: PartialBy<TxEnvelopeEip8141, 'type'>) {
703
745
  ],
704
746
  envelope.blobVersionedHashes ?? [],
705
747
  ] satisfies [
706
- Hex.Hex,
707
748
  Hex.Hex,
708
749
  Address.Address,
709
750
  Frame.Tuple[],
@@ -711,4 +752,11 @@ function toTuple(envelope: PartialBy<TxEnvelopeEip8141, 'type'>) {
711
752
  Hex.Hex[],
712
753
  readonly Hex.Hex[],
713
754
  ]
755
+ return envelope.nonceKeys === undefined
756
+ ? ([quantity(envelope.chainId), ...fields] as const)
757
+ : ([
758
+ quantity(envelope.chainId),
759
+ envelope.nonceKeys.map(quantity),
760
+ ...fields,
761
+ ] as const)
714
762
  }
@@ -136,6 +136,8 @@ describe('decode', () => {
136
136
  params: [hash],
137
137
  }))!
138
138
 
139
+ if (input === undefined) throw new Error('Missing transaction input')
140
+
139
141
  expect(
140
142
  AbiConstructor.decode(abiConstructor, {
141
143
  bytecode: Constructor.bytecode.object,
@@ -4,14 +4,15 @@ import { expectTypeOf, test } from 'vite-plus/test'
4
4
  test('from preserves literals', () => {
5
5
  const frame = Frame.from({
6
6
  data: '0x',
7
+ executionGas: 50_000n,
7
8
  flags: 3,
8
- gas: 50_000n,
9
9
  mode: 1,
10
10
  stateGas: 0n,
11
11
  value: 0n,
12
12
  })
13
13
  expectTypeOf(frame.mode).toEqualTypeOf<1>()
14
- expectTypeOf(frame.gas).toEqualTypeOf<50_000n>()
14
+ expectTypeOf(frame.executionGas).toEqualTypeOf<50_000n>()
15
+ expectTypeOf(frame.stateGas).toEqualTypeOf<0n>()
15
16
  expectTypeOf(Frame.toTuple(frame)).toEqualTypeOf<Frame.Tuple>()
16
17
  })
17
18
 
@@ -31,8 +32,8 @@ test('tuple accepts readonly input', () => {
31
32
  test('rejects unsupported modes and unsafe numeric budgets', () => {
32
33
  Frame.from({
33
34
  data: '0x',
35
+ executionGas: 0n,
34
36
  flags: 0,
35
- gas: 0n,
36
37
  // @ts-expect-error Unknown mode names are not supported.
37
38
  mode: 'unknown',
38
39
  stateGas: 0n,
@@ -40,9 +41,9 @@ test('rejects unsupported modes and unsafe numeric budgets', () => {
40
41
  })
41
42
  Frame.from({
42
43
  data: '0x',
43
- flags: 0,
44
44
  // @ts-expect-error Gas budgets use bigint.
45
- gas: 1,
45
+ executionGas: 1,
46
+ flags: 0,
46
47
  mode: 1,
47
48
  stateGas: 0n,
48
49
  value: 0n,
@@ -52,8 +53,8 @@ test('rejects unsupported modes and unsafe numeric budgets', () => {
52
53
  test('flags accept numbers and a strict named union', () => {
53
54
  const input = {
54
55
  data: '0x',
56
+ executionGas: 0n,
55
57
  flags: 'approvePayment',
56
- gas: 0n,
57
58
  mode: 1,
58
59
  stateGas: 0n,
59
60
  value: 0n,
@@ -74,8 +75,8 @@ test('flags accept numbers and a strict named union', () => {
74
75
  test('mode accepts numbers and a strict named union', () => {
75
76
  const frame = Frame.from({
76
77
  data: '0x',
78
+ executionGas: 0n,
77
79
  flags: 0,
78
- gas: 0n,
79
80
  mode: 'sender',
80
81
  stateGas: 0n,
81
82
  value: 1n,
@@ -95,6 +96,14 @@ test('allows omitted zero fields without widening supplied literals', () => {
95
96
  ).toEqualTypeOf<Frame.Tuple>()
96
97
  })
97
98
 
99
+ test('execution gas is named explicitly', () => {
100
+ const invalid: Frame.Frame = {
101
+ // @ts-expect-error The execution budget is named executionGas.
102
+ gas: 50_000n,
103
+ }
104
+ void invalid
105
+ })
106
+
98
107
  test('destination is named to', () => {
99
108
  const frame = Frame.from({ to: '0x1111111111111111111111111111111111111111' })
100
109
  expectTypeOf(
@@ -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(Frame.toTuple({ ...frame, flags: 0, gas: 0n, mode: 0 })),
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
- { gas: null },
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
- { gas: -1n },
189
- { gas: 2n ** 64n },
214
+ { executionGas: -1n },
215
+ { executionGas: 2n ** 64n },
190
216
  { stateGas: -1n },
191
217
  { stateGas: 2n ** 64n },
192
- { gas: 2n ** 64n - 1n, stateGas: 1n },
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
- executionGasLimit: '0x20000000000001',
225
- flags: 0,
226
- mode: 2,
227
- stateGasLimit: '0x0',
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: 0n,
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
- executionGasLimit: '0x0',
251
- flags: 3,
252
- mode: 1,
253
- stateGasLimit: '0x0',
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(Frame.toRpc({ gas: '0xc350', stateGas: 12, value: 0n })).toEqual({
284
+ expect(
285
+ Frame.toRpc({ executionGas: '0xc350', stateGas: 12, value: 0n }),
286
+ ).toEqual({
259
287
  data: '0x',
260
- executionGasLimit: '0xc350',
261
- flags: 0,
262
- mode: 0,
263
- stateGasLimit: '0xc',
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
- [0, 'reverted'],
18
- [1, 'success'],
19
- [2, 'skipped'],
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
- gasUsed: 9007199254740993n,
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', 0],
52
- ['success', 1],
53
- ['skipped', 2],
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
- gasUsed: '0x5208',
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": 1,
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
+ })