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.
- package/CHANGELOG.md +30 -0
- package/FrameRequest/package.json +6 -0
- package/_cjs/core/FrameRequest.js +29 -0
- package/_cjs/core/FrameRequest.js.map +1 -0
- package/_cjs/core/FrameSignature.js +7 -5
- package/_cjs/core/FrameSignature.js.map +1 -1
- package/_cjs/core/Siwe.js +3 -3
- package/_cjs/core/Siwe.js.map +1 -1
- package/_cjs/core/Transaction.js +8 -0
- package/_cjs/core/Transaction.js.map +1 -1
- package/_cjs/core/TransactionRequest.js +8 -3
- 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/index.js +3 -2
- package/_cjs/index.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/core/FrameRequest.js +53 -0
- package/_esm/core/FrameRequest.js.map +1 -0
- package/_esm/core/FrameSignature.js +7 -5
- package/_esm/core/FrameSignature.js.map +1 -1
- package/_esm/core/Siwe.js +3 -3
- package/_esm/core/Siwe.js.map +1 -1
- package/_esm/core/Transaction.js +8 -0
- package/_esm/core/Transaction.js.map +1 -1
- package/_esm/core/TransactionRequest.js +8 -3
- 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/index.js +13 -0
- package/_esm/index.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/core/FrameRequest.d.ts +44 -0
- package/_types/core/FrameRequest.d.ts.map +1 -0
- package/_types/core/FrameSignature.d.ts +4 -4
- package/_types/core/FrameSignature.d.ts.map +1 -1
- package/_types/core/Siwe.d.ts.map +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 +8 -5
- 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/core/internal/rpcSchemas/eth.d.ts +35 -0
- package/_types/core/internal/rpcSchemas/eth.d.ts.map +1 -1
- package/_types/index.d.ts +13 -0
- package/_types/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/core/FrameRequest.ts +72 -0
- package/core/FrameSignature.ts +15 -9
- package/core/Siwe.ts +21 -13
- package/core/Transaction.ts +17 -0
- package/core/TransactionRequest.ts +17 -7
- package/core/TxEnvelopeEip8141.ts +61 -13
- package/core/internal/frameNonce.ts +70 -0
- package/core/internal/rpcSchemas/eth.ts +39 -0
- package/index.ts +15 -0
- package/package.json +6 -1
- package/version.ts +1 -1
package/_types/version.d.ts
CHANGED
|
@@ -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
|
+
}
|
package/core/FrameSignature.ts
CHANGED
|
@@ -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 |
|
|
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 (
|
|
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
|
|
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
|
|
548
|
-
|
|
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 {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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,
|
package/core/Transaction.ts
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
|
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)
|
|
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
|
-
|
|
|
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
|
-
/**
|
|
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
|
+
}
|
|
@@ -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
|