@aztec/sequencer-client 0.57.0 → 0.58.0

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 (38) hide show
  1. package/dest/block_builder/index.d.ts +2 -2
  2. package/dest/block_builder/index.d.ts.map +1 -1
  3. package/dest/block_builder/light.d.ts +3 -3
  4. package/dest/block_builder/light.d.ts.map +1 -1
  5. package/dest/block_builder/light.js +6 -5
  6. package/dest/block_builder/orchestrator.d.ts +3 -3
  7. package/dest/block_builder/orchestrator.d.ts.map +1 -1
  8. package/dest/block_builder/orchestrator.js +1 -1
  9. package/dest/client/sequencer-client.d.ts +2 -2
  10. package/dest/client/sequencer-client.d.ts.map +1 -1
  11. package/dest/client/sequencer-client.js +3 -4
  12. package/dest/config.d.ts.map +1 -1
  13. package/dest/config.js +4 -5
  14. package/dest/publisher/l1-publisher-metrics.d.ts.map +1 -1
  15. package/dest/publisher/l1-publisher-metrics.js +2 -1
  16. package/dest/publisher/l1-publisher.d.ts +9 -4
  17. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  18. package/dest/publisher/l1-publisher.js +71 -25
  19. package/dest/sequencer/sequencer.d.ts +3 -4
  20. package/dest/sequencer/sequencer.d.ts.map +1 -1
  21. package/dest/sequencer/sequencer.js +98 -80
  22. package/dest/tx_validator/phases_validator.d.ts +1 -1
  23. package/dest/tx_validator/phases_validator.d.ts.map +1 -1
  24. package/dest/tx_validator/phases_validator.js +1 -1
  25. package/dest/tx_validator/tx_validator_factory.d.ts +9 -7
  26. package/dest/tx_validator/tx_validator_factory.d.ts.map +1 -1
  27. package/dest/tx_validator/tx_validator_factory.js +20 -10
  28. package/package.json +19 -19
  29. package/src/block_builder/index.ts +2 -2
  30. package/src/block_builder/light.ts +7 -6
  31. package/src/block_builder/orchestrator.ts +8 -3
  32. package/src/client/sequencer-client.ts +3 -9
  33. package/src/config.ts +3 -4
  34. package/src/publisher/l1-publisher-metrics.ts +1 -0
  35. package/src/publisher/l1-publisher.ts +90 -26
  36. package/src/sequencer/sequencer.ts +117 -100
  37. package/src/tx_validator/phases_validator.ts +1 -1
  38. package/src/tx_validator/tx_validator_factory.ts +39 -15
@@ -4,7 +4,7 @@ import {
4
4
  Body,
5
5
  L2Block,
6
6
  MerkleTreeId,
7
- type MerkleTreeOperations,
7
+ type MerkleTreeWriteOperations,
8
8
  type ProcessedTx,
9
9
  type TxEffect,
10
10
  makeEmptyProcessedTx,
@@ -15,11 +15,11 @@ import {
15
15
  type GlobalVariables,
16
16
  NESTED_RECURSIVE_PROOF_LENGTH,
17
17
  NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
18
- VerificationKeyData,
19
18
  makeEmptyRecursiveProof,
20
19
  } from '@aztec/circuits.js';
21
20
  import { padArrayEnd } from '@aztec/foundation/collection';
22
- import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types';
21
+ import { TubeVk, getVKTreeRoot } from '@aztec/noir-protocol-circuits-types';
22
+ import { protocolContractTreeRoot } from '@aztec/protocol-contracts';
23
23
  import { buildBaseRollupInput, buildHeaderFromTxEffects, getTreeSnapshot } from '@aztec/prover-client/helpers';
24
24
  import { type TelemetryClient } from '@aztec/telemetry-client';
25
25
  import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
@@ -36,7 +36,7 @@ export class LightweightBlockBuilder implements BlockBuilder {
36
36
 
37
37
  private readonly logger = createDebugLogger('aztec:sequencer-client:block_builder_light');
38
38
 
39
- constructor(private db: MerkleTreeOperations, private telemetry: TelemetryClient) {}
39
+ constructor(private db: MerkleTreeWriteOperations, private telemetry: TelemetryClient) {}
40
40
 
41
41
  async startNewBlock(numTxs: number, globalVariables: GlobalVariables, l1ToL2Messages: Fr[]): Promise<void> {
42
42
  this.logger.verbose('Starting new block', { numTxs, globalVariables, l1ToL2Messages });
@@ -56,7 +56,7 @@ export class LightweightBlockBuilder implements BlockBuilder {
56
56
  makeEmptyRecursiveProof(NESTED_RECURSIVE_PROOF_LENGTH),
57
57
  this.globalVariables!,
58
58
  this.db,
59
- VerificationKeyData.makeFake(),
59
+ TubeVk,
60
60
  );
61
61
  }
62
62
 
@@ -70,6 +70,7 @@ export class LightweightBlockBuilder implements BlockBuilder {
70
70
  this.globalVariables!.chainId,
71
71
  this.globalVariables!.version,
72
72
  getVKTreeRoot(),
73
+ protocolContractTreeRoot,
73
74
  ),
74
75
  );
75
76
  }
