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.
- package/CHANGELOG.md +53 -0
- package/dist/core/Frame.d.ts +23 -20
- package/dist/core/Frame.d.ts.map +1 -1
- package/dist/core/Frame.js +28 -23
- package/dist/core/Frame.js.map +1 -1
- package/dist/core/FrameReceipt.d.ts +23 -14
- package/dist/core/FrameReceipt.d.ts.map +1 -1
- package/dist/core/FrameReceipt.js +15 -11
- package/dist/core/FrameReceipt.js.map +1 -1
- package/dist/core/FrameRequest.d.ts +50 -0
- package/dist/core/FrameRequest.d.ts.map +1 -0
- package/dist/core/FrameRequest.js +59 -0
- package/dist/core/FrameRequest.js.map +1 -0
- package/dist/core/FrameSignature.d.ts +17 -16
- package/dist/core/FrameSignature.d.ts.map +1 -1
- package/dist/core/FrameSignature.js +38 -21
- package/dist/core/FrameSignature.js.map +1 -1
- package/dist/core/Siwe.d.ts.map +1 -1
- package/dist/core/Siwe.js +3 -3
- package/dist/core/Siwe.js.map +1 -1
- package/dist/core/Transaction.d.ts +7 -4
- package/dist/core/Transaction.d.ts.map +1 -1
- package/dist/core/Transaction.js +8 -0
- package/dist/core/Transaction.js.map +1 -1
- package/dist/core/TransactionRequest.d.ts +8 -5
- package/dist/core/TransactionRequest.d.ts.map +1 -1
- package/dist/core/TransactionRequest.js +11 -3
- package/dist/core/TransactionRequest.js.map +1 -1
- package/dist/core/TxEnvelope.d.ts +4 -2
- package/dist/core/TxEnvelope.d.ts.map +1 -1
- package/dist/core/TxEnvelope.js +11 -2
- package/dist/core/TxEnvelope.js.map +1 -1
- package/dist/core/TxEnvelopeEip8141.d.ts +22 -11
- package/dist/core/TxEnvelopeEip8141.d.ts.map +1 -1
- package/dist/core/TxEnvelopeEip8141.js +45 -16
- package/dist/core/TxEnvelopeEip8141.js.map +1 -1
- package/dist/core/internal/frameNonce.d.ts +10 -0
- package/dist/core/internal/frameNonce.d.ts.map +1 -0
- package/dist/core/internal/frameNonce.js +45 -0
- package/dist/core/internal/frameNonce.js.map +1 -0
- package/dist/core/internal/rpcSchemas/eth.d.ts +16 -4
- package/dist/core/internal/rpcSchemas/eth.d.ts.map +1 -1
- package/dist/index.d.ts +21 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -5
- package/dist/index.js.map +1 -1
- package/dist/zod/Block.d.ts +52 -37
- package/dist/zod/Block.d.ts.map +1 -1
- package/dist/zod/Frame.d.ts +14 -14
- package/dist/zod/Frame.js +5 -5
- package/dist/zod/Frame.js.map +1 -1
- package/dist/zod/FrameReceipt.d.ts +42 -2
- package/dist/zod/FrameReceipt.d.ts.map +1 -1
- package/dist/zod/FrameReceipt.js +11 -1
- package/dist/zod/FrameReceipt.js.map +1 -1
- package/dist/zod/FrameRequest.d.ts +40 -0
- package/dist/zod/FrameRequest.d.ts.map +1 -0
- package/dist/zod/FrameRequest.js +37 -0
- package/dist/zod/FrameRequest.js.map +1 -0
- package/dist/zod/FrameSignature.d.ts +21 -13
- package/dist/zod/FrameSignature.d.ts.map +1 -1
- package/dist/zod/FrameSignature.js +10 -7
- package/dist/zod/FrameSignature.js.map +1 -1
- package/dist/zod/RpcSchema.d.ts +470 -265
- package/dist/zod/RpcSchema.d.ts.map +1 -1
- package/dist/zod/Transaction.d.ts +104 -74
- package/dist/zod/Transaction.d.ts.map +1 -1
- package/dist/zod/Transaction.js +22 -15
- package/dist/zod/Transaction.js.map +1 -1
- package/dist/zod/TransactionEnvelope.d.ts +60 -34
- package/dist/zod/TransactionEnvelope.d.ts.map +1 -1
- package/dist/zod/TransactionReceipt.d.ts +42 -2
- package/dist/zod/TransactionReceipt.d.ts.map +1 -1
- package/dist/zod/TransactionRequest.d.ts +31 -21
- package/dist/zod/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/TransactionRequest.js +10 -4
- package/dist/zod/TransactionRequest.js.map +1 -1
- package/dist/zod/TxEnvelopeEip8141.d.ts +44 -25
- package/dist/zod/TxEnvelopeEip8141.d.ts.map +1 -1
- package/dist/zod/TxEnvelopeEip8141.js +2 -0
- package/dist/zod/TxEnvelopeEip8141.js.map +1 -1
- package/dist/zod/internal/Frame.d.ts +8 -0
- package/dist/zod/internal/Frame.d.ts.map +1 -1
- package/dist/zod/internal/Frame.js +29 -0
- package/dist/zod/internal/Frame.js.map +1 -1
- package/dist/zod/internal/rpcSchemas/Eth.d.ts +472 -267
- package/dist/zod/internal/rpcSchemas/Eth.d.ts.map +1 -1
- package/dist/zod/internal/rpcSchemas/Eth.js +9 -0
- package/dist/zod/internal/rpcSchemas/Eth.js.map +1 -1
- package/dist/zod/internal/rpcSchemas/Wallet.d.ts +51 -21
- package/dist/zod/internal/rpcSchemas/Wallet.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +68 -48
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +52 -37
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/z.d.ts +1 -0
- package/dist/zod/z.d.ts.map +1 -1
- package/dist/zod/z.js +1 -0
- package/dist/zod/z.js.map +1 -1
- package/package.json +11 -1
- package/src/core/Frame.ts +41 -33
- package/src/core/FrameReceipt.ts +30 -16
- package/src/core/FrameRequest.ts +78 -0
- package/src/core/FrameSignature.ts +64 -34
- package/src/core/Siwe.ts +21 -13
- package/src/core/Transaction.ts +52 -24
- package/src/core/TransactionRequest.ts +21 -7
- package/src/core/TxEnvelope.ts +16 -3
- package/src/core/TxEnvelopeEip8141.ts +69 -21
- package/src/core/_test/AbiConstructor.test.ts +2 -0
- package/src/core/_test/Frame.test-d.ts +16 -7
- package/src/core/_test/Frame.test.ts +52 -24
- package/src/core/_test/FrameReceipt.test.ts +15 -9
- package/src/core/_test/FrameRequest.test-d.ts +16 -0
- package/src/core/_test/FrameRequest.test.ts +41 -0
- package/src/core/_test/FrameRpc.test.ts +131 -0
- package/src/core/_test/FrameSignature.test-d.ts +20 -0
- package/src/core/_test/FrameSignature.test.ts +132 -11
- package/src/core/_test/RpcSchema.test-d.ts +45 -0
- package/src/core/_test/Siwe.test.ts +147 -0
- package/src/core/_test/TransactionEnvelope.test-d.ts +27 -0
- package/src/core/_test/TransactionEnvelope.test.ts +27 -1
- package/src/core/_test/TransactionReceipt.test.ts +32 -6
- package/src/core/_test/TransactionRequest.test-d.ts +10 -1
- package/src/core/_test/TransactionRequest.test.ts +31 -2
- package/src/core/_test/TxEnvelopeEip8141.test-d.ts +29 -2
- package/src/core/_test/TxEnvelopeEip8141.test.ts +548 -55
- package/src/core/_test/index.test.ts +1 -0
- package/src/core/internal/frameNonce.ts +61 -0
- package/src/core/internal/rpcSchemas/eth.ts +13 -4
- package/src/index.ts +22 -5
- package/src/version.ts +1 -1
- package/src/zod/Frame.ts +5 -5
- package/src/zod/FrameReceipt.ts +11 -1
- package/src/zod/FrameRequest.ts +46 -0
- package/src/zod/FrameSignature.ts +10 -7
- package/src/zod/Transaction.ts +46 -27
- package/src/zod/TransactionRequest.ts +33 -18
- package/src/zod/TxEnvelopeEip8141.ts +2 -0
- package/src/zod/_test/Frame.test.ts +13 -11
- package/src/zod/_test/FrameReceipt.test.ts +11 -4
- package/src/zod/_test/FrameRequest.test-d.ts +15 -0
- package/src/zod/_test/FrameRequest.test.ts +44 -0
- package/src/zod/_test/FrameSignature.test.ts +73 -9
- package/src/zod/_test/RpcSchema.test.ts +106 -0
- package/src/zod/_test/TransactionRequest.test.ts +61 -0
- package/src/zod/_test/TxEnvelopeEip8141.nonce.test.ts +174 -0
- package/src/zod/_test/TxEnvelopeEip8141.test-d.ts +29 -0
- package/src/zod/_test/TxEnvelopeEip8141.test.ts +3 -3
- package/src/zod/internal/Frame.ts +45 -0
- package/src/zod/internal/rpcSchemas/Eth.ts +20 -0
- package/src/zod/z.ts +1 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { encodeRlp, keccak256 } from 'ethers'
|
|
1
2
|
import {
|
|
2
3
|
Address,
|
|
3
4
|
Blobs,
|
|
@@ -245,7 +246,7 @@ describe('assert', () => {
|
|
|
245
246
|
expect(() =>
|
|
246
247
|
TxEnvelopeEip8141.assert({
|
|
247
248
|
chainId: 1,
|
|
248
|
-
frames: [{
|
|
249
|
+
frames: [{ executionGas: 2n ** 64n - 1n }, { stateGas: 1n }],
|
|
249
250
|
sender: accounts[0].address,
|
|
250
251
|
}),
|
|
251
252
|
).toThrowErrorMatchingInlineSnapshot(
|
|
@@ -257,7 +258,7 @@ describe('assert', () => {
|
|
|
257
258
|
expect(() =>
|
|
258
259
|
TxEnvelopeEip8141.assert({
|
|
259
260
|
chainId: 1,
|
|
260
|
-
frames: [{
|
|
261
|
+
frames: [{ executionGas: 16_777_216n }],
|
|
261
262
|
sender: accounts[0].address,
|
|
262
263
|
}),
|
|
263
264
|
).toThrowErrorMatchingInlineSnapshot(
|
|
@@ -354,8 +355,8 @@ describe('deserialize', () => {
|
|
|
354
355
|
"frames": [
|
|
355
356
|
{
|
|
356
357
|
"data": "0x",
|
|
358
|
+
"executionGas": 0n,
|
|
357
359
|
"flags": 0,
|
|
358
|
-
"gas": 0n,
|
|
359
360
|
"mode": 0,
|
|
360
361
|
"stateGas": 0n,
|
|
361
362
|
"value": 0n,
|
|
@@ -402,7 +403,7 @@ describe('deserialize', () => {
|
|
|
402
403
|
expect(() =>
|
|
403
404
|
TxEnvelopeEip8141.deserialize('0x06c0' as TxEnvelopeEip8141.Serialized),
|
|
404
405
|
).toThrowErrorMatchingInlineSnapshot(
|
|
405
|
-
`[TxEnvelopeEip8141.InvalidError: Expected seven transaction fields.]`,
|
|
406
|
+
`[TxEnvelopeEip8141.InvalidError: Expected seven or eight transaction fields.]`,
|
|
406
407
|
)
|
|
407
408
|
})
|
|
408
409
|
|
|
@@ -426,7 +427,7 @@ describe('deserialize', () => {
|
|
|
426
427
|
expect(() =>
|
|
427
428
|
TxEnvelopeEip8141.deserialize('0x06c180' as TxEnvelopeEip8141.Serialized),
|
|
428
429
|
).toThrowErrorMatchingInlineSnapshot(
|
|
429
|
-
`[TxEnvelopeEip8141.InvalidError: Expected seven transaction fields.]`,
|
|
430
|
+
`[TxEnvelopeEip8141.InvalidError: Expected seven or eight transaction fields.]`,
|
|
430
431
|
)
|
|
431
432
|
})
|
|
432
433
|
|
|
@@ -450,7 +451,7 @@ describe('from', () => {
|
|
|
450
451
|
expect(
|
|
451
452
|
TxEnvelopeEip8141.from({
|
|
452
453
|
chainId: 1,
|
|
453
|
-
frames: [{
|
|
454
|
+
frames: [{ executionGas: 50000n }],
|
|
454
455
|
sender: accounts[0].address,
|
|
455
456
|
}),
|
|
456
457
|
).toMatchInlineSnapshot(`
|
|
@@ -458,7 +459,7 @@ describe('from', () => {
|
|
|
458
459
|
"chainId": 1,
|
|
459
460
|
"frames": [
|
|
460
461
|
{
|
|
461
|
-
"
|
|
462
|
+
"executionGas": 50000n,
|
|
462
463
|
},
|
|
463
464
|
],
|
|
464
465
|
"sender": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
|
|
@@ -479,8 +480,8 @@ describe('from', () => {
|
|
|
479
480
|
"frames": [
|
|
480
481
|
{
|
|
481
482
|
"data": "0x",
|
|
483
|
+
"executionGas": 0n,
|
|
482
484
|
"flags": 0,
|
|
483
|
-
"gas": 0n,
|
|
484
485
|
"mode": 0,
|
|
485
486
|
"stateGas": 0n,
|
|
486
487
|
"value": 0n,
|
|
@@ -499,16 +500,43 @@ describe('from', () => {
|
|
|
499
500
|
})
|
|
500
501
|
|
|
501
502
|
describe('getSignPayload', () => {
|
|
503
|
+
test.each([
|
|
504
|
+
{ scheme: 'secp256k1', signature: '0x' },
|
|
505
|
+
{ scheme: 'p256', signature: '0x' },
|
|
506
|
+
] as const)(
|
|
507
|
+
'empty $scheme placeholders preserve the signing hash',
|
|
508
|
+
(entry) => {
|
|
509
|
+
const { signature: _, ...unsigned } = entry
|
|
510
|
+
const envelope = {
|
|
511
|
+
chainId: 1,
|
|
512
|
+
frames: [{ executionGas: 50_000n, mode: 'sender' }],
|
|
513
|
+
sender: accounts[0].address,
|
|
514
|
+
signatures: [unsigned],
|
|
515
|
+
} as const
|
|
516
|
+
const input = {
|
|
517
|
+
...envelope,
|
|
518
|
+
signatures: [entry],
|
|
519
|
+
} as const
|
|
520
|
+
expect(TxEnvelopeEip8141.getSignPayload(input)).toBe(
|
|
521
|
+
TxEnvelopeEip8141.getSignPayload(envelope),
|
|
522
|
+
)
|
|
523
|
+
expect(TxEnvelopeEip8141.serialize(input)).toBe(
|
|
524
|
+
TxEnvelopeEip8141.serialize(envelope),
|
|
525
|
+
)
|
|
526
|
+
expect(input.signatures[0].signature).toBe('0x')
|
|
527
|
+
},
|
|
528
|
+
)
|
|
529
|
+
|
|
502
530
|
test('omitted nested defaults preserve the signing hash and encoding', () => {
|
|
503
531
|
const envelope = TxEnvelopeEip8141.from({
|
|
504
532
|
chainId: 1,
|
|
505
|
-
frames: [{
|
|
533
|
+
frames: [{ executionGas: 50_000n, mode: 'sender' }],
|
|
506
534
|
sender: accounts[0].address,
|
|
507
535
|
signatures: [{ scheme: 'secp256k1' }],
|
|
508
536
|
})
|
|
509
537
|
const explicit = TxEnvelopeEip8141.from({
|
|
510
538
|
...envelope,
|
|
511
|
-
frames: [Frame.from({
|
|
539
|
+
frames: [Frame.from({ executionGas: 50_000n, mode: 'sender' })],
|
|
512
540
|
signatures: [FrameSignature.from({ scheme: 'secp256k1' })],
|
|
513
541
|
})
|
|
514
542
|
expect(TxEnvelopeEip8141.getSignPayload(envelope)).toBe(
|
|
@@ -630,26 +658,26 @@ describe('serialize', () => {
|
|
|
630
658
|
const request = TransactionRequest.toRpc({
|
|
631
659
|
frames: [
|
|
632
660
|
Frame.from({
|
|
661
|
+
executionGas: 50_000n,
|
|
633
662
|
flags: 'approveExecutionAndPayment',
|
|
634
|
-
gas: 50_000n,
|
|
635
663
|
mode: 'verify',
|
|
664
|
+
stateGas: 0n,
|
|
665
|
+
}),
|
|
666
|
+
Frame.from({
|
|
667
|
+
executionGas: 50_000n,
|
|
668
|
+
mode: 'sender',
|
|
669
|
+
stateGas: 0n,
|
|
670
|
+
to: target,
|
|
671
|
+
value: 1n,
|
|
636
672
|
}),
|
|
637
|
-
Frame.from({ gas: 50_000n, mode: 'sender', to: target, value: 1n }),
|
|
638
673
|
],
|
|
639
674
|
from: sender,
|
|
640
675
|
signatures: [{ scheme: 'secp256k1' }],
|
|
641
|
-
// Nethermind's simulation mapping requires an outer recipient before processing frames.
|
|
642
|
-
to: sender,
|
|
643
676
|
type: 'eip8141',
|
|
644
677
|
})
|
|
645
|
-
// TODO: remove once migrated to reth or anvil.
|
|
646
|
-
// Nethermind validates signatures when filling gas, before the transaction can be signed.
|
|
647
|
-
await expect(
|
|
648
|
-
rpc.request({ method: 'eth_fillTransaction', params: [request] }),
|
|
649
|
-
).rejects.toThrow('frame transaction signature has the wrong length')
|
|
650
678
|
const { tx } = await rpc.request({
|
|
651
679
|
method: 'eth_fillTransaction',
|
|
652
|
-
params: [
|
|
680
|
+
params: [request],
|
|
653
681
|
})
|
|
654
682
|
expect(tx.type).toBe('0x6')
|
|
655
683
|
if (tx.type !== '0x6' || !tx.frames)
|
|
@@ -658,7 +686,6 @@ describe('serialize', () => {
|
|
|
658
686
|
expect(tx.nonce).toBe(nonce)
|
|
659
687
|
expect(tx.frames).toEqual(request.frames)
|
|
660
688
|
expect(tx.signatures).toEqual(request.signatures)
|
|
661
|
-
expect(Hex.toBigInt(tx.gas)).toBe(100_000n)
|
|
662
689
|
expect(Hex.toBigInt(tx.maxFeePerGas)).toBeGreaterThan(0n)
|
|
663
690
|
expect(Hex.toBigInt(tx.maxPriorityFeePerGas)).toBeGreaterThanOrEqual(0n)
|
|
664
691
|
expect(Hex.toBigInt(tx.maxFeePerGas)).toBeGreaterThanOrEqual(
|
|
@@ -728,14 +755,19 @@ describe('serialize', () => {
|
|
|
728
755
|
chainId: 8141,
|
|
729
756
|
frames: [
|
|
730
757
|
Frame.from({
|
|
758
|
+
executionGas: 50_000n,
|
|
731
759
|
flags: 'approveExecutionAndPayment',
|
|
732
|
-
gas: 50_000n,
|
|
733
760
|
mode: 'verify',
|
|
734
761
|
}),
|
|
735
|
-
Frame.from({
|
|
762
|
+
Frame.from({
|
|
763
|
+
executionGas: 50_000n,
|
|
764
|
+
mode: 'sender',
|
|
765
|
+
to: target,
|
|
766
|
+
value: 1n,
|
|
767
|
+
}),
|
|
736
768
|
Frame.from({
|
|
737
769
|
data: '0xdeadbeef',
|
|
738
|
-
|
|
770
|
+
executionGas: 50_000n,
|
|
739
771
|
mode: 'sender',
|
|
740
772
|
to: '0x0000000000000000000000000000000000000004',
|
|
741
773
|
}),
|
|
@@ -756,14 +788,12 @@ describe('serialize', () => {
|
|
|
756
788
|
})
|
|
757
789
|
const request = TransactionRequest.toRpc({
|
|
758
790
|
...TransactionEnvelope.toTransactionRequest(signed),
|
|
759
|
-
// Nethermind's simulation mapping requires an outer recipient before processing frames.
|
|
760
|
-
to: sender,
|
|
761
791
|
})
|
|
762
792
|
const result = await rpc.request({
|
|
763
793
|
method,
|
|
764
794
|
params: [request, 'latest'],
|
|
765
795
|
})
|
|
766
|
-
//
|
|
796
|
+
// Transaction-level calls do not expose individual frame return data.
|
|
767
797
|
if (method === 'eth_call') expect(result).toBe('0x')
|
|
768
798
|
else {
|
|
769
799
|
// Estimation includes all signed frame budgets plus intrinsic gas.
|
|
@@ -774,11 +804,14 @@ describe('serialize', () => {
|
|
|
774
804
|
rpc.request({
|
|
775
805
|
method,
|
|
776
806
|
params: [
|
|
777
|
-
{
|
|
807
|
+
{
|
|
808
|
+
...request,
|
|
809
|
+
frames: [{ ...request.frames![0]!, mode: '0xff' }],
|
|
810
|
+
},
|
|
778
811
|
'latest',
|
|
779
812
|
],
|
|
780
813
|
}),
|
|
781
|
-
).rejects.toThrow('
|
|
814
|
+
).rejects.toThrow('Invalid params')
|
|
782
815
|
expect(
|
|
783
816
|
await rpc.request({
|
|
784
817
|
method: 'eth_getBalance',
|
|
@@ -806,11 +839,16 @@ describe('serialize', () => {
|
|
|
806
839
|
chainId: 8141,
|
|
807
840
|
frames: [
|
|
808
841
|
Frame.from({
|
|
842
|
+
executionGas: 50_000n,
|
|
809
843
|
flags: 'approveExecutionAndPayment',
|
|
810
|
-
gas: 50_000n,
|
|
811
844
|
mode: 'verify',
|
|
812
845
|
}),
|
|
813
|
-
Frame.from({
|
|
846
|
+
Frame.from({
|
|
847
|
+
executionGas: 50_000n,
|
|
848
|
+
mode: 'sender',
|
|
849
|
+
to: target,
|
|
850
|
+
value: 1n,
|
|
851
|
+
}),
|
|
814
852
|
],
|
|
815
853
|
maxFeePerGas: 10_000_000_000n,
|
|
816
854
|
maxPriorityFeePerGas: 1_000_000_000n,
|
|
@@ -840,10 +878,12 @@ describe('serialize', () => {
|
|
|
840
878
|
})
|
|
841
879
|
expect(hash).toBe(TransactionEnvelope.hash(signed))
|
|
842
880
|
expect(
|
|
843
|
-
|
|
844
|
-
|
|
881
|
+
TransactionEnvelope.serialize(
|
|
882
|
+
TransactionRequest.toEnvelope(
|
|
883
|
+
TransactionEnvelope.toTransactionRequest(signed),
|
|
884
|
+
),
|
|
845
885
|
),
|
|
846
|
-
).
|
|
886
|
+
).toBe(serialized)
|
|
847
887
|
expect(TxEnvelopeEip8141.toRpc(signed)).toMatchObject(
|
|
848
888
|
TransactionRequest.toRpc(
|
|
849
889
|
TransactionEnvelope.toTransactionRequest(signed),
|
|
@@ -939,18 +979,18 @@ describe('serialize', () => {
|
|
|
939
979
|
chainId: 8141,
|
|
940
980
|
frames: [
|
|
941
981
|
Frame.from({
|
|
982
|
+
executionGas: 20_000n,
|
|
942
983
|
flags: 'approveExecution',
|
|
943
|
-
gas: 50_000n,
|
|
944
984
|
mode: 'verify',
|
|
945
985
|
}),
|
|
946
986
|
Frame.from({
|
|
987
|
+
executionGas: 20_000n,
|
|
947
988
|
flags: 'approvePayment',
|
|
948
|
-
gas: 50_000n,
|
|
949
989
|
mode: 'verify',
|
|
950
990
|
to: accounts[1].address,
|
|
951
991
|
}),
|
|
952
992
|
Frame.from({
|
|
953
|
-
|
|
993
|
+
executionGas: 20_000n,
|
|
954
994
|
mode: 'sender',
|
|
955
995
|
to: accounts[1].address,
|
|
956
996
|
value: 1n,
|
|
@@ -1045,25 +1085,25 @@ describe('serialize', () => {
|
|
|
1045
1085
|
chainId: 8141,
|
|
1046
1086
|
frames: [
|
|
1047
1087
|
Frame.from({
|
|
1088
|
+
executionGas: 50_000n,
|
|
1048
1089
|
flags: 'approveExecutionAndPayment',
|
|
1049
|
-
gas: 50_000n,
|
|
1050
1090
|
mode: 'verify',
|
|
1051
1091
|
}),
|
|
1052
1092
|
Frame.from({
|
|
1093
|
+
executionGas: 50_000n,
|
|
1053
1094
|
flags: 'atomicBatch',
|
|
1054
|
-
gas: 50_000n,
|
|
1055
1095
|
mode: 'sender',
|
|
1056
1096
|
to: accounts[1].address,
|
|
1057
1097
|
value: 1n,
|
|
1058
1098
|
}),
|
|
1059
1099
|
Frame.from({
|
|
1100
|
+
executionGas: 50_000n,
|
|
1060
1101
|
flags: 'atomicBatch',
|
|
1061
|
-
gas: 50_000n,
|
|
1062
1102
|
mode: 'sender',
|
|
1063
1103
|
to: '0x0000000000000000000000000000000000001000',
|
|
1064
1104
|
}),
|
|
1065
1105
|
Frame.from({
|
|
1066
|
-
|
|
1106
|
+
executionGas: 50_000n,
|
|
1067
1107
|
mode: 'sender',
|
|
1068
1108
|
to: accounts[1].address,
|
|
1069
1109
|
value: 2n,
|
|
@@ -1131,8 +1171,8 @@ describe('serialize', () => {
|
|
|
1131
1171
|
chainId: 8141,
|
|
1132
1172
|
frames: [
|
|
1133
1173
|
Frame.from({
|
|
1174
|
+
executionGas: 50_000n,
|
|
1134
1175
|
flags: 'approveExecutionAndPayment',
|
|
1135
|
-
gas: 50_000n,
|
|
1136
1176
|
mode: 'verify',
|
|
1137
1177
|
}),
|
|
1138
1178
|
],
|
|
@@ -1166,7 +1206,7 @@ describe('serialize', () => {
|
|
|
1166
1206
|
],
|
|
1167
1207
|
}),
|
|
1168
1208
|
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
1169
|
-
`[RpcResponse.InvalidInputError:
|
|
1209
|
+
`[RpcResponse.InvalidInputError: EIP-8141 public mempool policy: validation prefix execution failed]`,
|
|
1170
1210
|
)
|
|
1171
1211
|
})
|
|
1172
1212
|
|
|
@@ -1176,13 +1216,13 @@ describe('serialize', () => {
|
|
|
1176
1216
|
frames: [
|
|
1177
1217
|
Frame.from({
|
|
1178
1218
|
data: '0x0000000000000001',
|
|
1179
|
-
|
|
1219
|
+
executionGas: 20_000n,
|
|
1180
1220
|
mode: 'verify',
|
|
1181
1221
|
to: '0x0000000000000000000000000000000000008141',
|
|
1182
1222
|
}),
|
|
1183
1223
|
Frame.from({
|
|
1224
|
+
executionGas: 20_000n,
|
|
1184
1225
|
flags: 'approveExecutionAndPayment',
|
|
1185
|
-
gas: 50_000n,
|
|
1186
1226
|
mode: 'verify',
|
|
1187
1227
|
}),
|
|
1188
1228
|
],
|
|
@@ -1213,7 +1253,7 @@ describe('serialize', () => {
|
|
|
1213
1253
|
params: [TxEnvelopeEip8141.serialize(signed)],
|
|
1214
1254
|
}),
|
|
1215
1255
|
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
1216
|
-
`[RpcResponse.InvalidInputError:
|
|
1256
|
+
`[RpcResponse.InvalidInputError: EIP-8141 public mempool policy: validation prefix execution failed]`,
|
|
1217
1257
|
)
|
|
1218
1258
|
})
|
|
1219
1259
|
|
|
@@ -1226,12 +1266,12 @@ describe('serialize', () => {
|
|
|
1226
1266
|
chainId: 8141,
|
|
1227
1267
|
frames: [
|
|
1228
1268
|
Frame.from({
|
|
1269
|
+
executionGas: 50_000n,
|
|
1229
1270
|
flags: 'approveExecutionAndPayment',
|
|
1230
|
-
gas: 50_000n,
|
|
1231
1271
|
mode: 'verify',
|
|
1232
1272
|
}),
|
|
1233
1273
|
Frame.from({
|
|
1234
|
-
|
|
1274
|
+
executionGas: 50_000n,
|
|
1235
1275
|
mode: 'sender',
|
|
1236
1276
|
to: accounts[1].address,
|
|
1237
1277
|
value: 1n,
|
|
@@ -1613,7 +1653,7 @@ describe('validate', () => {
|
|
|
1613
1653
|
expect(
|
|
1614
1654
|
TxEnvelopeEip8141.validate({
|
|
1615
1655
|
chainId: 1,
|
|
1616
|
-
frames: [{
|
|
1656
|
+
frames: [{ executionGas: 2n ** 64n - 1n }, { stateGas: 1n }],
|
|
1617
1657
|
sender: accounts[0].address,
|
|
1618
1658
|
}),
|
|
1619
1659
|
).toBe(false)
|
|
@@ -1623,7 +1663,7 @@ describe('validate', () => {
|
|
|
1623
1663
|
expect(
|
|
1624
1664
|
TxEnvelopeEip8141.validate({
|
|
1625
1665
|
chainId: 1,
|
|
1626
|
-
frames: [{
|
|
1666
|
+
frames: [{ executionGas: 16_777_216n }],
|
|
1627
1667
|
sender: accounts[0].address,
|
|
1628
1668
|
}),
|
|
1629
1669
|
).toBe(false)
|
|
@@ -1706,7 +1746,7 @@ describe('validate', () => {
|
|
|
1706
1746
|
|
|
1707
1747
|
const frame = {
|
|
1708
1748
|
data: '0x0000000000000001',
|
|
1709
|
-
|
|
1749
|
+
executionGas: 5000n,
|
|
1710
1750
|
mode: 'verify',
|
|
1711
1751
|
to: '0x0000000000000000000000000000000000008141',
|
|
1712
1752
|
} as const
|
|
@@ -1738,10 +1778,10 @@ describe('toRpc', () => {
|
|
|
1738
1778
|
frames: [
|
|
1739
1779
|
{
|
|
1740
1780
|
data: '0x',
|
|
1741
|
-
|
|
1742
|
-
flags:
|
|
1743
|
-
mode:
|
|
1744
|
-
|
|
1781
|
+
executionGas: '0x0',
|
|
1782
|
+
flags: '0x0',
|
|
1783
|
+
mode: '0x0',
|
|
1784
|
+
stateGas: '0x0',
|
|
1745
1785
|
value: '0x0',
|
|
1746
1786
|
},
|
|
1747
1787
|
],
|
|
@@ -1773,3 +1813,456 @@ describe('fromRpc', () => {
|
|
|
1773
1813
|
)
|
|
1774
1814
|
})
|
|
1775
1815
|
})
|
|
1816
|
+
|
|
1817
|
+
describe('keyed nonces', () => {
|
|
1818
|
+
const sender = '0x1111111111111111111111111111111111111111'
|
|
1819
|
+
const envelope = TxEnvelopeEip8141.from({
|
|
1820
|
+
chainId: 1,
|
|
1821
|
+
frames: [{ mode: 'sender' }],
|
|
1822
|
+
sender,
|
|
1823
|
+
signatures: [{ scheme: 'arbitrary', signature: '0xdeadbeef' }],
|
|
1824
|
+
})
|
|
1825
|
+
|
|
1826
|
+
// Generated independently with ethers 6.17 from the literal EIP-8250 RLP layout.
|
|
1827
|
+
const vectors = [
|
|
1828
|
+
{
|
|
1829
|
+
nonceKeys: undefined,
|
|
1830
|
+
nonce: 0n,
|
|
1831
|
+
serialized:
|
|
1832
|
+
'0x06f00180941111111111111111111111111111111111111111c9c8028080c280808080c9c880808084deadbeefc3808080c0',
|
|
1833
|
+
hash: '0xa14c90dee4c545369e201e4975ee2ef415357df20a0a183d1be9f602bac9a47d',
|
|
1834
|
+
signHash:
|
|
1835
|
+
'0x5682abf965767351637964eed1fe3f775ab45de426def7da68736cf2ae0a43b5',
|
|
1836
|
+
},
|
|
1837
|
+
{
|
|
1838
|
+
nonceKeys: [0n],
|
|
1839
|
+
nonce: 0n,
|
|
1840
|
+
serialized:
|
|
1841
|
+
'0x06f201c18080941111111111111111111111111111111111111111c9c8028080c280808080c9c880808084deadbeefc3808080c0',
|
|
1842
|
+
hash: '0x069a1528377db98ed06429aab0dad8043315859ac454ce3e1f5a250e365d072d',
|
|
1843
|
+
signHash:
|
|
1844
|
+
'0xd573654ae8d57432b3856b0078fb98f2f3e4d3f73a023d7ba8a952a16d0f4726',
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
nonceKeys: [1n, 128n, 2n ** 256n - 1n],
|
|
1848
|
+
nonce: 2n ** 64n - 2n,
|
|
1849
|
+
serialized:
|
|
1850
|
+
'0x06f85d01e4018180a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff88fffffffffffffffe941111111111111111111111111111111111111111c9c8028080c280808080c9c880808084deadbeefc3808080c0',
|
|
1851
|
+
hash: '0x620915f063bdd017a09ae856d0043d064568b3455aba741d7ebc748731fc503f',
|
|
1852
|
+
signHash:
|
|
1853
|
+
'0x3d28f5f76f353611385be1512c2832220686b27cad3c53d5b07941de1515319b',
|
|
1854
|
+
},
|
|
1855
|
+
] as const
|
|
1856
|
+
|
|
1857
|
+
describe('serialize', () => {
|
|
1858
|
+
test.each(vectors)(
|
|
1859
|
+
'independent encoding and signing vector %#',
|
|
1860
|
+
(vector) => {
|
|
1861
|
+
const value = {
|
|
1862
|
+
...envelope,
|
|
1863
|
+
nonce: vector.nonce,
|
|
1864
|
+
nonceKeys: vector.nonceKeys,
|
|
1865
|
+
}
|
|
1866
|
+
expect(TxEnvelopeEip8141.serialize(value)).toBe(vector.serialized)
|
|
1867
|
+
expect(TxEnvelopeEip8141.hash(value)).toBe(vector.hash)
|
|
1868
|
+
expect(TxEnvelopeEip8141.getSignPayload(value)).toBe(vector.signHash)
|
|
1869
|
+
expect(TransactionEnvelope.serialize(value)).toBe(vector.serialized)
|
|
1870
|
+
expect(TransactionEnvelope.getSignPayload(value)).toBe(vector.signHash)
|
|
1871
|
+
const decoded = TransactionEnvelope.deserialize(vector.serialized)
|
|
1872
|
+
expect(TxEnvelopeEip8141.serialize(decoded)).toBe(vector.serialized)
|
|
1873
|
+
expect(decoded.nonceKeys).toEqual(vector.nonceKeys)
|
|
1874
|
+
expect(decoded.nonce).toBe(vector.nonce)
|
|
1875
|
+
expect(TxEnvelopeEip8141.getSignPayload(decoded)).toBe(vector.signHash)
|
|
1876
|
+
},
|
|
1877
|
+
)
|
|
1878
|
+
|
|
1879
|
+
test('matches independent literal RLP layout', () => {
|
|
1880
|
+
const body = [
|
|
1881
|
+
'0x01',
|
|
1882
|
+
['0x01', '0x8180'],
|
|
1883
|
+
'0x05',
|
|
1884
|
+
sender,
|
|
1885
|
+
[['0x02', '0x', '0x', ['0x', '0x'], '0x', '0x']],
|
|
1886
|
+
[['0x', '0x', '0x', '0xdeadbeef']],
|
|
1887
|
+
['0x', '0x', '0x'],
|
|
1888
|
+
[],
|
|
1889
|
+
]
|
|
1890
|
+
const serialized = `0x06${encodeRlp(body).slice(2)}`
|
|
1891
|
+
const value = { ...envelope, nonceKeys: [1n, 33152n], nonce: 5n }
|
|
1892
|
+
expect(TxEnvelopeEip8141.serialize(value)).toBe(serialized)
|
|
1893
|
+
expect(TxEnvelopeEip8141.hash(value)).toBe(keccak256(serialized))
|
|
1894
|
+
})
|
|
1895
|
+
|
|
1896
|
+
test('keyed PeerDAS wrapper preserves keys and excludes sidecars from hashes', () => {
|
|
1897
|
+
const commitment = `0x${'00'.repeat(48)}` as const
|
|
1898
|
+
const value = {
|
|
1899
|
+
...envelope,
|
|
1900
|
+
nonceKeys: [1n],
|
|
1901
|
+
blobVersionedHashes: [Blobs.commitmentToVersionedHash(commitment)],
|
|
1902
|
+
sidecars: {
|
|
1903
|
+
blobs: [`0x${'00'.repeat(Blobs.bytesPerBlob)}` as const],
|
|
1904
|
+
commitments: [commitment],
|
|
1905
|
+
cellProofs: Array.from({ length: 128 }, () => commitment),
|
|
1906
|
+
},
|
|
1907
|
+
}
|
|
1908
|
+
const serialized = TxEnvelopeEip8141.serialize(value)
|
|
1909
|
+
const decoded = TxEnvelopeEip8141.deserialize(serialized)
|
|
1910
|
+
expect(decoded.nonceKeys).toMatchInlineSnapshot(`
|
|
1911
|
+
[
|
|
1912
|
+
1n,
|
|
1913
|
+
]
|
|
1914
|
+
`)
|
|
1915
|
+
expect(TxEnvelopeEip8141.serialize(decoded)).toBe(serialized)
|
|
1916
|
+
expect(TxEnvelopeEip8141.hash(value)).toBe(
|
|
1917
|
+
TxEnvelopeEip8141.hash({ ...value, sidecars: undefined }),
|
|
1918
|
+
)
|
|
1919
|
+
expect(TxEnvelopeEip8141.getSignPayload(value)).toBe(
|
|
1920
|
+
TxEnvelopeEip8141.getSignPayload({ ...value, sidecars: undefined }),
|
|
1921
|
+
)
|
|
1922
|
+
})
|
|
1923
|
+
})
|
|
1924
|
+
|
|
1925
|
+
describe('assert', () => {
|
|
1926
|
+
test.each([
|
|
1927
|
+
[],
|
|
1928
|
+
[0n, 1n],
|
|
1929
|
+
[1n, 1n],
|
|
1930
|
+
[2n, 1n],
|
|
1931
|
+
[-1n],
|
|
1932
|
+
[2n ** 256n],
|
|
1933
|
+
Array.from({ length: 17 }, (_, i) => BigInt(i + 1)),
|
|
1934
|
+
])('rejects invalid keys %#', (...nonceKeys) => {
|
|
1935
|
+
expect(TxEnvelopeEip8141.validate({ ...envelope, nonceKeys })).toBe(false)
|
|
1936
|
+
expect(() =>
|
|
1937
|
+
TxEnvelopeEip8141.serialize({ ...envelope, nonceKeys }),
|
|
1938
|
+
).toThrow()
|
|
1939
|
+
})
|
|
1940
|
+
|
|
1941
|
+
test('accepts 16 keys and the maximum key', () => {
|
|
1942
|
+
expect(
|
|
1943
|
+
TxEnvelopeEip8141.validate({
|
|
1944
|
+
...envelope,
|
|
1945
|
+
nonceKeys: Array.from({ length: 16 }, (_, i) => BigInt(i + 1)),
|
|
1946
|
+
}),
|
|
1947
|
+
).toBe(true)
|
|
1948
|
+
expect(
|
|
1949
|
+
TxEnvelopeEip8141.validate({
|
|
1950
|
+
...envelope,
|
|
1951
|
+
nonceKeys: [2n ** 256n - 1n],
|
|
1952
|
+
}),
|
|
1953
|
+
).toBe(true)
|
|
1954
|
+
})
|
|
1955
|
+
|
|
1956
|
+
test.each([-1n, 2n ** 64n - 1n, 2n ** 64n])(
|
|
1957
|
+
'rejects invalid shared sequence %s',
|
|
1958
|
+
(nonce) => {
|
|
1959
|
+
expect(() =>
|
|
1960
|
+
TxEnvelopeEip8141.assert({ ...envelope, nonceKeys: [1n], nonce }),
|
|
1961
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
1962
|
+
`[FrameNonce.InvalidError: Nonce must be less than 2^64 - 1 and nonnegative.]`,
|
|
1963
|
+
)
|
|
1964
|
+
},
|
|
1965
|
+
)
|
|
1966
|
+
|
|
1967
|
+
test('rejects a numeric key or null list', () => {
|
|
1968
|
+
// @ts-expect-error invalid runtime input
|
|
1969
|
+
expect(TxEnvelopeEip8141.validate({ ...envelope, nonceKeys: [1] })).toBe(
|
|
1970
|
+
false,
|
|
1971
|
+
)
|
|
1972
|
+
// @ts-expect-error invalid runtime input
|
|
1973
|
+
expect(TxEnvelopeEip8141.validate({ ...envelope, nonceKeys: null })).toBe(
|
|
1974
|
+
false,
|
|
1975
|
+
)
|
|
1976
|
+
})
|
|
1977
|
+
|
|
1978
|
+
test('charges encoded nonce calldata against the transaction gas cap', () => {
|
|
1979
|
+
const value = {
|
|
1980
|
+
...envelope,
|
|
1981
|
+
signatures: [],
|
|
1982
|
+
frames: [{ executionGas: 16_764_741n }],
|
|
1983
|
+
}
|
|
1984
|
+
expect(TxEnvelopeEip8141.validate(value)).toBe(true)
|
|
1985
|
+
expect(TxEnvelopeEip8141.validate({ ...value, nonceKeys: [0n] })).toBe(
|
|
1986
|
+
false,
|
|
1987
|
+
)
|
|
1988
|
+
// rlp([0]) || rlp(0) = c1 80 80, three nonzero bytes at 16 gas each.
|
|
1989
|
+
expect(
|
|
1990
|
+
TxEnvelopeEip8141.validate({
|
|
1991
|
+
...value,
|
|
1992
|
+
nonceKeys: [0n],
|
|
1993
|
+
frames: [{ executionGas: 16_764_693n }],
|
|
1994
|
+
}),
|
|
1995
|
+
).toBe(true)
|
|
1996
|
+
})
|
|
1997
|
+
})
|
|
1998
|
+
|
|
1999
|
+
describe('deserialize', () => {
|
|
2000
|
+
test.each([
|
|
2001
|
+
['0x', '0x'],
|
|
2002
|
+
[[], '0x'],
|
|
2003
|
+
[['0x00'], '0x'],
|
|
2004
|
+
[['0x0001'], '0x'],
|
|
2005
|
+
[[['0x01']], '0x'],
|
|
2006
|
+
[['0x01'], '0x00'],
|
|
2007
|
+
[['0x01'], '0x0001'],
|
|
2008
|
+
[['0x01'], []],
|
|
2009
|
+
[['0x01', '0x01'], '0x'],
|
|
2010
|
+
[['0x02', '0x01'], '0x'],
|
|
2011
|
+
[['0x', '0x01'], '0x'],
|
|
2012
|
+
[[`0x01${'00'.repeat(32)}`], '0x'],
|
|
2013
|
+
[['0x01'], '0x010000000000000000'],
|
|
2014
|
+
])('rejects malformed nonce fields %#', (keys, nonce) => {
|
|
2015
|
+
const serialized =
|
|
2016
|
+
`0x06${encodeRlp(['0x01', keys, nonce, sender, [['0x02', '0x', '0x', ['0x', '0x'], '0x', '0x']], [], ['0x', '0x', '0x'], []]).slice(2)}` as const
|
|
2017
|
+
expect(() => TxEnvelopeEip8141.deserialize(serialized)).toThrow()
|
|
2018
|
+
})
|
|
2019
|
+
|
|
2020
|
+
test('rejects nonminimal RLP string and list lengths', () => {
|
|
2021
|
+
const canonical = vectors[1].serialized
|
|
2022
|
+
expect(() =>
|
|
2023
|
+
TxEnvelopeEip8141.deserialize(
|
|
2024
|
+
canonical.replace(
|
|
2025
|
+
'f201c180',
|
|
2026
|
+
'f301c28100',
|
|
2027
|
+
) as TxEnvelopeEip8141.Serialized,
|
|
2028
|
+
),
|
|
2029
|
+
).toThrow()
|
|
2030
|
+
expect(() =>
|
|
2031
|
+
TxEnvelopeEip8141.deserialize(
|
|
2032
|
+
canonical.replace(
|
|
2033
|
+
'f201c180',
|
|
2034
|
+
'f301f80180',
|
|
2035
|
+
) as TxEnvelopeEip8141.Serialized,
|
|
2036
|
+
),
|
|
2037
|
+
).toThrow()
|
|
2038
|
+
expect(() =>
|
|
2039
|
+
TxEnvelopeEip8141.deserialize(
|
|
2040
|
+
canonical.replace('f201', 'f83201') as TxEnvelopeEip8141.Serialized,
|
|
2041
|
+
),
|
|
2042
|
+
).toThrow()
|
|
2043
|
+
})
|
|
2044
|
+
})
|
|
2045
|
+
|
|
2046
|
+
describe('getSignPayload', () => {
|
|
2047
|
+
test('signatures bind the complete key set and sequence', () => {
|
|
2048
|
+
const value = {
|
|
2049
|
+
...envelope,
|
|
2050
|
+
nonceKeys: [1n, 2n],
|
|
2051
|
+
nonce: 0n,
|
|
2052
|
+
signatures: [{ scheme: 'secp256k1' } as const],
|
|
2053
|
+
}
|
|
2054
|
+
const privateKey =
|
|
2055
|
+
'0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'
|
|
2056
|
+
const publicKey = Secp256k1.getPublicKey({ privateKey })
|
|
2057
|
+
const signature = Secp256k1.sign({
|
|
2058
|
+
privateKey,
|
|
2059
|
+
payload: TxEnvelopeEip8141.getSignPayload(value),
|
|
2060
|
+
})
|
|
2061
|
+
const signed = {
|
|
2062
|
+
...value,
|
|
2063
|
+
signatures: [{ scheme: 'secp256k1', signature } as const],
|
|
2064
|
+
}
|
|
2065
|
+
expect(
|
|
2066
|
+
Secp256k1.verify({
|
|
2067
|
+
publicKey,
|
|
2068
|
+
signature,
|
|
2069
|
+
payload: TxEnvelopeEip8141.getSignPayload(signed),
|
|
2070
|
+
}),
|
|
2071
|
+
).toBe(true)
|
|
2072
|
+
expect(
|
|
2073
|
+
Secp256k1.verify({
|
|
2074
|
+
publicKey,
|
|
2075
|
+
signature,
|
|
2076
|
+
payload: TxEnvelopeEip8141.getSignPayload({
|
|
2077
|
+
...signed,
|
|
2078
|
+
nonceKeys: [1n, 3n],
|
|
2079
|
+
}),
|
|
2080
|
+
}),
|
|
2081
|
+
).toBe(false)
|
|
2082
|
+
expect(
|
|
2083
|
+
Secp256k1.verify({
|
|
2084
|
+
publicKey,
|
|
2085
|
+
signature,
|
|
2086
|
+
payload: TxEnvelopeEip8141.getSignPayload({ ...signed, nonce: 1n }),
|
|
2087
|
+
}),
|
|
2088
|
+
).toBe(false)
|
|
2089
|
+
})
|
|
2090
|
+
|
|
2091
|
+
test('commits to keys and shared sequence without mutating signatures', () => {
|
|
2092
|
+
const value = { ...envelope, nonceKeys: [1n, 2n], nonce: 3n }
|
|
2093
|
+
const hash = TxEnvelopeEip8141.getSignPayload(value)
|
|
2094
|
+
expect(
|
|
2095
|
+
TxEnvelopeEip8141.getSignPayload({ ...value, nonceKeys: [1n, 3n] }),
|
|
2096
|
+
).not.toBe(hash)
|
|
2097
|
+
expect(
|
|
2098
|
+
TxEnvelopeEip8141.getSignPayload({ ...value, nonce: 4n }),
|
|
2099
|
+
).not.toBe(hash)
|
|
2100
|
+
expect(
|
|
2101
|
+
TxEnvelopeEip8141.getSignPayload({ ...value, nonceKeys: [1n] }),
|
|
2102
|
+
).not.toBe(hash)
|
|
2103
|
+
expect(
|
|
2104
|
+
TxEnvelopeEip8141.getSignPayload({
|
|
2105
|
+
...value,
|
|
2106
|
+
signatures: [{ scheme: 'arbitrary', signature: '0xabcdef' }],
|
|
2107
|
+
}),
|
|
2108
|
+
).toBe(hash)
|
|
2109
|
+
expect(value.signatures[0].signature).toBe('0xdeadbeef')
|
|
2110
|
+
})
|
|
2111
|
+
|
|
2112
|
+
test('retains explicit-message signatures', () => {
|
|
2113
|
+
const value = {
|
|
2114
|
+
...envelope,
|
|
2115
|
+
nonceKeys: [1n],
|
|
2116
|
+
signatures: [
|
|
2117
|
+
{
|
|
2118
|
+
scheme: 'arbitrary',
|
|
2119
|
+
payload: `0x${'11'.repeat(32)}`,
|
|
2120
|
+
signature: '0xdeadbeef',
|
|
2121
|
+
} as const,
|
|
2122
|
+
],
|
|
2123
|
+
}
|
|
2124
|
+
expect(TxEnvelopeEip8141.getSignPayload(value)).toBe(
|
|
2125
|
+
TxEnvelopeEip8141.hash(value),
|
|
2126
|
+
)
|
|
2127
|
+
expect(
|
|
2128
|
+
TxEnvelopeEip8141.getSignPayload({
|
|
2129
|
+
...value,
|
|
2130
|
+
signatures: [{ ...value.signatures[0]!, signature: '0xabcdef' }],
|
|
2131
|
+
}),
|
|
2132
|
+
).not.toBe(TxEnvelopeEip8141.getSignPayload(value))
|
|
2133
|
+
})
|
|
2134
|
+
})
|
|
2135
|
+
|
|
2136
|
+
describe('toRpc', () => {
|
|
2137
|
+
test.each(vectors)(
|
|
2138
|
+
'preserves absent or explicit nonce keys %#',
|
|
2139
|
+
(vector) => {
|
|
2140
|
+
const value = {
|
|
2141
|
+
...envelope,
|
|
2142
|
+
nonceKeys: vector.nonceKeys,
|
|
2143
|
+
nonce: vector.nonce,
|
|
2144
|
+
}
|
|
2145
|
+
const rpc = TxEnvelopeEip8141.toRpc(value)
|
|
2146
|
+
expect('nonceSeq' in rpc).toBe(false)
|
|
2147
|
+
expect('nonceKeys' in rpc).toBe(vector.nonceKeys !== undefined)
|
|
2148
|
+
expect(rpc.nonceKeys).toEqual(
|
|
2149
|
+
vector.nonceKeys?.map((key) => Hex.fromNumber(key)),
|
|
2150
|
+
)
|
|
2151
|
+
expect(rpc.nonce).toBe(Hex.fromNumber(vector.nonce))
|
|
2152
|
+
expect(
|
|
2153
|
+
TxEnvelopeEip8141.serialize(TxEnvelopeEip8141.fromRpc(rpc)),
|
|
2154
|
+
).toBe(vector.serialized)
|
|
2155
|
+
const request = TransactionEnvelope.toTransactionRequest(value)
|
|
2156
|
+
expect(request.nonceKeys).toEqual(vector.nonceKeys)
|
|
2157
|
+
expect(
|
|
2158
|
+
TransactionEnvelope.serialize(TransactionRequest.toEnvelope(request)),
|
|
2159
|
+
).toBe(vector.serialized)
|
|
2160
|
+
},
|
|
2161
|
+
)
|
|
2162
|
+
|
|
2163
|
+
test('request conversion preserves omitted sequence and frame gas', () => {
|
|
2164
|
+
const request = {
|
|
2165
|
+
type: 'eip8141',
|
|
2166
|
+
nonceKeys: [1n, 2n],
|
|
2167
|
+
frames: [{ mode: 'sender' }, { mode: 'sender', executionGas: 0n }],
|
|
2168
|
+
} as const
|
|
2169
|
+
const rpc = TransactionRequest.toRpc(request)
|
|
2170
|
+
expect(rpc.nonce).toBeUndefined()
|
|
2171
|
+
expect(rpc.nonceKeys).toMatchInlineSnapshot(`
|
|
2172
|
+
[
|
|
2173
|
+
"0x1",
|
|
2174
|
+
"0x2",
|
|
2175
|
+
]
|
|
2176
|
+
`)
|
|
2177
|
+
expect(rpc.frames?.map((frame) => frame.executionGas)).toEqual([
|
|
2178
|
+
undefined,
|
|
2179
|
+
'0x0',
|
|
2180
|
+
])
|
|
2181
|
+
expect(TransactionRequest.fromRpc(rpc).nonceKeys).toEqual(
|
|
2182
|
+
request.nonceKeys,
|
|
2183
|
+
)
|
|
2184
|
+
expect(TransactionRequest.toRpc({ ...request, nonce: 0n }).nonce).toBe(
|
|
2185
|
+
'0x0',
|
|
2186
|
+
)
|
|
2187
|
+
})
|
|
2188
|
+
|
|
2189
|
+
test('transaction result conversion retains keyed nonces', () => {
|
|
2190
|
+
const rpc = {
|
|
2191
|
+
...TxEnvelopeEip8141.toRpc({
|
|
2192
|
+
...envelope,
|
|
2193
|
+
nonceKeys: [1n, 2n],
|
|
2194
|
+
nonce: 3n,
|
|
2195
|
+
}),
|
|
2196
|
+
blockHash: null,
|
|
2197
|
+
blockNumber: null,
|
|
2198
|
+
transactionIndex: null,
|
|
2199
|
+
hash: `0x${'11'.repeat(32)}` as const,
|
|
2200
|
+
}
|
|
2201
|
+
const value = Transaction.fromRpc(rpc, { pending: true })!
|
|
2202
|
+
expect(value.nonceKeys).toMatchInlineSnapshot(`
|
|
2203
|
+
[
|
|
2204
|
+
1n,
|
|
2205
|
+
2n,
|
|
2206
|
+
]
|
|
2207
|
+
`)
|
|
2208
|
+
expect(Transaction.toRpc(value, { pending: true }).nonceKeys).toEqual(
|
|
2209
|
+
rpc.nonceKeys,
|
|
2210
|
+
)
|
|
2211
|
+
expect(value.nonce).toBe(3n)
|
|
2212
|
+
})
|
|
2213
|
+
|
|
2214
|
+
test('accepts numberish keys without losing uint256 precision', () => {
|
|
2215
|
+
expect(
|
|
2216
|
+
TxEnvelopeEip8141.toRpc({
|
|
2217
|
+
...envelope,
|
|
2218
|
+
nonceKeys: ['0x1', 128, 2n ** 256n - 1n],
|
|
2219
|
+
nonce: '0x0',
|
|
2220
|
+
}).nonceKeys,
|
|
2221
|
+
).toEqual(['0x1', '0x80', `0x${'ff'.repeat(32)}`])
|
|
2222
|
+
})
|
|
2223
|
+
})
|
|
2224
|
+
|
|
2225
|
+
describe('fromRpc', () => {
|
|
2226
|
+
test('requires the sequence in keyed transaction results', () => {
|
|
2227
|
+
const value = {
|
|
2228
|
+
...TxEnvelopeEip8141.toRpc({ ...envelope, nonceKeys: [1n] }),
|
|
2229
|
+
nonce: undefined,
|
|
2230
|
+
}
|
|
2231
|
+
expect(() =>
|
|
2232
|
+
Transaction.fromRpc(value as never),
|
|
2233
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
2234
|
+
`[FrameNonce.InvalidError: A keyed transaction result requires nonce.]`,
|
|
2235
|
+
)
|
|
2236
|
+
})
|
|
2237
|
+
|
|
2238
|
+
test.each([
|
|
2239
|
+
'0x00',
|
|
2240
|
+
'0x01',
|
|
2241
|
+
'0x',
|
|
2242
|
+
'0x10000000000000000',
|
|
2243
|
+
'0xffffffffffffffff',
|
|
2244
|
+
])('rejects invalid keyed sequence %s', (nonce) => {
|
|
2245
|
+
expect(() =>
|
|
2246
|
+
TxEnvelopeEip8141.fromRpc({
|
|
2247
|
+
...TxEnvelopeEip8141.toRpc(envelope),
|
|
2248
|
+
nonceKeys: ['0x1'],
|
|
2249
|
+
nonce: nonce as Hex.Hex,
|
|
2250
|
+
}),
|
|
2251
|
+
).toThrow()
|
|
2252
|
+
expect(() =>
|
|
2253
|
+
TransactionRequest.fromRpc({
|
|
2254
|
+
nonceKeys: ['0x1'],
|
|
2255
|
+
nonce: nonce as Hex.Hex,
|
|
2256
|
+
}),
|
|
2257
|
+
).toThrow()
|
|
2258
|
+
})
|
|
2259
|
+
test.each([['0x01'], ['0x'], ['0x0', '0x1'], ['0x2', '0x1']])(
|
|
2260
|
+
'rejects invalid RPC keys %j',
|
|
2261
|
+
(...nonceKeys) => {
|
|
2262
|
+
expect(() =>
|
|
2263
|
+
TransactionRequest.fromRpc({ nonceKeys: nonceKeys as Hex.Hex[] }),
|
|
2264
|
+
).toThrow()
|
|
2265
|
+
},
|
|
2266
|
+
)
|
|
2267
|
+
})
|
|
2268
|
+
})
|