ox 0.14.49 → 0.14.52

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 (65) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/FrameRequest/package.json +6 -0
  3. package/_cjs/core/FrameRequest.js +29 -0
  4. package/_cjs/core/FrameRequest.js.map +1 -0
  5. package/_cjs/core/FrameSignature.js +7 -5
  6. package/_cjs/core/FrameSignature.js.map +1 -1
  7. package/_cjs/core/Siwe.js +3 -3
  8. package/_cjs/core/Siwe.js.map +1 -1
  9. package/_cjs/core/Transaction.js +8 -0
  10. package/_cjs/core/Transaction.js.map +1 -1
  11. package/_cjs/core/TransactionRequest.js +8 -3
  12. package/_cjs/core/TransactionRequest.js.map +1 -1
  13. package/_cjs/core/TxEnvelopeEip8141.js +37 -8
  14. package/_cjs/core/TxEnvelopeEip8141.js.map +1 -1
  15. package/_cjs/core/internal/frameNonce.js +57 -0
  16. package/_cjs/core/internal/frameNonce.js.map +1 -0
  17. package/_cjs/index.js +3 -2
  18. package/_cjs/index.js.map +1 -1
  19. package/_cjs/version.js +1 -1
  20. package/_esm/core/FrameRequest.js +53 -0
  21. package/_esm/core/FrameRequest.js.map +1 -0
  22. package/_esm/core/FrameSignature.js +7 -5
  23. package/_esm/core/FrameSignature.js.map +1 -1
  24. package/_esm/core/Siwe.js +3 -3
  25. package/_esm/core/Siwe.js.map +1 -1
  26. package/_esm/core/Transaction.js +8 -0
  27. package/_esm/core/Transaction.js.map +1 -1
  28. package/_esm/core/TransactionRequest.js +8 -3
  29. package/_esm/core/TransactionRequest.js.map +1 -1
  30. package/_esm/core/TxEnvelopeEip8141.js +37 -8
  31. package/_esm/core/TxEnvelopeEip8141.js.map +1 -1
  32. package/_esm/core/internal/frameNonce.js +62 -0
  33. package/_esm/core/internal/frameNonce.js.map +1 -0
  34. package/_esm/index.js +13 -0
  35. package/_esm/index.js.map +1 -1
  36. package/_esm/version.js +1 -1
  37. package/_types/core/FrameRequest.d.ts +44 -0
  38. package/_types/core/FrameRequest.d.ts.map +1 -0
  39. package/_types/core/FrameSignature.d.ts +4 -4
  40. package/_types/core/FrameSignature.d.ts.map +1 -1
  41. package/_types/core/Siwe.d.ts.map +1 -1
  42. package/_types/core/Transaction.d.ts +5 -2
  43. package/_types/core/Transaction.d.ts.map +1 -1
  44. package/_types/core/TransactionRequest.d.ts +8 -5
  45. package/_types/core/TransactionRequest.d.ts.map +1 -1
  46. package/_types/core/TxEnvelopeEip8141.d.ts +18 -7
  47. package/_types/core/TxEnvelopeEip8141.d.ts.map +1 -1
  48. package/_types/core/internal/frameNonce.d.ts +21 -0
  49. package/_types/core/internal/frameNonce.d.ts.map +1 -0
  50. package/_types/core/internal/rpcSchemas/eth.d.ts +35 -0
  51. package/_types/core/internal/rpcSchemas/eth.d.ts.map +1 -1
  52. package/_types/index.d.ts +13 -0
  53. package/_types/index.d.ts.map +1 -1
  54. package/_types/version.d.ts +1 -1
  55. package/core/FrameRequest.ts +72 -0
  56. package/core/FrameSignature.ts +15 -9
  57. package/core/Siwe.ts +21 -13
  58. package/core/Transaction.ts +17 -0
  59. package/core/TransactionRequest.ts +17 -7
  60. package/core/TxEnvelopeEip8141.ts +61 -13
  61. package/core/internal/frameNonce.ts +70 -0
  62. package/core/internal/rpcSchemas/eth.ts +39 -0
  63. package/index.ts +15 -0
  64. package/package.json +6 -1
  65. package/version.ts +1 -1
@@ -1,3 +1,3 @@
1
1
  /** @internal */
2
- export declare const version = "0.14.49";
2
+ export declare const version = "0.14.52";
3
3
  //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1,72 @@
