@aztec/sequencer-client 0.48.0 → 0.50.1

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 (44) hide show
  1. package/dest/block_builder/index.d.ts +26 -0
  2. package/dest/block_builder/index.d.ts.map +1 -0
  3. package/dest/block_builder/index.js +40 -0
  4. package/dest/client/sequencer-client.d.ts +6 -2
  5. package/dest/client/sequencer-client.d.ts.map +1 -1
  6. package/dest/client/sequencer-client.js +14 -7
  7. package/dest/config.d.ts.map +1 -1
  8. package/dest/config.js +1 -6
  9. package/dest/global_variable_builder/global_builder.d.ts +6 -54
  10. package/dest/global_variable_builder/global_builder.d.ts.map +1 -1
  11. package/dest/global_variable_builder/global_builder.js +24 -12
  12. package/dest/global_variable_builder/index.d.ts +0 -9
  13. package/dest/global_variable_builder/index.d.ts.map +1 -1
  14. package/dest/global_variable_builder/index.js +2 -12
  15. package/dest/publisher/index.d.ts +0 -8
  16. package/dest/publisher/index.d.ts.map +1 -1
  17. package/dest/publisher/index.js +1 -10
  18. package/dest/publisher/l1-publisher.d.ts +26 -70
  19. package/dest/publisher/l1-publisher.d.ts.map +1 -1
  20. package/dest/publisher/l1-publisher.js +168 -19
  21. package/dest/sequencer/sequencer.d.ts +25 -11
  22. package/dest/sequencer/sequencer.d.ts.map +1 -1
  23. package/dest/sequencer/sequencer.js +90 -36
  24. package/package.json +18 -15
  25. package/src/block_builder/index.ts +51 -0
  26. package/src/client/sequencer-client.ts +15 -7
  27. package/src/config.ts +0 -5
  28. package/src/global_variable_builder/global_builder.ts +38 -62
  29. package/src/global_variable_builder/index.ts +0 -15
  30. package/src/publisher/index.ts +0 -14
  31. package/src/publisher/l1-publisher.ts +217 -95
  32. package/src/sequencer/sequencer.ts +113 -45
  33. package/dest/global_variable_builder/viem-reader.d.ts +0 -17
  34. package/dest/global_variable_builder/viem-reader.d.ts.map +0 -1
  35. package/dest/global_variable_builder/viem-reader.js +0 -40
  36. package/dest/publisher/viem-tx-sender.d.ts +0 -59
  37. package/dest/publisher/viem-tx-sender.d.ts.map +0 -1
  38. package/dest/publisher/viem-tx-sender.js +0 -236
  39. package/dest/receiver.d.ts +0 -10
  40. package/dest/receiver.d.ts.map +0 -1
  41. package/dest/receiver.js +0 -2
  42. package/src/global_variable_builder/viem-reader.ts +0 -64
  43. package/src/publisher/viem-tx-sender.ts +0 -296
  44. package/src/receiver.ts +0 -11
@@ -5,71 +5,47 @@ import {
5
5
  GasFees,
6
6
  GlobalVariables,
7
7
  } from '@aztec/circuits.js';
8
+ import { type L1ReaderConfig, createEthereumChain } from '@aztec/ethereum';
8
9
  import { Fr } from '@aztec/foundation/fields';
9
10
  import { createDebugLogger } from '@aztec/foundation/log';
11
+ import { RollupAbi } from '@aztec/l1-artifacts';
12
+
13
+ import {
14
+ type GetContractReturnType,
15
+ type HttpTransport,
16
+ type PublicClient,
17
+ createPublicClient,
18
+ getAddress,
19
+ getContract,
20
+ http,
21
+ } from 'viem';
22
+ import type * as chains from 'viem/chains';
10
23
 
11
24
  /**
12
- * Reads values from L1 state that is used for the global values.
25
+ * Simple global variables builder.
13
26
  */
