ox 0.14.51 → 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.
- package/CHANGELOG.md +18 -0
- package/_cjs/core/Transaction.js +8 -0
- package/_cjs/core/Transaction.js.map +1 -1
- package/_cjs/core/TransactionRequest.js +5 -0
- package/_cjs/core/TransactionRequest.js.map +1 -1
- package/_cjs/core/TxEnvelopeEip8141.js +37 -8
- package/_cjs/core/TxEnvelopeEip8141.js.map +1 -1
- package/_cjs/core/internal/frameNonce.js +57 -0
- package/_cjs/core/internal/frameNonce.js.map +1 -0
- package/_cjs/version.js +1 -1
- package/_esm/core/Transaction.js +8 -0
- package/_esm/core/Transaction.js.map +1 -1
- package/_esm/core/TransactionRequest.js +5 -0
- package/_esm/core/TransactionRequest.js.map +1 -1
- package/_esm/core/TxEnvelopeEip8141.js +37 -8
- package/_esm/core/TxEnvelopeEip8141.js.map +1 -1
- package/_esm/core/internal/frameNonce.js +62 -0
- package/_esm/core/internal/frameNonce.js.map +1 -0
- package/_esm/version.js +1 -1
- package/_types/core/Transaction.d.ts +5 -2
- package/_types/core/Transaction.d.ts.map +1 -1
- package/_types/core/TransactionRequest.d.ts +5 -2
- package/_types/core/TransactionRequest.d.ts.map +1 -1
- package/_types/core/TxEnvelopeEip8141.d.ts +18 -7
- package/_types/core/TxEnvelopeEip8141.d.ts.map +1 -1
- package/_types/core/internal/frameNonce.d.ts +21 -0
- package/_types/core/internal/frameNonce.d.ts.map +1 -0
- package/_types/version.d.ts +1 -1
- package/core/Transaction.ts +17 -0
- package/core/TransactionRequest.ts +9 -0
- package/core/TxEnvelopeEip8141.ts +61 -13
- package/core/internal/frameNonce.ts +70 -0
- package/package.json +1 -1
- package/version.ts +1 -1
|
@@ -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
|
-
/**
|
|
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
|
|
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[
|
|
570
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/package.json
CHANGED
package/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @internal */
|
|
2
|
-
export const version = '0.14.
|
|
2
|
+
export const version = '0.14.52'
|