1
+ import * as Frame from './Frame.js'
2
+ import type { Compute, PartialBy } from './internal/types.js'
3
+
4
+ /** A frame request whose omitted gas limits are estimated by the node. */
5
+ export type FrameRequest<bigintType = bigint> = Frame.Frame<bigintType>
6
+
7
+ /** JSON-RPC frame request. Only the execution mode is required. */
8
+ export type Rpc = Compute<
9
+ PartialBy<Frame.Rpc, Exclude<keyof Frame.Rpc, 'mode'>>
10
+ >
11
+
12
+ /**
13
+ * Converts an RPC frame request, preserving omitted gas limits for node estimation.
14
+ *
15
+ * @example
16
+ * ```ts twoslash
17
+ * import { FrameRequest } from 'ox'
18
+ *
19
+ * const frame = FrameRequest.fromRpc({ mode: '0x2', stateGas: '0x0' })
20
+ * ```
21
+ *
22
+ * @param frame - The RPC frame request.
23
+ * @returns The decoded request with omitted gas limits preserved.
24
+ */
25
+ export function fromRpc(frame: Rpc): FrameRequest {
26
+ const { executionGas, stateGas, ...rest } = Frame.fromRpc({
27
+ ...frame,
28
+ data: frame.data ?? '0x',
29
+ executionGas: frame.executionGas ?? '0x0',
30
+ flags: frame.flags ?? '0x0',
31
+ stateGas: frame.stateGas ?? '0x0',
32
+ value: frame.value ?? '0x0',
33
+ })
34
+ return {
35
+ ...rest,
36
+ ...(frame.executionGas === undefined ? {} : { executionGas }),
37
+ ...(frame.stateGas === undefined ? {} : { stateGas }),
38
+ }
39
+ }
40
+
41
+ export declare namespace fromRpc {
42
+ type ErrorType = Frame.fromRpc.ErrorType
43
+ }
44
+
45
+ /**
46
+ * Converts a frame request to RPC fields without filling omitted gas limits.
47
+ *
48
+ * Explicit zero limits remain zero. Omitted mode, flags, value, and data use frame defaults.
49
+ *
50
+ * @example
51
+ * ```ts twoslash
52
+ * import { FrameRequest } from 'ox'
53
+ *
54
+ * const frame = FrameRequest.toRpc({ mode: 'sender', stateGas: 0n })
55
+ * ```
56
+ *
57
+ * @param frame - The frame request. Gas and value accept hex, bigint, or number values.
58
+ * @returns The RPC request with omitted gas limits preserved.
59
+ */
60
+ export function toRpc(frame: toRpc.Input): Rpc {
61
+ const { executionGas, stateGas, ...rest } = Frame.toRpc(frame)
62
+ return {
63
+ ...rest,
64
+ ...(frame.executionGas === undefined ? {} : { executionGas }),
65
+ ...(frame.stateGas === undefined ? {} : { stateGas }),
66
+ }
67
+ }
68
+
69
+ export declare namespace toRpc {
70
+ type Input = Frame.toRpc.Input
71
+ type ErrorType = Frame.toRpc.ErrorType
72
+ }
@@ -32,7 +32,7 @@ export type Secp256k1 = {
32
32
  payload?: Hex.Hex | undefined
33
33
  /** secp256k1 verification scheme. */
34
34
  scheme: 1 | 'secp256k1'
35
- /** Recovered signature or `Signature.toHex` bytes. Omit for an unsigned entry. */
35
+ /** Recovered signature or `Signature.toHex` bytes. Omit or use `0x` for an unsigned entry. */
36
36
  signature?: Hex.Hex | Signature.Signature | undefined
37
37
  /** Signer address. Omit to use the transaction sender. */
38
38
  signer?: Address.Address | undefined
@@ -56,8 +56,8 @@ export type P256 = {
56
56
  | {
57
57
  /** Public key, if already known. Empty wire signatures do not retain it. */
58
58
  publicKey?: PublicKey.PublicKey | undefined
59
- /** Omit for an unsigned entry. */
60
- signature?: undefined
59
+ /** Omit or use `0x` for an unsigned entry. */
60
+ signature?: '0x' | undefined
61
61
  }
62
62
  )
63
63
 
@@ -73,7 +73,7 @@ export type Rpc = {
73
73
  /** Encoded signature bytes. */
74
74
  signature?: Hex.Hex | undefined
75
75
  /** Signer address; absent for the transaction sender. */
76
- signer?: Address.Address | null | undefined
76
+ signer?: Address.Address | '0x' | undefined
77
77
  }
78
78
 
79
79
  /** RLP-ready signature entry. The payload occupies the specification's `msg` field. */
@@ -167,10 +167,14 @@ export function assert(
167
167
  throw new InvalidError('Public key coordinates must fit in 32 bytes.')
168
168
  }
169
169
  }
170
- if (entry.signature !== undefined && entry.publicKey === undefined)
170
+ if (
171
+ entry.signature !== undefined &&
172
+ entry.signature !== '0x' &&
173
+ entry.publicKey === undefined
174
+ )
171
175
  throw new InvalidError('P-256 signatures require a public key.')
172
176
  }