14
- export interface L1GlobalReader {
15
- /**
16
- * Fetches the version of the rollup contract.
17
- * @returns The version of the rollup contract.
18
- */
19
- getVersion(): Promise<bigint>;
20
- /**
21
- * Gets the chain id.
22
- * @returns The chain id.
23
- */
24
- getChainId(): Promise<bigint>;
25
-
26
- /**
27
- * Gets the current L1 time.
28
- * @returns The current L1 time.
29
- */
30
- getL1CurrentTime(): Promise<bigint>;
27
+ export class GlobalVariableBuilder {
28
+ private log = createDebugLogger('aztec:sequencer:global_variable_builder');
31
29
 
32
- /**
33
- * Gets the current slot.
34
- * @returns The current slot.
35
- */
36
- getCurrentSlot(): Promise<bigint>;
30
+ private rollupContract: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, chains.Chain>>;
31
+ private publicClient: PublicClient<HttpTransport, chains.Chain>;
37
32
 
38
- /**
39
- * Get the slot for a specific timestamp.
40
- * @param timestamp - The timestamp to get the slot for.
41
- */
42
- getSlotAt(timestamp: readonly [bigint]): Promise<bigint>;
33
+ constructor(config: L1ReaderConfig) {
34
+ const { l1RpcUrl, l1ChainId: chainId, l1Contracts } = config;
43
35
 
44
- /**
45
- * Gets the timestamp for a slot
46
- * @param slot - The slot to get the timestamp for.
47
- * @returns The timestamp for the slot.
48
- */
49
- getTimestampForSlot(slot: readonly [bigint]): Promise<bigint>;
50
- }
36
+ const chain = createEthereumChain(l1RpcUrl, chainId);
51
37
 
52
- /**
53
- * Builds global variables from L1 state.
54
- */
55
- export interface GlobalVariableBuilder {
56
- /**
57
- * Builds global variables.
58
- * @param blockNumber - The block number to build global variables for.
59
- * @param coinbase - The address to receive block reward.
60
- * @param feeRecipient - The address to receive fees.
61
- * @returns The global variables for the given block number.
62
- */
63
- buildGlobalVariables(blockNumber: Fr, coinbase: EthAddress, feeRecipient: AztecAddress): Promise<GlobalVariables>;
64
- }
38
+ this.publicClient = createPublicClient({
39
+ chain: chain.chainInfo,
40
+ transport: http(chain.rpcUrl),
41
+ });
65
42
 
66
- /**
67
- * Simple test implementation of a builder that uses the minimum time possible for the global variables.
68
- * Also uses a "hack" to make use of the warp cheatcode that manipulates time on Aztec.
69
- */
70
- export class SimpleTestGlobalVariableBuilder implements GlobalVariableBuilder {
71
- private log = createDebugLogger('aztec:sequencer:simple_test_global_variable_builder');
72
- constructor(private readonly reader: L1GlobalReader) {}
43
+ this.rollupContract = getContract({
44
+ address: getAddress(l1Contracts.rollupAddress.toString()),
45
+ abi: RollupAbi,
46
+ client: this.publicClient,
47
+ });
48
+ }
73
49
 
74
50
  /**
75
51
  * Simple builder of global variables that use the minimum time possible.
@@ -83,18 +59,18 @@ export class SimpleTestGlobalVariableBuilder implements GlobalVariableBuilder {
83
59
  coinbase: EthAddress,
84
60
  feeRecipient: AztecAddress,
85
61
  ): Promise<GlobalVariables> {
86
- // Not just the current slot, the slot of the next block.
87
- const ts = (await this.reader.getL1CurrentTime()) + BigInt(ETHEREUM_SLOT_DURATION);
62
+ const version = new Fr(await this.rollupContract.read.VERSION());
63
+ const chainId = new Fr(this.publicClient.chain.id);
64
+
65
+ const ts = (await this.publicClient.getBlock()).timestamp;
88
66
 
89
- const slot = await this.reader.getSlotAt([ts]);
90
- const timestamp = await this.reader.getTimestampForSlot([slot]);
67
+ // Not just the current slot, the slot of the next block.
68
+ const slot = await this.rollupContract.read.getSlotAt([ts + BigInt(ETHEREUM_SLOT_DURATION)]);
69
+ const timestamp = await this.rollupContract.read.getTimestampForSlot([slot]);
91
70
 
92
71
  const slotFr = new Fr(slot);
93
72
  const timestampFr = new Fr(timestamp);
94
73
 
95
- const version = new Fr(await this.reader.getVersion());
96
- const chainId = new Fr(await this.reader.getChainId());
97
-
98
74
  const gasFees = GasFees.default();
99
75
  const globalVariables = new GlobalVariables(
100
76
  chainId,
@@ -1,16 +1 @@
1
- import { type L1ReaderConfig } from '@aztec/ethereum';
2
-
3
- import { type GlobalVariableBuilder, SimpleTestGlobalVariableBuilder } from './global_builder.js';
4
- import { ViemReader } from './viem-reader.js';
5
-
6
- export { SimpleTestGlobalVariableBuilder as SimpleGlobalVariableBuilder } from './global_builder.js';
7
1
  export { GlobalVariableBuilder } from './global_builder.js';
8
-
9
- /**
10
- * Returns a new instance of the global variable builder.
11
- * @param config - Configuration to initialize the builder.
12
- * @returns A new instance of the global variable builder.
13
- */
14
- export function getGlobalVariableBuilder(config: L1ReaderConfig): GlobalVariableBuilder {
15
- return new SimpleTestGlobalVariableBuilder(new ViemReader(config));
16
- }
@@ -1,16 +1,2 @@
1
- import { type TelemetryClient } from '@aztec/telemetry-client';
2
-
3
- import { type PublisherConfig, type TxSenderConfig } from './config.js';
4
- import { L1Publisher } from './l1-publisher.js';
5
- import { ViemTxSender } from './viem-tx-sender.js';
6
-
7
1
  export { L1Publisher } from './l1-publisher.js';
8
2
  export * from './config.js';
9
-
10
- /**
11
- * Returns a new instance of the L1Publisher.
12
- * @param config - Configuration to initialize the new instance.
13
- */
14
- export function getL1Publisher(config: PublisherConfig & TxSenderConfig, client: TelemetryClient): L1Publisher {
15
- return new L1Publisher(new ViemTxSender(config), client, config);
16
- }
@@ -1,17 +1,34 @@
1
- import { type L2Block } from '@aztec/circuit-types';
1
+ import { type L2Block, type Signature } from '@aztec/circuit-types';
2
2
  import { type L1PublishBlockStats, type L1PublishProofStats } from '@aztec/circuit-types/stats';
3
- import { type EthAddress, type Header, type Proof } from '@aztec/circuits.js';
3
+ import { ETHEREUM_SLOT_DURATION, EthAddress, type Header, type Proof } from '@aztec/circuits.js';
4
+ import { createEthereumChain } from '@aztec/ethereum';
4
5
  import { type Fr } from '@aztec/foundation/fields';
5
6
  import { createDebugLogger } from '@aztec/foundation/log';
6
7
  import { serializeToBuffer } from '@aztec/foundation/serialize';
7
8
  import { InterruptibleSleep } from '@aztec/foundation/sleep';
8
9
  import { Timer } from '@aztec/foundation/timer';
10
+ import { AvailabilityOracleAbi, RollupAbi } from '@aztec/l1-artifacts';
9
11
  import { type TelemetryClient } from '@aztec/telemetry-client';
10
12
 
11
13
  import pick from 'lodash.pick';
12
-
13
- import { type L2BlockReceiver } from '../receiver.js';
14
- import { type PublisherConfig } from './config.js';
14
+ import {
15
+ type GetContractReturnType,
16
+ type Hex,
17
+ type HttpTransport,
18
+ type PrivateKeyAccount,
19
+ type PublicClient,
20
+ type WalletClient,
21
+ createPublicClient,
22
+ createWalletClient,
23
+ getAddress,
24
+ getContract,
25
+ hexToBytes,
26
+ http,
27
+ } from 'viem';
28
+ import { privateKeyToAccount } from 'viem/accounts';
29
+ import type * as chains from 'viem/chains';
30
+
31
+ import { type PublisherConfig, type TxSenderConfig } from './config.js';
15
32
  import { L1PublisherMetrics } from './l1-publisher-metrics.js';
16
33
 
17
34
  /**
@@ -27,7 +44,7 @@ export type TransactionStats = {
27
44
  };
28
45
 
29
46
  /**
30
- * Minimal information from a tx receipt returned by an L1PublisherTxSender.
47
+ * Minimal information from a tx receipt.
31
48
  */
32
49
  export type MinimalTransactionReceipt = {
33
50
  /** True if the tx was successful, false if reverted. */
@@ -49,84 +66,18 @@ export type MinimalTransactionReceipt = {
49
66
  */
50
67
  export type Attestation = { isEmpty: boolean; v: number; r: `0x${string}`; s: `0x${string}` };
51
68
 
52
- /**
53
- * Pushes txs to the L1 chain and waits for their completion.
54
- */
55
- export interface L1PublisherTxSender {
56
- /** Attests to the given archive root. */
57
- attest(archive: `0x${string}`): Promise<Attestation>;
58
-
59
- /** Returns the EOA used for sending txs to L1. */
60
- getSenderAddress(): Promise<EthAddress>;
61
-
62
- /** Returns the address of the L2 proposer at the NEXT Ethereum block zero if anyone can submit. */
63
- getProposerAtNextEthBlock(): Promise<EthAddress>;
64
-
65
- /**
66
- * Publishes tx effects to Availability Oracle.
67
- * @param encodedBody - Encoded block body.
68
- * @returns The hash of the mined tx.
69
- */
70
- sendPublishTx(encodedBody: Buffer): Promise<string | undefined>;
71
-
72
- /**
73
- * Sends a tx to the L1 rollup contract with a new L2 block. Returns once the tx has been mined.
74
- * @param encodedData - Serialized data for processing the new L2 block.
75
- * @returns The hash of the mined tx.
76
- */
77
- sendProcessTx(encodedData: L1ProcessArgs): Promise<string | undefined>;
78
-
79
- /**
80
- * Publishes tx effects to availability oracle and send L2 block to rollup contract
81
- * @param encodedData - Data for processing the new L2 block.
82
- * @returns The hash of the tx.
83
- */
84
- sendPublishAndProcessTx(encodedData: L1ProcessArgs): Promise<string | undefined>;
85
-
86
- /**
87
- * Sends a tx to the L1 rollup contract with a proof. Returns once the tx has been mined.
88
- * @param encodedData - Serialized data for processing the new L2 block.
89
- * @returns The hash of the mined tx.
90
- */
91
- sendSubmitProofTx(submitProofArgs: L1SubmitProofArgs): Promise<string | undefined>;
92
-
93
- /**
94
- * Returns a tx receipt if the tx has been mined.
95
- * @param txHash - Hash of the tx to look for.
96
- * @returns Undefined if the tx hasn't been mined yet, the receipt otherwise.
97
- */
98
- getTransactionReceipt(txHash: string): Promise<MinimalTransactionReceipt | undefined>;
99
-
100
- /**
101
- * Returns info on a tx by calling eth_getTransaction.
102
- * @param txHash - Hash of the tx to look for.
103
- */
104
- getTransactionStats(txHash: string): Promise<TransactionStats | undefined>;
105
-
106
- /**
107
- * Returns the current archive root.
108
- * @returns The current archive root of the rollup contract.
109
- */
110
- getCurrentArchive(): Promise<Buffer>;
111
-
112
- /**
113
- * Checks if the transaction effects of the given block are available.
114
- * @param block - The block of which to check whether txs are available.
115
- * @returns True if the txs are available, false otherwise.
116
- */
117
- checkIfTxsAreAvailable(block: L2Block): Promise<boolean>;
118
- }
119
-
120
69
  /** Arguments to the process method of the rollup contract */
121
70
  export type L1ProcessArgs = {
122
71
  /** The L2 block header. */
123
72
  header: Buffer;
124
73
  /** A root of the archive tree after the L2 block is applied. */
125
74
  archive: Buffer;
75
+ /** The L2 block's leaf in the archive tree. */
76
+ blockHash: Buffer;
126
77
  /** L2 block body. */
127
78
  body: Buffer;
128
79
  /** Attestations */
129
- attestations?: Attestation[];
80
+ attestations?: Signature[];
130
81
  };
131
82
 
132
83
  /** Arguments to the submitProof method of the rollup contract */
@@ -151,38 +102,107 @@ export type L1SubmitProofArgs = {
151
102
  *
152
103
  * Adapted from https://github.com/AztecProtocol/aztec2-internal/blob/master/falafel/src/rollup_publisher.ts.
153
104
  */
154
- export class L1Publisher implements L2BlockReceiver {
105
+ export class L1Publisher {
155
106
  private interruptibleSleep = new InterruptibleSleep();
156
107
  private sleepTimeMs: number;
157
108
  private interrupted = false;
158
109
  private metrics: L1PublisherMetrics;
159
110
  private log = createDebugLogger('aztec:sequencer:publisher');
160
111
 
161
- constructor(private txSender: L1PublisherTxSender, client: TelemetryClient, config?: PublisherConfig) {
112
+ private availabilityOracleContract: GetContractReturnType<
113
+ typeof AvailabilityOracleAbi,
114
+ WalletClient<HttpTransport, chains.Chain, PrivateKeyAccount>
115
+ >;
116
+ private rollupContract: GetContractReturnType<
117
+ typeof RollupAbi,
118
+ WalletClient<HttpTransport, chains.Chain, PrivateKeyAccount>
119
+ >;
120
+ private publicClient: PublicClient<HttpTransport, chains.Chain>;
121
+ private account: PrivateKeyAccount;
122
+
123
+ constructor(config: TxSenderConfig & PublisherConfig, client: TelemetryClient) {
162
124
  this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000;
163
125
  this.metrics = new L1PublisherMetrics(client, 'L1Publisher');
126
+
127
+ const { l1RpcUrl: rpcUrl, l1ChainId: chainId, publisherPrivateKey, l1Contracts } = config;
128
+ const chain = createEthereumChain(rpcUrl, chainId);
129
+ this.account = privateKeyToAccount(publisherPrivateKey);
130
+ const walletClient = createWalletClient({
131
+ account: this.account,
132
+ chain: chain.chainInfo,
133
+ transport: http(chain.rpcUrl),
134
+ });
135
+
136
+ this.publicClient = createPublicClient({
137
+ chain: chain.chainInfo,
138
+ transport: http(chain.rpcUrl),
139
+ });
140
+
141
+ this.availabilityOracleContract = getContract({
142
+ address: getAddress(l1Contracts.availabilityOracleAddress.toString()),
143
+ abi: AvailabilityOracleAbi,
144
+ client: walletClient,
145
+ });
146
+ this.rollupContract = getContract({
147
+ address: getAddress(l1Contracts.rollupAddress.toString()),
148
+ abi: RollupAbi,
149
+ client: walletClient,
150
+ });
164
151
  }
165
152
 
166
- public async attest(archive: `0x${string}`): Promise<Attestation> {
167
- return await this.txSender.attest(archive);
153
+ public getSenderAddress(): Promise<EthAddress> {
154
+ return Promise.resolve(EthAddress.fromString(this.account.address));
168
155
  }
169
156
 
170
- public async senderAddress(): Promise<EthAddress> {
171
- return await this.txSender.getSenderAddress();
157
+ // Computes who will be the L2 proposer at the next Ethereum block
158
+ // Using next Ethereum block so we do NOT need to wait for it being mined before seeing the effect
159
+ // @note Assumes that all ethereum slots have blocks
160
+ async getProposerAtNextEthBlock(): Promise<EthAddress> {
161
+ try {
162
+ const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION));
163
+ const submitter = await this.rollupContract.read.getProposerAt([ts]);
164
+ return EthAddress.fromString(submitter);
165
+ } catch (err) {
166
+ this.log.warn(`Failed to get submitter: ${err}`);
167
+ return EthAddress.ZERO;
168
+ }
172
169
  }
173
170
 
174
171
  public async isItMyTurnToSubmit(): Promise<boolean> {
175
- const submitter = await this.txSender.getProposerAtNextEthBlock();
176
- const sender = await this.txSender.getSenderAddress();
172
+ const submitter = await this.getProposerAtNextEthBlock();
173
+ const sender = await this.getSenderAddress();
177
174
  return submitter.isZero() || submitter.equals(sender);
178
175
  }
179
176
 
177
+ public async getCurrentEpochCommittee(): Promise<EthAddress[]> {
178
+ const committee = await this.rollupContract.read.getCurrentEpochCommittee();
179
+ return committee.map(EthAddress.fromString);
180
+ }
181
+
182
+ checkIfTxsAreAvailable(block: L2Block): Promise<boolean> {
183
+ const args = [`0x${block.body.getTxsEffectsHash().toString('hex').padStart(64, '0')}`] as const;
184
+ return this.availabilityOracleContract.read.isAvailable(args);
185
+ }
186
+
187
+ async getTransactionStats(txHash: string): Promise<TransactionStats | undefined> {
188
+ const tx = await this.publicClient.getTransaction({ hash: txHash as Hex });
189
+ if (!tx) {
190
+ return undefined;
191
+ }
192
+ const calldata = hexToBytes(tx.input);
193
+ return {
194
+ transactionHash: tx.hash,
195
+ calldataSize: calldata.length,
196
+ calldataGas: getCalldataGasUsage(calldata),
197
+ };
198
+ }
199
+
180
200
  /**
181
201
  * Publishes L2 block on L1.
182
202
  * @param block - L2 block to publish.
183
203
  * @returns True once the tx has been confirmed and is successful, false on revert or interrupt, blocks otherwise.
184
204
  */
185
- public async processL2Block(block: L2Block, attestations?: Attestation[]): Promise<boolean> {
205
+ public async processL2Block(block: L2Block, attestations?: Signature[]): Promise<boolean> {
186
206
  const ctx = { blockNumber: block.number, blockHash: block.hash().toString() };
187
207
  // TODO(#4148) Remove this block number check, it's here because we don't currently have proper genesis state on the contract
188
208
  const lastArchive = block.header.lastArchive.root.toBuffer();
@@ -195,6 +215,7 @@ export class L1Publisher implements L2BlockReceiver {
195
215
  const processTxArgs = {
196
216
  header: block.header.toBuffer(),
197
217
  archive: block.archive.root.toBuffer(),
218
+ blockHash: block.header.hash().toBuffer(),
198
219
  body: encodedBody,
199
220
  attestations,
200
221
  };
@@ -204,7 +225,7 @@ export class L1Publisher implements L2BlockReceiver {
204
225
  let txHash;
205
226
  const timer = new Timer();
206
227
 
207
- if (await this.txSender.checkIfTxsAreAvailable(block)) {
228
+ if (await this.checkIfTxsAreAvailable(block)) {
208
229
  this.log.verbose(`Transaction effects of block ${block.number} already published.`, ctx);
209
230
  txHash = await this.sendProcessTx(processTxArgs);
210
231
  } else {
@@ -224,7 +245,7 @@ export class L1Publisher implements L2BlockReceiver {
224
245
 
225
246
  // Tx was mined successfully
226
247
  if (receipt.status) {
227
- const tx = await this.txSender.getTransactionStats(txHash);
248
+ const tx = await this.getTransactionStats(txHash);
228
249
  const stats: L1PublishBlockStats = {
229
250
  ...pick(receipt, 'gasPrice', 'gasUsed', 'transactionHash'),
230
251
  ...pick(tx!, 'calldataGas', 'calldataSize'),
@@ -284,13 +305,13 @@ export class L1Publisher implements L2BlockReceiver {
284
305
 
285
306
  // Tx was mined successfully
286
307
  if (receipt.status) {
287
- const tx = await this.txSender.getTransactionStats(txHash);
308
+ const tx = await this.getTransactionStats(txHash);
288
309
  const stats: L1PublishProofStats = {
289
310
  ...pick(receipt, 'gasPrice', 'gasUsed', 'transactionHash'),
290
311
  ...pick(tx!, 'calldataGas', 'calldataSize'),
291
312
  eventName: 'proof-published-to-l1',
292
313
  };
293
- this.log.info(`Published L2 block to L1 rollup contract`, { ...stats, ...ctx });
314
+ this.log.info(`Published proof to L1 rollup contract`, { ...stats, ...ctx });
294
315
  this.metrics.recordSubmitProof(timer.ms(), stats);
295
316
  return true;
296
317
  }
@@ -320,13 +341,18 @@ export class L1Publisher implements L2BlockReceiver {
320
341
  this.interrupted = false;
321
342
  }
322
343
 
344
+ async getCurrentArchive(): Promise<Buffer> {
345
+ const archive = await this.rollupContract.read.archive();
346
+ return Buffer.from(archive.replace('0x', ''), 'hex');
347
+ }
348
+
323
349
  /**
324
350
  * Verifies that the given value of last archive in a block header equals current archive of the rollup contract
325
351
  * @param lastArchive - The last archive of the block we wish to publish.
326
352
  * @returns Boolean indicating if the hashes are equal.
327
353
  */
328
354
  private async checkLastArchiveHash(lastArchive: Buffer): Promise<boolean> {
329
- const fromChain = await this.txSender.getCurrentArchive();
355
+ const fromChain = await this.getCurrentArchive();
330
356
  const areSame = lastArchive.equals(fromChain);
331
357
  if (!areSame) {
332
358
  this.log.debug(`Contract archive: ${fromChain.toString('hex')}`);
@@ -339,7 +365,19 @@ export class L1Publisher implements L2BlockReceiver {
339
365
  try {
340
366
  const size = Object.values(submitProofArgs).reduce((acc, arg) => acc + arg.length, 0);
341
367
  this.log.info(`SubmitProof size=${size} bytes`);
342
- return await this.txSender.sendSubmitProofTx(submitProofArgs);
368
+
369
+ const { header, archive, proverId, aggregationObject, proof } = submitProofArgs;
370
+ const args = [
371
+ `0x${header.toString('hex')}`,
372
+ `0x${archive.toString('hex')}`,
373
+ `0x${proverId.toString('hex')}`,
374
+ `0x${aggregationObject.toString('hex')}`,
375
+ `0x${proof.toString('hex')}`,
376
+ ] as const;
377
+
378
+ return await this.rollupContract.write.submitBlockRootProof(args, {
379
+ account: this.account,
380
+ });
343
381
  } catch (err) {
344
382
  this.log.error(`Rollup submit proof failed`, err);
345
383
  return undefined;
@@ -350,7 +388,11 @@ export class L1Publisher implements L2BlockReceiver {
350
388
  while (!this.interrupted) {
351
389
  try {
352
390
  this.log.info(`TxEffects size=${encodedBody.length} bytes`);
353
- return await this.txSender.sendPublishTx(encodedBody);
391
+ const args = [`0x${encodedBody.toString('hex')}`] as const;
392
+
393
+ return await this.availabilityOracleContract.write.publish(args, {
394
+ account: this.account,
395
+ });
354
396
  } catch (err) {
355
397
  this.log.error(`TxEffects publish failed`, err);
356
398
  return undefined;
@@ -361,7 +403,29 @@ export class L1Publisher implements L2BlockReceiver {
361
403
  private async sendProcessTx(encodedData: L1ProcessArgs): Promise<string | undefined> {
362
404
  while (!this.interrupted) {
363
405
  try {
364
- return await this.txSender.sendProcessTx(encodedData);
406
+ if (encodedData.attestations) {
407
+ const attestations = encodedData.attestations.map(attest => attest.toViemSignature());
408
+ const args = [
409
+ `0x${encodedData.header.toString('hex')}`,
410
+ `0x${encodedData.archive.toString('hex')}`,
411
+ `0x${encodedData.blockHash.toString('hex')}`,
412
+ attestations,
413
+ ] as const;
414
+
415
+ return await this.rollupContract.write.process(args, {
416
+ account: this.account,
417
+ });
418
+ } else {
419
+ const args = [
420
+ `0x${encodedData.header.toString('hex')}`,
421
+ `0x${encodedData.archive.toString('hex')}`,
422
+ `0x${encodedData.blockHash.toString('hex')}`,
423
+ ] as const;
424
+
425
+ return await this.rollupContract.write.process(args, {
426
+ account: this.account,
427
+ });
428
+ }
365
429
  } catch (err) {
366
430
  this.log.error(`Rollup publish failed`, err);
367
431
  return undefined;
@@ -372,7 +436,32 @@ export class L1Publisher implements L2BlockReceiver {
372
436
  private async sendPublishAndProcessTx(encodedData: L1ProcessArgs): Promise<string | undefined> {
373
437
  while (!this.interrupted) {
374
438
  try {
375
- return await this.txSender.sendPublishAndProcessTx(encodedData);
439
+ // @note This is quite a sin, but I'm committing war crimes in this code already.
440
+ if (encodedData.attestations) {
441
+ const attestations = encodedData.attestations.map(attest => attest.toViemSignature());
442
+ const args = [
443
+ `0x${encodedData.header.toString('hex')}`,
444
+ `0x${encodedData.archive.toString('hex')}`,
445
+ `0x${encodedData.blockHash.toString('hex')}`,
446
+ attestations,
447
+ `0x${encodedData.body.toString('hex')}`,
448
+ ] as const;
449
+
450
+ return await this.rollupContract.write.publishAndProcess(args, {
451
+ account: this.account,
452
+ });
453
+ } else {
454
+ const args = [
455
+ `0x${encodedData.header.toString('hex')}`,
456
+ `0x${encodedData.archive.toString('hex')}`,
457
+ `0x${encodedData.blockHash.toString('hex')}`,
458
+ `0x${encodedData.body.toString('hex')}`,
459
+ ] as const;
460
+
461
+ return await this.rollupContract.write.publishAndProcess(args, {
462
+ account: this.account,
463
+ });
464
+ }
376
465
  } catch (err) {
377
466
  this.log.error(`Rollup publish failed`, err);
378
467
  return undefined;
@@ -380,10 +469,34 @@ export class L1Publisher implements L2BlockReceiver {
380
469
  }
381
470
  }
382
471
 
383
- private async getTransactionReceipt(txHash: string): Promise<MinimalTransactionReceipt | undefined> {
472
+ /**
473
+ * Returns a tx receipt if the tx has been mined.
474
+ * @param txHash - Hash of the tx to look for.
475
+ * @returns Undefined if the tx hasn't been mined yet, the receipt otherwise.
476
+ */
477
+ async getTransactionReceipt(txHash: string): Promise<MinimalTransactionReceipt | undefined> {
384
478
  while (!this.interrupted) {
385
479
  try {
386
- return await this.txSender.getTransactionReceipt(txHash);
480
+ const receipt = await this.publicClient.getTransactionReceipt({
481
+ hash: txHash as Hex,
482
+ });
483
+
484
+ if (receipt) {
485
+ if (receipt.transactionHash !== txHash) {
486
+ throw new Error(`Tx hash mismatch: ${receipt.transactionHash} !== ${txHash}`);
487
+ }
488
+
489
+ return {
490
+ status: receipt.status === 'success',
491
+ transactionHash: txHash,
492
+ gasUsed: receipt.gasUsed,
493
+ gasPrice: receipt.effectiveGasPrice,
494
+ logs: receipt.logs,
495
+ };
496
+ }
497
+
498
+ this.log.debug(`Receipt not found for tx hash ${txHash}`);
499
+ return undefined;
387
500
  } catch (err) {
388
501
  //this.log.error(`Error getting tx receipt`, err);
389
502
  await this.sleepOrInterrupted();
@@ -395,3 +508,12 @@ export class L1Publisher implements L2BlockReceiver {
395
508
  await this.interruptibleSleep.sleep(this.sleepTimeMs);
396
509
  }
397
510
  }
511
+
512
+ /**
513
+ * Returns cost of calldata usage in Ethereum.
514
+ * @param data - Calldata.
515
+ * @returns 4 for each zero byte, 16 for each nonzero.
516
+ */
517
+ function getCalldataGasUsage(data: Uint8Array) {
518
+ return data.filter(byte => byte === 0).length * 4 + data.filter(byte => byte !== 0).length * 16;
519
+ }