@@ -96,7 +97,7 @@ export class LightweightBlockBuilder implements BlockBuilder {
96
97
  export class LightweightBlockBuilderFactory {
97
98
  constructor(private telemetry?: TelemetryClient) {}
98
99
 
99
- create(db: MerkleTreeOperations): BlockBuilder {
100
+ create(db: MerkleTreeWriteOperations): BlockBuilder {
100
101
  return new LightweightBlockBuilder(db, this.telemetry ?? new NoopTelemetryClient());
101
102
  }
102
103
  }
@@ -1,5 +1,10 @@
1
1
  import { TestCircuitProver } from '@aztec/bb-prover';
2
- import { type BlockBuilder, type L2Block, type MerkleTreeOperations, type ProcessedTx } from '@aztec/circuit-types';
2
+ import {
3
+ type BlockBuilder,
4
+ type L2Block,
5
+ type MerkleTreeWriteOperations,
6
+ type ProcessedTx,
7
+ } from '@aztec/circuit-types';
3
8
  import { type Fr, type GlobalVariables } from '@aztec/circuits.js';
4
9
  import { ProvingOrchestrator } from '@aztec/prover-client/orchestrator';
5
10
  import { type SimulationProvider } from '@aztec/simulator';
@@ -13,7 +18,7 @@ import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
13
18
  */
14
19
  export class OrchestratorBlockBuilder implements BlockBuilder {
15
20
  private orchestrator: ProvingOrchestrator;
16
- constructor(db: MerkleTreeOperations, simulationProvider: SimulationProvider, telemetry: TelemetryClient) {
21
+ constructor(db: MerkleTreeWriteOperations, simulationProvider: SimulationProvider, telemetry: TelemetryClient) {
17
22
  const testProver = new TestCircuitProver(telemetry, simulationProvider);
18
23
  this.orchestrator = new ProvingOrchestrator(db, testProver, telemetry);
19
24
  }
@@ -32,7 +37,7 @@ export class OrchestratorBlockBuilder implements BlockBuilder {
32
37
  export class OrchestratorBlockBuilderFactory {
33
38
  constructor(private simulationProvider: SimulationProvider, private telemetry?: TelemetryClient) {}
34
39
 
35
- create(db: MerkleTreeOperations): BlockBuilder {
40
+ create(db: MerkleTreeWriteOperations): BlockBuilder {
36
41
  return new OrchestratorBlockBuilder(db, this.simulationProvider, this.telemetry ?? new NoopTelemetryClient());
37
42
  }
38
43
  }
@@ -1,9 +1,9 @@
1
1
  import { type L1ToL2MessageSource, type L2BlockSource, type WorldStateSynchronizer } from '@aztec/circuit-types';
2
+ import { type ContractDataSource } from '@aztec/circuits.js';
2
3
  import { type EthAddress } from '@aztec/foundation/eth-address';
3
4
  import { type P2P } from '@aztec/p2p';
4
5
  import { PublicProcessorFactory, type SimulationProvider } from '@aztec/simulator';
5
6
  import { type TelemetryClient } from '@aztec/telemetry-client';
6
- import { type ContractDataSource } from '@aztec/types/contracts';
7
7
  import { type ValidatorClient } from '@aztec/validator-client';
8
8
 
9
9
  import { LightweightBlockBuilderFactory } from '../block_builder/index.js';
@@ -45,14 +45,8 @@ export class SequencerClient {
45
45
  ) {
46
46
  const publisher = new L1Publisher(config, telemetryClient);
47
47
  const globalsBuilder = new GlobalVariableBuilder(config);
48
- const merkleTreeDb = worldStateSynchronizer.getLatest();
49
48
 
50
- const publicProcessorFactory = new PublicProcessorFactory(
51
- merkleTreeDb,
52
- contractDataSource,
53
- simulationProvider,
54
- telemetryClient,
55
- );
49
+ const publicProcessorFactory = new PublicProcessorFactory(contractDataSource, simulationProvider, telemetryClient);
56
50
 
57
51
  const sequencer = new Sequencer(
58
52
  publisher,
@@ -64,7 +58,7 @@ export class SequencerClient {
64
58
  l2BlockSource,
65
59
  l1ToL2MessageSource,
66
60
  publicProcessorFactory,
67
- new TxValidatorFactory(merkleTreeDb, contractDataSource, !!config.enforceFees),
61
+ new TxValidatorFactory(worldStateSynchronizer.getCommitted(), contractDataSource, !!config.enforceFees),
68
62
  telemetryClient,
69
63
  config,
70
64
  );
package/src/config.ts CHANGED
@@ -10,8 +10,7 @@ import {
10
10
  import { EthAddress } from '@aztec/foundation/eth-address';
11
11
  import { FPCContract } from '@aztec/noir-contracts.js/FPC';
12
12
  import { TokenContractArtifact } from '@aztec/noir-contracts.js/Token';
13
- import { AuthRegistryAddress } from '@aztec/protocol-contracts/auth-registry';
14
- import { FeeJuiceAddress } from '@aztec/protocol-contracts/fee-juice';
13
+ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
15
14
 
16
15
  import {
17
16
  type PublisherConfig,
@@ -184,11 +183,11 @@ function getDefaultAllowedSetupFunctions(): AllowedElement[] {
184
183
  return [
185
184
  // needed for authwit support
186
185
  {
187
- address: AuthRegistryAddress,
186
+ address: ProtocolContractAddress.AuthRegistry,
188
187
  },
189
188
  // needed for claiming on the same tx as a spend
190
189
  {
191
- address: FeeJuiceAddress,
190
+ address: ProtocolContractAddress.FeeJuice,
192
191
  // We can't restrict the selector because public functions get routed via dispatch.
193
192
  // selector: FunctionSelector.fromSignature('_increase_public_balance((Field),Field)'),
194
193
  },
@@ -83,6 +83,7 @@ export class L1PublisherMetrics {
83
83
  private recordTx(txType: L1TxType, durationMs: number, stats: Omit<L1PublishProofStats, 'eventName'>) {
84
84
  const attributes = {
85
85
  [Attributes.L1_TX_TYPE]: txType,
86
+ [Attributes.L1_SENDER]: stats.sender,
86
87
  } as const;
87
88
 
88
89
  this.txCount.add(1, {
@@ -9,6 +9,7 @@ import {
9
9
  import { type L1PublishBlockStats, type L1PublishProofStats } from '@aztec/circuit-types/stats';
10
10
  import {
11
11
  AGGREGATION_OBJECT_LENGTH,
12
+ AZTEC_EPOCH_DURATION,
12
13
  ETHEREUM_SLOT_DURATION,
13
14
  EthAddress,
14
15
  type FeeRecipient,
@@ -18,11 +19,11 @@ import {
18
19
  } from '@aztec/circuits.js';
19
20
  import { createEthereumChain } from '@aztec/ethereum';
20
21
  import { makeTuple } from '@aztec/foundation/array';
21
- import { areArraysEqual, times } from '@aztec/foundation/collection';
22
+ import { areArraysEqual, compactArray, times } from '@aztec/foundation/collection';
22
23
  import { type Signature } from '@aztec/foundation/eth-signature';
23
24
  import { Fr } from '@aztec/foundation/fields';
24
25
  import { createDebugLogger } from '@aztec/foundation/log';
25
- import { type Tuple, serializeToBuffer } from '@aztec/foundation/serialize';
26
+ import { type Tuple, serializeToBuffer, toFriendlyJSON } from '@aztec/foundation/serialize';
26
27
  import { InterruptibleSleep } from '@aztec/foundation/sleep';
27
28
  import { Timer } from '@aztec/foundation/timer';
28
29
  import { RollupAbi } from '@aztec/l1-artifacts';
@@ -34,6 +35,7 @@ import {
34
35
  type BaseError,
35
36
  type Chain,
36
37
  type Client,
38
+ type ContractFunctionExecutionError,
37
39
  ContractFunctionRevertedError,
38
40
  type GetContractReturnType,
39
41
  type Hex,
@@ -66,6 +68,8 @@ import { prettyLogViemError } from './utils.js';
66
68
  * Stats for a sent transaction.
67
69
  */
68
70
  export type TransactionStats = {
71
+ /** Address of the sender. */
72
+ sender: string;
69
73
  /** Hash of the transaction. */
70
74
  transactionHash: string;
71
75
  /** Size in bytes of the tx calldata */
@@ -81,13 +85,15 @@ export type MinimalTransactionReceipt = {
81
85
  /** True if the tx was successful, false if reverted. */
82
86
  status: boolean;
83
87
  /** Hash of the transaction. */
84
- transactionHash: string;
88
+ transactionHash: `0x${string}`;
85
89
  /** Effective gas used by the tx. */
86
90
  gasUsed: bigint;
87
91
  /** Effective gas price paid by the tx. */
88
92
  gasPrice: bigint;
89
93
  /** Logs emitted in this tx. */
90
94
  logs: any[];
95
+ /** Block number in which this tx was mined. */
96
+ blockNumber: bigint;
91
97
  };
92
98
 
93
99
  /** Arguments to the process method of the rollup contract */
@@ -116,7 +122,7 @@ export type L1SubmitEpochProofArgs = {
116
122
  endTimestamp: Fr;
117
123
  outHash: Fr;
118
124
  proverId: Fr;
119
- fees: Tuple<FeeRecipient, 32>;
125
+ fees: Tuple<FeeRecipient, typeof AZTEC_EPOCH_DURATION>;
120
126
  proof: Proof;
121
127
  };
122
128
 
@@ -133,7 +139,8 @@ export class L1Publisher {
133
139
  private sleepTimeMs: number;
134
140
  private interrupted = false;
135
141
  private metrics: L1PublisherMetrics;
136
- private log = createDebugLogger('aztec:sequencer:publisher');
142
+
143
+ protected log = createDebugLogger('aztec:sequencer:publisher');
137
144
 
138
145
  private rollupContract: GetContractReturnType<
139
146
  typeof RollupAbi,
@@ -144,8 +151,8 @@ export class L1Publisher {
144
151
  private walletClient: WalletClient<HttpTransport, chains.Chain, PrivateKeyAccount>;
145
152
  private account: PrivateKeyAccount;
146
153
 
147
- public static PROPOSE_GAS_GUESS: bigint = 500_000n;
148
- public static PROPOSE_AND_CLAIM_GAS_GUESS: bigint = 600_000n;
154
+ public static PROPOSE_GAS_GUESS: bigint = 12_000_000n;
155
+ public static PROPOSE_AND_CLAIM_GAS_GUESS: bigint = this.PROPOSE_GAS_GUESS + 100_000n;
149
156
 
150
157
  constructor(config: TxSenderConfig & PublisherConfig, client: TelemetryClient) {
151
158
  this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
@@ -206,6 +213,9 @@ export class L1Publisher {
206
213
  * @return blockNumber - The L2 block number of the next L2 block
207
214
  */
208
215
  public async canProposeAtNextEthBlock(archive: Buffer): Promise<[bigint, bigint]> {
216
+ // FIXME: This should not throw if unable to propose but return a falsey value, so
217
+ // we can differentiate between errors when hitting the L1 rollup contract (eg RPC error)
218
+ // which may require a retry, vs actually not being the turn for proposing.
209
219
  const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION));
210
220
  const [slot, blockNumber] = await this.rollupContract.read.canProposeAtTime([ts, `0x${archive.toString('hex')}`]);
211
221
  return [slot, blockNumber];
@@ -258,8 +268,9 @@ export class L1Publisher {
258
268
  try {
259
269
  await this.rollupContract.read.validateEpochProofRightClaim(args, { account: this.account });
260
270
  } catch (err) {
271
+ this.log.verbose(toFriendlyJSON(err as object));
261
272
  const errorName = tryGetCustomErrorName(err);
262
- this.log.verbose(`Proof quote validation failed: ${errorName}`);
273
+ this.log.warn(`Proof quote validation failed: ${errorName}`);
263
274
  return undefined;
264
275
  }
265
276
  return quote;
@@ -321,6 +332,7 @@ export class L1Publisher {
321
332
  }
322
333
  const calldata = hexToBytes(tx.input);
323
334
  return {
335
+ sender: tx.from.toString(),
324
336
  transactionHash: tx.hash,
325
337
  calldataSize: calldata.length,
326
338
  calldataGas: getCalldataGasUsage(calldata),
@@ -375,15 +387,17 @@ export class L1Publisher {
375
387
 
376
388
  this.log.verbose(`Submitting propose transaction`);
377
389
 
378
- const txHash = proofQuote
390
+ const tx = proofQuote
379
391
  ? await this.sendProposeAndClaimTx(proposeTxArgs, proofQuote)
380
392
  : await this.sendProposeTx(proposeTxArgs);
381
393
 
382
- if (!txHash) {
394
+ if (!tx) {
383
395
  this.log.info(`Failed to publish block ${block.number} to L1`, ctx);
384
396
  return false;
385
397
  }
386
398
 
399
+ const { hash: txHash, args, functionName, gasLimit } = tx;
400
+
387
401
  const receipt = await this.getTransactionReceipt(txHash);
388
402
  if (!receipt) {
389
403
  this.log.info(`Failed to get receipt for tx ${txHash}`, ctx);
@@ -395,7 +409,7 @@ export class L1Publisher {
395
409
  const tx = await this.getTransactionStats(txHash);
396
410
  const stats: L1PublishBlockStats = {
397
411
  ...pick(receipt, 'gasPrice', 'gasUsed', 'transactionHash'),
398
- ...pick(tx!, 'calldataGas', 'calldataSize'),
412
+ ...pick(tx!, 'calldataGas', 'calldataSize', 'sender'),
399
413
  ...block.getStats(),
400
414
  eventName: 'rollup-published-to-l1',
401
415
  };
@@ -407,11 +421,45 @@ export class L1Publisher {
407
421
 
408
422
  this.metrics.recordFailedTx('process');
409
423
 
410
- this.log.error(`Rollup.process tx status failed: ${receipt.transactionHash}`, ctx);
424
+ const errorMsg = await this.tryGetErrorFromRevertedTx({
425
+ args,
426
+ functionName,
427
+ gasLimit,
428
+ abi: RollupAbi,
429
+ address: this.rollupContract.address,
430
+ blockNumber: receipt.blockNumber,
431
+ });
432
+ this.log.error(`Rollup process tx reverted. ${errorMsg}`, undefined, {
433
+ ...ctx,
434
+ txHash: receipt.transactionHash,
435
+ });
411
436
  await this.sleepOrInterrupted();
412
437
  return false;
413
438
  }
414
439
 
440
+ private async tryGetErrorFromRevertedTx(args: {
441
+ args: any[];
442
+ functionName: string;
443
+ gasLimit: bigint;
444
+ abi: any;
445
+ address: Hex;
446
+ blockNumber: bigint | undefined;
447
+ }) {
448
+ try {
449
+ await this.publicClient.simulateContract({ ...args, account: this.walletClient.account });
450
+ return undefined;
451
+ } catch (err: any) {
452
+ if (err.name === 'ContractFunctionExecutionError') {
453
+ const execErr = err as ContractFunctionExecutionError;
454
+ return compactArray([
455
+ execErr.shortMessage,
456
+ ...(execErr.metaMessages ?? []).slice(0, 2).map(s => s.trim()),
457
+ ]).join(' ');
458
+ }
459
+ this.log.error(`Error getting error from simulation`, err);
460
+ }
461
+ }
462
+
415
463
  public async submitEpochProof(args: {
416
464
  epochNumber: number;
417
465
  fromBlock: number;
@@ -442,7 +490,7 @@ export class L1Publisher {
442
490
  const tx = await this.getTransactionStats(txHash);
443
491
  const stats: L1PublishProofStats = {
444
492
  ...pick(receipt, 'gasPrice', 'gasUsed', 'transactionHash'),
445
- ...pick(tx!, 'calldataGas', 'calldataSize'),
493
+ ...pick(tx!, 'calldataGas', 'calldataSize', 'sender'),
446
494
  eventName: 'proof-published-to-l1',
447
495
  };
448
496
  this.log.info(`Published epoch proof to L1 rollup contract`, { ...stats, ...ctx });
@@ -601,7 +649,7 @@ export class L1Publisher {
601
649
  args.publicInputs.outHash.toString(),
602
650
  args.publicInputs.proverId.toString(),
603
651
  ],
604
- makeTuple(64, i =>
652
+ makeTuple(AZTEC_EPOCH_DURATION * 2, i =>
605
653
  i % 2 === 0
606
654
  ? args.publicInputs.fees[i / 2].recipient.toField().toString()
607
655
  : args.publicInputs.fees[(i - 1) / 2].value.toString(),
@@ -610,17 +658,24 @@ export class L1Publisher {
610
658
  ] as const;
611
659
  }
612
660
 
613
- private async sendProposeTx(encodedData: L1ProcessArgs): Promise<string | undefined> {
661
+ private async sendProposeTx(
662
+ encodedData: L1ProcessArgs,
663
+ ): Promise<{ hash: string; args: any; functionName: string; gasLimit: bigint } | undefined> {
614
664
  if (this.interrupted) {
615
- return;
665
+ return undefined;
616
666
  }
617
667
  try {
618
668
  const { args, gasGuesstimate } = await this.prepareProposeTx(encodedData, L1Publisher.PROPOSE_GAS_GUESS);
619
669
 
620
- return await this.rollupContract.write.propose(args, {
621
- account: this.account,
622
- gas: gasGuesstimate,
623
- });
670
+ return {
671
+ hash: await this.rollupContract.write.propose(args, {
672
+ account: this.account,
673
+ gas: gasGuesstimate,
674
+ }),
675
+ args,
676
+ functionName: 'propose',
677
+ gasLimit: gasGuesstimate,
678
+ };
624
679
  } catch (err) {
625
680
  prettyLogViemError(err, this.log);
626
681
  this.log.error(`Rollup publish failed`, err);
@@ -628,9 +683,12 @@ export class L1Publisher {
628
683
  }
629
684
  }
630
685
 
631
- private async sendProposeAndClaimTx(encodedData: L1ProcessArgs, quote: EpochProofQuote): Promise<string | undefined> {
686
+ private async sendProposeAndClaimTx(
687
+ encodedData: L1ProcessArgs,
688
+ quote: EpochProofQuote,
689
+ ): Promise<{ hash: string; args: any; functionName: string; gasLimit: bigint } | undefined> {
632
690
  if (this.interrupted) {
633
- return;
691
+ return undefined;
634
692
  }
635
693
  try {
636
694
  const { args, gasGuesstimate } = await this.prepareProposeTx(
@@ -640,10 +698,15 @@ export class L1Publisher {
640
698
  this.log.info(`ProposeAndClaim`);
641
699
  this.log.info(inspect(quote.payload));
642
700
 
643
- return await this.rollupContract.write.proposeAndClaim([...args, quote.toViemArgs()], {
644
- account: this.account,
645
- gas: gasGuesstimate,
646
- });
701
+ return {
702
+ hash: await this.rollupContract.write.proposeAndClaim([...args, quote.toViemArgs()], {
703
+ account: this.account,
704
+ gas: gasGuesstimate,
705
+ }),
706
+ functionName: 'proposeAndClaim',
707
+ args,
708
+ gasLimit: gasGuesstimate,
709
+ };
647
710
  } catch (err) {
648
711
  prettyLogViemError(err, this.log);
649
712
  this.log.error(`Rollup publish failed`, err);
@@ -674,6 +737,7 @@ export class L1Publisher {
674
737
  gasUsed: receipt.gasUsed,
675
738
  gasPrice: receipt.effectiveGasPrice,
676
739
  logs: receipt.logs,
740
+ blockNumber: receipt.blockNumber,
677
741
  };
678
742
  }
679
743