173
- if (entry.signature === undefined) {
177
+ if (entry.signature === undefined || entry.signature === '0x') {
174
178
  if (options.signed) throw new InvalidError('Signature is required.')
175
179
  return
176
180
  }
@@ -531,7 +535,8 @@ export function toTuple(entry: FrameSignature): Tuple {
531
535
  return entry.signature
532
536
  case 1:
533
537
  case 'secp256k1': {
534
- if (entry.signature === undefined) return '0x'
538
+ if (entry.signature === undefined || entry.signature === '0x')
539
+ return '0x'
535
540
  const signature =
536
541
  typeof entry.signature === 'string'
537
542
  ? Signature.fromHex(entry.signature)
@@ -544,8 +549,9 @@ export function toTuple(entry: FrameSignature): Tuple {
544
549
  }
545
550
  case 2:
546
551
  case 'p256': {
547
- if (entry.signature === undefined) return '0x'
548
- const { publicKey } = entry
552
+ if (entry.signature === undefined || entry.signature === '0x')
553
+ return '0x'
554
+ const publicKey = entry.publicKey!
549
555
  const signature =
550
556
  typeof entry.signature === 'string'
551
557
  ? Signature.fromHex(entry.signature)
package/core/Siwe.ts CHANGED
@@ -21,7 +21,7 @@ export const prefixRegex =
21
21
 
22
22
  // https://regexr.com/80gf9
23
23
  export const suffixRegex =
24
- /(?:URI: (?<uri>.+))\n(?:Version: (?<version>.+))\n(?:Chain ID: (?<chainId>\d+))\n(?:Nonce: (?<nonce>[a-zA-Z0-9]+))\n(?:Issued At: (?<issuedAt>.+))(?:\nExpiration Time: (?<expirationTime>.+))?(?:\nNot Before: (?<notBefore>.+))?(?:\nRequest ID: (?<requestId>.+))?/
24
+ /(?:URI: (?<uri>.+))\n(?:Version: (?<version>.+))\n(?:Chain ID: (?<chainId>\d+))\n(?:Nonce: (?<nonce>[a-zA-Z0-9]+))\n(?:Issued At: (?<issuedAt>.+))(?:\nExpiration Time: (?<expirationTime>.+))?(?:\nNot Before: (?<notBefore>.+))?(?:\nRequest ID: (?<requestId>.*))?(?:\nResources:(?<resources>(?:\n- .+)*))?/
25
25
 
26
26
  /** [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) message fields. */
27
27
  export type Message = {
@@ -372,18 +372,26 @@ export function parseMessage(message: string): ExactPartial<Message> {
372
372
  scheme?: string
373
373
  statement?: string
374
374
  }
375
- const { chainId, expirationTime, issuedAt, notBefore, requestId, ...suffix } =
376
- (message.match(suffixRegex)?.groups ?? {}) as {
377
- chainId: string
378
- expirationTime?: string
379
- issuedAt?: string
380
- nonce: string
381
- notBefore?: string
382
- requestId?: string
383
- uri: string
384
- version: '1'
385
- }
386
- const resources = message.split('Resources:')[1]?.split('\n- ').slice(1)
375
+ const {
376
+ chainId,
377
+ expirationTime,
378
+ issuedAt,
379
+ notBefore,
380
+ requestId,
381
+ resources: resources_,
382
+ ...suffix
383
+ } = (message.match(suffixRegex)?.groups ?? {}) as {
384
+ chainId: string
385
+ expirationTime?: string
386
+ issuedAt?: string
387
+ nonce: string
388
+ notBefore?: string
389
+ requestId?: string
390
+ resources?: string
391
+ uri: string
392
+ version: '1'
393
+ }
394
+ const resources = resources_?.split('\n- ').slice(1)
387
395
  return {
388
396
  ...prefix,
389
397
  ...suffix,
@@ -5,6 +5,7 @@ import type * as Errors from './Errors.js'
5
5
  import * as Frame from './Frame.js'
6
6
  import * as FrameSignature from './FrameSignature.js'
7
7
  import * as Hex from './Hex.js'
8
+ import * as FrameNonce from './internal/frameNonce.js'
8
9
  import type { Compute, OneOf, UnionCompute } from './internal/types.js'
9
10
  import * as Signature from './Signature.js'
10
11
 
@@ -214,6 +215,8 @@ export type Eip8141<
214
215
  maxFeePerGas: bigintType
215
216
  /** Maximum priority fee per gas. */
216
217
  maxPriorityFeePerGas: bigintType
218
+ /** EIP-8250 domains sharing the transaction's `nonce` sequence. */
219
+ nonceKeys?: readonly bigintType[] | undefined
217
220
  /** Frame signature entries. */
218
221
  signatures: readonly ([bigintType] extends [Hex.Hex]
219
222
  ? FrameSignature.Rpc
@@ -354,6 +357,16 @@ export function fromRpc<
354
357
  : null
355
358
  transaction_.value = BigInt(transaction.value ?? 0n)
356
359
 
360
+ if (transaction.type === '0x6' && transaction.nonceKeys !== undefined) {
361
+ if (transaction.nonce === undefined)
362
+ throw new FrameNonce.InvalidError(
363
+ 'A keyed transaction result requires nonce.',
364
+ )
365
+ transaction_.nonceKeys = FrameNonce.fromRpc(
366
+ transaction.nonceKeys,
367
+ transaction.nonce,
368
+ )
369
+ }
357
370
  if (transaction.frames)
358
371
  transaction_.frames = transaction.frames.map(Frame.fromRpc)
359
372
  if (transaction.signatures)
@@ -389,6 +402,7 @@ export declare namespace fromRpc {
389
402
  }
390
403
 
391
404
  type ErrorType =
405
+ | FrameNonce.InvalidError
392
406
  | Frame.fromRpc.ErrorType
393
407
  | FrameSignature.fromRpc.ErrorType
394
408
  | Signature.extract.ErrorType
@@ -456,6 +470,8 @@ export function toRpc<pending extends boolean = false>(
456
470
  rpc.type = (toRpcType as any)[transaction.type] ?? transaction.type
457
471
  rpc.value = Hex.fromNumber(transaction.value ?? 0n)
458
472
 
473
+ if (transaction.type === 'eip8141' && transaction.nonceKeys !== undefined)
474
+ rpc.nonceKeys = FrameNonce.toRpc(transaction.nonceKeys, transaction.nonce)
459
475
  if (transaction.frames) rpc.frames = transaction.frames.map(Frame.toRpc)
460
476
  if (transaction.signatures)
461
477
  rpc.signatures = transaction.signatures.map(FrameSignature.toRpc)
@@ -493,6 +509,7 @@ export declare namespace toRpc {
493
509
  }
494
510
 
495
511
  type ErrorType =
512
+ | FrameNonce.InvalidError
496
513
  | Frame.toRpc.ErrorType
497
514
  | FrameSignature.toRpc.ErrorType
498
515
  | Signature.extract.ErrorType
@@ -2,9 +2,10 @@ import type * as AccessList from './AccessList.js'
2
2
  import type * as Address from './Address.js'
3
3
  import * as Authorization from './Authorization.js'
4
4
  import type * as Errors from './Errors.js'
5
- import * as Frame from './Frame.js'
5
+ import * as FrameRequest from './FrameRequest.js'
6
6
  import * as FrameSignature from './FrameSignature.js'
7
7
  import * as Hex from './Hex.js'
8
+ import * as FrameNonce from './internal/frameNonce.js'
8
9
  import type { Compute } from './internal/types.js'
9
10
  import * as Transaction from './Transaction.js'
10
11
 
@@ -13,7 +14,7 @@ export type TransactionRequest<
13
14
  bigintType = bigint,
14
15
  numberType = number,
15
16
  type extends string = string,
16
- frame = Frame.Frame<bigintType>,
17
+ frame = FrameRequest.FrameRequest<bigintType>,
17
18
  signature = FrameSignature.FrameSignature,
18
19
  > = Compute<{
19
20
  /** EIP-2930 Access List. */
@@ -48,6 +49,8 @@ export type TransactionRequest<
48
49
  maxPriorityFeePerGas?: bigintType | undefined
49
50
  /** Unique number identifying this transaction */
50
51
  nonce?: bigintType | undefined
52
+ /** EIP-8250 nonce domains. `nonce` is their shared sequence; omitted keys preserve EIP-8141 encoding. */
53
+ nonceKeys?: readonly bigintType[] | undefined
51
54
  /** Frame signature entries for EIP-8141 transactions. */
52
55
  signatures?: readonly signature[] | undefined
53
56
  /** Transaction recipient */
@@ -63,7 +66,7 @@ export type Rpc = TransactionRequest<
63
66
  Hex.Hex,
64
67
  Hex.Hex,
65
68
  string,
66
- Frame.Rpc,
69
+ FrameRequest.Rpc,
67
70
  FrameSignature.Rpc
68
71
  >
69
72
 
@@ -92,7 +95,7 @@ export function fromRpc(request: Rpc): TransactionRequest {
92
95
  )
93
96
  if (typeof request.chainId !== 'undefined')
94
97
  request_.chainId = Hex.toNumber(request.chainId)
95
- if (request.frames) request_.frames = request.frames.map(Frame.fromRpc)
98
+ if (request.frames) request_.frames = request.frames.map(FrameRequest.fromRpc)
96
99
  if (request.signatures)
97
100
  request_.signatures = request.signatures.map(FrameSignature.fromRpc)
98
101
  if (typeof request.gas !== 'undefined')
@@ -107,6 +110,8 @@ export function fromRpc(request: Rpc): TransactionRequest {
107
110
  request_.maxPriorityFeePerGas = Hex.toBigInt(request.maxPriorityFeePerGas)
108
111
  if (typeof request.nonce !== 'undefined')
109
112
  request_.nonce = Hex.toBigInt(request.nonce)
113
+ if (request.nonceKeys !== undefined)
114
+ request_.nonceKeys = FrameNonce.fromRpc(request.nonceKeys, request.nonce)
110
115
  if (typeof request.type !== 'undefined')
111
116
  request_.type =
112
117
  Transaction.fromRpcType[
@@ -120,7 +125,8 @@ export function fromRpc(request: Rpc): TransactionRequest {
120
125
 
121
126
  export declare namespace fromRpc {
122
127
  export type ErrorType =
123
- | Frame.fromRpc.ErrorType
128
+ | FrameNonce.InvalidError
129
+ | FrameRequest.fromRpc.ErrorType
124
130
  | FrameSignature.fromRpc.ErrorType
125
131
  | Authorization.fromRpcList.ErrorType
126
132
  | Hex.toNumber.ErrorType
@@ -189,7 +195,8 @@ export function toRpc(request: TransactionRequest): Rpc {
189
195
  request_rpc.input = request.input
190
196
  }
191
197
  if (typeof request.from !== 'undefined') request_rpc.from = request.from
192
- if (request.frames) request_rpc.frames = request.frames.map(Frame.toRpc)
198
+ if (request.frames)
199
+ request_rpc.frames = request.frames.map(FrameRequest.toRpc)
193
200
  if (request.signatures)
194
201
  request_rpc.signatures = request.signatures.map(FrameSignature.toRpc)
195
202
  if (typeof request.gas !== 'undefined')
@@ -210,6 +217,8 @@ export function toRpc(request: TransactionRequest): Rpc {
210
217
  )
211
218
  if (typeof request.nonce !== 'undefined')
212
219
  request_rpc.nonce = Hex.fromNumber(request.nonce)
220
+ if (request.nonceKeys !== undefined)
221
+ request_rpc.nonceKeys = FrameNonce.toRpc(request.nonceKeys, request.nonce)
213
222
  if (typeof request.to !== 'undefined') request_rpc.to = request.to
214
223
  if (typeof request.type !== 'undefined')
215
224
  request_rpc.type =
@@ -224,7 +233,8 @@ export function toRpc(request: TransactionRequest): Rpc {
224
233
 
225
234
  export declare namespace toRpc {
226
235
  export type ErrorType =
227
- | Frame.toRpc.ErrorType
236
+ | FrameNonce.InvalidError
237
+ | FrameRequest.toRpc.ErrorType
228
238
  | FrameSignature.toRpc.ErrorType
229
239
  | Authorization.toRpcList.ErrorType
230
240
  | Hex.fromNumber.ErrorType
@@ -6,11 +6,17 @@ 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
 
13
- /** An EIP-8141 transaction containing independently budgeted call frames. */
14
+ /**
15
+ * An EIP-8141 transaction containing independently budgeted call frames.
16
+ *
17
+ * Omitted `nonceKeys` preserves the original EIP-8141 encoding. Supplying keys,
18
+ * including `[0n]`, selects EIP-8250 encoding and changes the signing hash.
19
+ */
14
20
  export type TxEnvelopeEip8141 = {
15
21
  /** Versioned blob hashes. @default [] */
16
22
  blobVersionedHashes?: readonly Hex.Hex[] | undefined
@@ -24,8 +30,10 @@ export type TxEnvelopeEip8141 = {
24
30
  maxFeePerGas?: bigint | undefined
25
31
  /** Maximum priority fee per gas, in wei. @default 0n */
26
32
  maxPriorityFeePerGas?: bigint | undefined
27
- /** Sender nonce. @default 0n */
33
+ /** Sender nonce or shared sequence. With `nonceKeys`, the sequence must be less than 2^64 - 1. @default 0n */
28
34
  nonce?: bigint | undefined
35
+ /** EIP-8250 domains: 1–16 strictly increasing uint256 keys. `[0n]` selects the legacy account nonce; zero cannot accompany other keys. */
36
+ nonceKeys?: readonly bigint[] | undefined
29
37
  /** Account authorizing execution. */
30
38
  sender: Address.Address
31
39
  /** PeerDAS sidecars. Excluded from transaction and signing hashes. */
@@ -58,8 +66,10 @@ export type Rpc = {
58
66
  maxFeePerGas: Hex.Hex
59
67
  /** Maximum priority fee per gas. */
60
68
  maxPriorityFeePerGas: Hex.Hex
61
- /** Sender nonce. */
69
+ /** Sender nonce or shared EIP-8250 sequence. */
62
70
  nonce: Hex.Hex
71
+ /** EIP-8250 domains. Omitted for the original EIP-8141 encoding. */
72
+ nonceKeys?: readonly Hex.Hex[] | undefined
63
73
  /** Frame signature entries. */
64
74
  signatures: readonly FrameSignature.Rpc[]
65
75
  /** RPC transaction type. */
@@ -111,6 +121,7 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
111
121
  maxFeePerGas = 0n,
112
122
  maxPriorityFeePerGas = 0n,
113
123
  nonce = 0n,
124
+ nonceKeys,
114
125
  sender,
115
126
  signatures = [],
116
127
  } = envelope
@@ -123,6 +134,7 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
123
134
  throw new InvalidError(
124
135
  'chainId must be an unsigned 256-bit integer; use bigint for large IDs.',
125
136
  )
137
+ if (nonceKeys !== undefined) FrameNonce.assert(nonceKeys, nonce)
126
138
  Address.assert(sender, { strict: false })
127
139
  for (const [field, value, bits] of [
128
140
  ['maxFeePerBlobGas', maxFeePerBlobGas, 256n],
@@ -162,6 +174,18 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
162
174
  for (const byte of Hex.toBytes(data)) tokens += byte === 0 ? 1n : 4n
163
175
  size += BigInt(Hex.size(data))
164
176
  }
177
+ if (nonceKeys !== undefined) {
178
+ count(
179
+ Rlp.fromHex(
180
+ nonceKeys.map((key) =>
181
+ key === 0n ? '0x' : Hex.fromBytes(Bytes.fromNumber(key)),
182
+ ),
183
+ ),
184
+ )
185
+ count(
186
+ Rlp.fromHex(nonce === 0n ? '0x' : Hex.fromBytes(Bytes.fromNumber(nonce))),
187
+ )
188
+ }
165
189
  for (const [index, frame] of (frames as readonly Frame.Frame[]).entries()) {
166
190
  Frame.assert(frame)
167
191
  const flags =
@@ -226,6 +250,7 @@ export function assert(envelope: PartialBy<TxEnvelopeEip8141, 'type'>): void {
226
250
  export declare namespace assert {
227
251
  type ErrorType =
228
252
  | InvalidError
253
+ | FrameNonce.InvalidError
229
254
  | Frame.assert.ErrorType
230
255
  | FrameSignature.toTuple.ErrorType
231
256
  | Address.assert.ErrorType
@@ -263,9 +288,14 @@ export function deserialize(serialized: Serialized): TxEnvelopeEip8141 {
263
288
  throw new InvalidError('Expected a transaction list.')
264
289
  const wrapped = Array.isArray(decoded[0])
265
290
  const body = wrapped ? decoded[0] : decoded
266
- if (!Array.isArray(body) || body.length !== 7)
267
- throw new InvalidError('Expected seven transaction fields.')
268
- const [chainId, nonce, sender, frames, signatures, fees, hashes] = body
291
+ if (!Array.isArray(body) || (body.length !== 7 && body.length !== 8))
292
+ throw new InvalidError('Expected seven or eight transaction fields.')
293
+ const keyed = body.length === 8
294
+ const [chainId, ...fields] = body
295
+ const keys = keyed ? fields.shift() : undefined
296
+ if (keyed && !Array.isArray(keys))
297
+ throw new InvalidError('Expected a nonce key list.')
298
+ const [nonce, sender, frames, signatures, fees, hashes] = fields
269
299
  if (
270
300
  typeof sender !== 'string' ||
271
301
  !Array.isArray(frames) ||
@@ -293,6 +323,7 @@ export function deserialize(serialized: Serialized): TxEnvelopeEip8141 {
293
323
  maxFeePerGas: integer(fees[1]),
294
324
  maxPriorityFeePerGas: integer(fees[0]),
295
325
  nonce: integer(nonce),
326
+ ...(Array.isArray(keys) ? { nonceKeys: keys.map(integer) } : {}),
296
327
  sender: sender as Address.Address,
297
328
  signatures: signatures.map((entry) =>
298
329
  FrameSignature.fromTuple(entry as FrameSignature.Tuple),
@@ -408,6 +439,9 @@ export function fromRpc(envelope: Rpc): TxEnvelopeEip8141 {
408
439
  maxFeePerGas: Hex.toBigInt(envelope.maxFeePerGas),
409
440
  maxPriorityFeePerGas: Hex.toBigInt(envelope.maxPriorityFeePerGas),
410
441
  nonce: Hex.toBigInt(envelope.nonce),
442
+ ...(envelope.nonceKeys !== undefined
443
+ ? { nonceKeys: FrameNonce.fromRpc(envelope.nonceKeys, envelope.nonce) }
444
+ : {}),
411
445
  sender: envelope.from,
412
446
  signatures: envelope.signatures.map(FrameSignature.fromRpc),
413
447
  })
@@ -415,6 +449,7 @@ export function fromRpc(envelope: Rpc): TxEnvelopeEip8141 {
415
449
 
416
450
  export declare namespace fromRpc {
417
451
  type ErrorType =
452
+ | FrameNonce.InvalidError
418
453
  | from.ErrorType
419
454
  | Frame.fromRpc.ErrorType
420
455
  | FrameSignature.fromRpc.ErrorType
@@ -453,6 +488,9 @@ export function toRpc(envelope: toRpc.Input): Rpc {
453
488
  envelope.maxPriorityFeePerGas ?? 0n,
454
489
  ),
455
490
  nonce: Quantity.fromNumberish(envelope.nonce ?? 0n),
491
+ ...(envelope.nonceKeys !== undefined
492
+ ? { nonceKeys: FrameNonce.toRpc(envelope.nonceKeys, envelope.nonce) }
493
+ : {}),
456
494
  signatures: (envelope.signatures ?? []).map(FrameSignature.toRpc),
457
495
  type: '0x6',
458
496
  }
@@ -467,6 +505,7 @@ export declare namespace toRpc {
467
505
  | 'maxFeePerGas'
468
506
  | 'maxPriorityFeePerGas'
469
507
  | 'nonce'
508
+ | 'nonceKeys'
470
509
  | 'type'
471
510
  > & {
472
511
  chainId: Hex.Hex | bigint | number
@@ -475,9 +514,11 @@ export declare namespace toRpc {
475
514
  maxFeePerGas?: Hex.Hex | bigint | number | undefined
476
515
  maxPriorityFeePerGas?: Hex.Hex | bigint | number | undefined
477
516
  nonce?: Hex.Hex | bigint | number | undefined
517
+ nonceKeys?: readonly (Hex.Hex | bigint | number)[] | undefined
478
518
  type?: Type | undefined
479
519
  }
480
520
  type ErrorType =
521
+ | FrameNonce.InvalidError
481
522
  | Frame.toRpc.ErrorType
482
523
  | FrameSignature.toRpc.ErrorType
483
524
  | Hex.fromNumber.ErrorType
@@ -565,10 +606,12 @@ export function hash(
565
606
  ): Hex.Hex {
566
607
  assert(envelope)
567
608
  const body = toTuple(envelope)
568
- if (options.presign)
569
- body[4] = body[4].map((entry) =>
570
- entry[2] === '0x' ? [entry[0], entry[1], entry[2], '0x'] : entry,
571
- )
609
+ if (options.presign) {
610
+ const signatures = body.length === 8 ? body[5] : body[4]
611
+ for (const [index, entry] of signatures.entries())
612
+ if (entry[2] === '0x')
613
+ signatures[index] = [entry[0], entry[1], entry[2], '0x']
614
+ }
572
615
  return Hash.keccak256(Hex.concat(serializedType, Rlp.fromHex(body)))
573
616
  }
574
617
  export declare namespace hash {
@@ -692,8 +735,7 @@ export class InvalidError extends Errors.BaseError {
692
735
  function toTuple(envelope: PartialBy<TxEnvelopeEip8141, 'type'>) {
693
736
  const quantity = (value: bigint | number | undefined): Hex.Hex =>
694
737
  value ? Hex.fromBytes(Bytes.fromNumber(value)) : '0x'
695
- return [
696
- quantity(envelope.chainId),
738
+ const fields = [
697
739
  quantity(envelope.nonce),
698
740
  envelope.sender,
699
741
  envelope.frames.map((frame) => Frame.toTuple(frame)),
@@ -705,7 +747,6 @@ function toTuple(envelope: PartialBy<TxEnvelopeEip8141, 'type'>) {
705
747
  ],
706
748
  envelope.blobVersionedHashes ?? [],
707
749
  ] satisfies [
708
- Hex.Hex,
709
750
  Hex.Hex,
710
751
  Address.Address,
711
752
  Frame.Tuple[],
@@ -713,4 +754,11 @@ function toTuple(envelope: PartialBy<TxEnvelopeEip8141, 'type'>) {
713
754
  Hex.Hex[],
714
755
  readonly Hex.Hex[],
715
756
  ]
757
+ return envelope.nonceKeys === undefined
758
+ ? ([quantity(envelope.chainId), ...fields] as const)
759
+ : ([
760
+ quantity(envelope.chainId),
761
+ envelope.nonceKeys.map(quantity),
762
+ ...fields,
763
+ ] as const)
716
764
  }
@@ -0,0 +1,70 @@
1
+ import * as Errors from '../Errors.js'
2
+ import * as Hex from '../Hex.js'
3
+ import * as Quantity from './quantity.js'
4
+
5
+ /**
6
+ * Asserts keyed nonce constraints.
7
+ * @internal
8
+ */
9
+ export function assert(keys: readonly bigint[], nonce?: bigint): void {
10
+ if (!Array.isArray(keys) || keys.length < 1 || keys.length > 16)
11
+ throw new InvalidError('Expected between 1 and 16 nonce keys.')
12
+ for (const [index, key] of keys.entries()) {
13
+ if (typeof key !== 'bigint' || key < 0n || key >= 2n ** 256n)
14
+ throw new InvalidError('Nonce keys must be unsigned 256-bit integers.')
15
+ if (index > 0 && key <= keys[index - 1]!)
16
+ throw new InvalidError('Nonce keys must be strictly increasing.')
17
+ }
18
+ if (keys.length > 1 && keys[0] === 0n)
19
+ throw new InvalidError(
20
+ 'The zero nonce key cannot be combined with other keys.',
21
+ )
22
+ if (
23
+ nonce !== undefined &&
24
+ (typeof nonce !== 'bigint' || nonce < 0n || nonce >= 2n ** 64n - 1n)
25
+ )
26
+ throw new InvalidError('Nonce must be less than 2^64 - 1 and nonnegative.')
27
+ }
28
+
29
+ /**
30
+ * Decodes and validates RPC nonce keys and their shared sequence.
31
+ * @internal
32
+ */
33
+ export function fromRpc(
34
+ keys: readonly Hex.Hex[],
35
+ nonce?: Hex.Hex,
36
+ ): readonly bigint[] {
37
+ if (!Array.isArray(keys)) throw new InvalidError('Expected a nonce key list.')
38
+ const integer = (value: Hex.Hex): bigint => {
39
+ if (
40
+ typeof value !== 'string' ||
41
+ !/^0x(?:0|[1-9a-fA-F][0-9a-fA-F]*)$/.test(value)
42
+ )
43
+ throw new InvalidError('Expected a canonical RPC nonce quantity.')
44
+ return Hex.toBigInt(value)
45
+ }
46
+ const decoded = keys.map(integer)
47
+ assert(decoded, nonce === undefined ? undefined : integer(nonce))
48
+ return decoded
49
+ }
50
+
51
+ /**
52
+ * Encodes and validates nonce keys and their shared sequence.
53
+ * @internal
54
+ */
55
+ export function toRpc(
56
+ keys: readonly (Hex.Hex | bigint | number)[],
57
+ nonce?: Hex.Hex | bigint | number,
58
+ ): readonly Hex.Hex[] {
59
+ if (!Array.isArray(keys)) throw new InvalidError('Expected a nonce key list.')
60
+ const encoded = keys.map((key) => Quantity.fromNumberish(key))
61
+ fromRpc(
62
+ encoded,
63
+ nonce === undefined ? undefined : Quantity.fromNumberish(nonce),
64
+ )
65
+ return encoded
66
+ }
67
+
68
+ export class InvalidError extends Errors.BaseError {
69
+ override readonly name = 'FrameNonce.InvalidError'
70
+ }
@@ -4,6 +4,7 @@ import type * as Block from '../../Block.js'
4
4
  import type * as BlockOverrides from '../../BlockOverrides.js'
5
5
  import type * as Fee from '../../Fee.js'
6
6
  import type * as Filter from '../../Filter.js'
7
+ import type * as FrameReceipt from '../../FrameReceipt.js'
7
8
  import type * as Hex from '../../Hex.js'
8
9
  import type * as Log from '../../Log.js'
9
10
  import type * as RpcSchema from '../../RpcSchema.js'
@@ -11,6 +12,12 @@ import type * as StateOverrides from '../../StateOverrides.js'
11
12
  import type * as Transaction from '../../Transaction.js'
12
13
  import type * as TransactionReceipt from '../../TransactionReceipt.js'
13
14
  import type * as TransactionRequest from '../../TransactionRequest.js'
15
+ import type * as TxEnvelopeEip1559 from '../../TxEnvelopeEip1559.js'
16
+ import type * as TxEnvelopeEip2930 from '../../TxEnvelopeEip2930.js'
17
+ import type * as TxEnvelopeEip4844 from '../../TxEnvelopeEip4844.js'
18
+ import type * as TxEnvelopeEip7702 from '../../TxEnvelopeEip7702.js'
19
+ import type * as TxEnvelopeEip8141 from '../../TxEnvelopeEip8141.js'
20
+ import type * as TxEnvelopeLegacy from '../../TxEnvelopeLegacy.js'
14
21
 
15
22
  /**
16
23
  * Union of all JSON-RPC Methods for the `eth_` namespace.
@@ -217,6 +224,31 @@ export type Eth = RpcSchema.From<
217
224
  }
218
225
  ReturnType: Fee.FeeHistoryRpc
219
226
  }
227
+ /**
228
+ * Fills transaction defaults and returns the unsigned transaction.
229
+ *
230
+ * @example
231
+ * ```
232
+ * request({ method: 'eth_fillTransaction', params: [{ from: '0x...', to: '0x...', value: '0x...' }] })
233
+ * // { tx: { ... } }
234
+ * ```
235
+ */
236
+ | {
237
+ Request: {
238
+ method: 'eth_fillTransaction'
239
+ params: [transaction: TransactionRequest.Rpc]
240
+ }
241
+ ReturnType: {
242
+ tx: (
243
+ | TxEnvelopeLegacy.Rpc<false>
244
+ | TxEnvelopeEip2930.Rpc<false>
245
+ | TxEnvelopeEip1559.Rpc<false>
246
+ | TxEnvelopeEip4844.Rpc<false>
247
+ | TxEnvelopeEip7702.Rpc<false>
248
+ | TxEnvelopeEip8141.Rpc
249
+ ) & { from: Address.Address }
250
+ }
251
+ }
220
252
  /**
221
253
  * Returns the current price of gas expressed in wei
222
254
  *
@@ -772,6 +804,13 @@ export type Eth = RpcSchema.From<
772
804
  message: string
773
805
  }
774
806
  | undefined
807
+ frameResults?:
808
+ | readonly (FrameReceipt.Rpc & {
809
+ error?: { code: number; message: string } | undefined
810
+ returnData: Hex.Hex
811
+ })[]
812
+ | undefined
813
+ payer?: Address.Address | undefined
775
814
  logs?: readonly Log.Rpc[] | undefined
776
815
  gasUsed: Hex.Hex
777
816
  returnData: Hex.Hex