@aztec/ethereum 0.0.1-commit.e6bd8901 → 0.0.1-commit.ec7ac5448

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 (163) hide show
  1. package/dest/client.d.ts +10 -2
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/client.js +13 -7
  4. package/dest/config.d.ts +8 -6
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +15 -11
  7. package/dest/contracts/chain_state_override.d.ts +38 -0
  8. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  9. package/dest/contracts/chain_state_override.js +100 -0
  10. package/dest/contracts/empire_base.d.ts +3 -1
  11. package/dest/contracts/empire_base.d.ts.map +1 -1
  12. package/dest/contracts/fee_asset_handler.d.ts +1 -1
  13. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  14. package/dest/contracts/fee_asset_handler.js +2 -0
  15. package/dest/contracts/fee_asset_price_oracle.d.ts +101 -0
  16. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  17. package/dest/contracts/fee_asset_price_oracle.js +651 -0
  18. package/dest/contracts/governance.d.ts +1 -1
  19. package/dest/contracts/governance.d.ts.map +1 -1
  20. package/dest/contracts/governance.js +5 -3
  21. package/dest/contracts/governance_proposer.d.ts +3 -1
  22. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  23. package/dest/contracts/governance_proposer.js +13 -1
  24. package/dest/contracts/inbox.d.ts +3 -3
  25. package/dest/contracts/inbox.d.ts.map +1 -1
  26. package/dest/contracts/inbox.js +5 -6
  27. package/dest/contracts/index.d.ts +4 -3
  28. package/dest/contracts/index.d.ts.map +1 -1
  29. package/dest/contracts/index.js +3 -2
  30. package/dest/contracts/multicall.d.ts +51 -2
  31. package/dest/contracts/multicall.d.ts.map +1 -1
  32. package/dest/contracts/multicall.js +87 -1
  33. package/dest/contracts/registry.d.ts +3 -1
  34. package/dest/contracts/registry.d.ts.map +1 -1
  35. package/dest/contracts/registry.js +30 -1
  36. package/dest/contracts/rollup.d.ts +45 -20
  37. package/dest/contracts/rollup.d.ts.map +1 -1
  38. package/dest/contracts/rollup.js +218 -52
  39. package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +3 -4
  40. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  41. package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +18 -13
  42. package/dest/deploy_aztec_l1_contracts.d.ts +4 -8
  43. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  44. package/dest/deploy_aztec_l1_contracts.js +39 -26
  45. package/dest/deploy_l1_contract.js +3 -3
  46. package/dest/generated/l1-contracts-defaults.d.ts +2 -2
  47. package/dest/generated/l1-contracts-defaults.js +2 -2
  48. package/dest/l1_artifacts.d.ts +9382 -15156
  49. package/dest/l1_artifacts.d.ts.map +1 -1
  50. package/dest/l1_artifacts.js +9 -24
  51. package/dest/l1_contract_addresses.d.ts +1 -5
  52. package/dest/l1_contract_addresses.d.ts.map +1 -1
  53. package/dest/l1_contract_addresses.js +0 -6
  54. package/dest/l1_reader.d.ts +3 -1
  55. package/dest/l1_reader.d.ts.map +1 -1
  56. package/dest/l1_reader.js +6 -1
  57. package/dest/l1_tx_utils/config.d.ts +7 -1
  58. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  59. package/dest/l1_tx_utils/config.js +14 -1
  60. package/dest/l1_tx_utils/constants.d.ts +1 -1
  61. package/dest/l1_tx_utils/constants.js +2 -2
  62. package/dest/l1_tx_utils/factory.d.ts +18 -10
  63. package/dest/l1_tx_utils/factory.d.ts.map +1 -1
  64. package/dest/l1_tx_utils/factory.js +17 -7
  65. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +1 -1
  66. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +1 -1
  67. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
  68. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
  69. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
  70. package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
  71. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
  72. package/dest/l1_tx_utils/index-blobs.js +2 -2
  73. package/dest/l1_tx_utils/index.d.ts +2 -1
  74. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  75. package/dest/l1_tx_utils/index.js +1 -0
  76. package/dest/l1_tx_utils/l1_tx_utils.d.ts +20 -7
  77. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  78. package/dest/l1_tx_utils/l1_tx_utils.js +81 -57
  79. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
  80. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  81. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +11 -7
  82. package/dest/l1_tx_utils/tx_delayer.d.ts +56 -0
  83. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  84. package/dest/{test → l1_tx_utils}/tx_delayer.js +62 -34
  85. package/dest/publisher_manager.d.ts +21 -6
  86. package/dest/publisher_manager.d.ts.map +1 -1
  87. package/dest/publisher_manager.js +81 -7
  88. package/dest/queries.js +3 -3
  89. package/dest/test/chain_monitor.d.ts +22 -3
  90. package/dest/test/chain_monitor.d.ts.map +1 -1
  91. package/dest/test/chain_monitor.js +33 -2
  92. package/dest/test/eth_cheat_codes.d.ts +6 -4
  93. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  94. package/dest/test/eth_cheat_codes.js +6 -4
  95. package/dest/test/index.d.ts +1 -3
  96. package/dest/test/index.d.ts.map +1 -1
  97. package/dest/test/index.js +0 -2
  98. package/dest/test/rollup_cheat_codes.d.ts +7 -1
  99. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  100. package/dest/test/rollup_cheat_codes.js +18 -1
  101. package/dest/test/start_anvil.d.ts +23 -3
  102. package/dest/test/start_anvil.d.ts.map +1 -1
  103. package/dest/test/start_anvil.js +143 -29
  104. package/dest/test/upgrade_utils.js +2 -2
  105. package/dest/utils.d.ts +2 -1
  106. package/dest/utils.d.ts.map +1 -1
  107. package/dest/utils.js +62 -12
  108. package/package.json +5 -7
  109. package/src/client.ts +10 -2
  110. package/src/config.ts +24 -15
  111. package/src/contracts/chain_state_override.ts +147 -0
  112. package/src/contracts/empire_base.ts +2 -0
  113. package/src/contracts/fee_asset_handler.ts +2 -0
  114. package/src/contracts/fee_asset_price_oracle.ts +280 -0
  115. package/src/contracts/governance.ts +5 -3
  116. package/src/contracts/governance_proposer.ts +10 -1
  117. package/src/contracts/inbox.ts +4 -4
  118. package/src/contracts/index.ts +3 -2
  119. package/src/contracts/multicall.ts +70 -3
  120. package/src/contracts/registry.ts +31 -1
  121. package/src/contracts/rollup.ts +242 -51
  122. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +16 -14
  123. package/src/deploy_aztec_l1_contracts.ts +61 -34
  124. package/src/deploy_l1_contract.ts +3 -3
  125. package/src/generated/l1-contracts-defaults.ts +2 -2
  126. package/src/l1_artifacts.ts +12 -35
  127. package/src/l1_contract_addresses.ts +0 -7
  128. package/src/l1_reader.ts +13 -1
  129. package/src/l1_tx_utils/config.ts +20 -0
  130. package/src/l1_tx_utils/constants.ts +2 -2
  131. package/src/l1_tx_utils/factory.ts +31 -31
  132. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +1 -1
  133. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +1 -1
  134. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
  135. package/src/l1_tx_utils/index-blobs.ts +2 -2
  136. package/src/l1_tx_utils/index.ts +1 -0
  137. package/src/l1_tx_utils/l1_tx_utils.ts +74 -36
  138. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +11 -7
  139. package/src/{test → l1_tx_utils}/tx_delayer.ts +78 -50
  140. package/src/publisher_manager.ts +107 -10
  141. package/src/queries.ts +3 -3
  142. package/src/test/chain_monitor.ts +60 -3
  143. package/src/test/eth_cheat_codes.ts +6 -4
  144. package/src/test/index.ts +0 -2
  145. package/src/test/rollup_cheat_codes.ts +20 -1
  146. package/src/test/start_anvil.ts +177 -29
  147. package/src/test/upgrade_utils.ts +2 -2
  148. package/src/utils.ts +70 -14
  149. package/dest/contracts/empire_slashing_proposer.d.ts +0 -67
  150. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  151. package/dest/contracts/empire_slashing_proposer.js +0 -209
  152. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  153. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  154. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  155. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  156. package/dest/test/delayed_tx_utils.d.ts +0 -13
  157. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  158. package/dest/test/delayed_tx_utils.js +0 -28
  159. package/dest/test/tx_delayer.d.ts +0 -36
  160. package/dest/test/tx_delayer.d.ts.map +0 -1
  161. package/src/contracts/empire_slashing_proposer.ts +0 -270
  162. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  163. package/src/test/delayed_tx_utils.ts +0 -52
@@ -4,6 +4,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { memoize } from '@aztec/foundation/decorators';
5
5
  import { EthAddress } from '@aztec/foundation/eth-address';
6
6
  import type { ViemSignature } from '@aztec/foundation/eth-signature';
7
+ import { createLogger } from '@aztec/foundation/log';
7
8
  import { makeBackoff, retry } from '@aztec/foundation/retry';
8
9
  import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
9
10
  import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
@@ -16,6 +17,7 @@ import {
16
17
  type Hex,
17
18
  type StateOverride,
18
19
  type WatchContractEventReturnType,
20
+ encodeAbiParameters,
19
21
  encodeFunctionData,
20
22
  getContract,
21
23
  hexToBigInt,
@@ -29,11 +31,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
29
31
  import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
30
32
  import type { ViemClient } from '../types.js';
31
33
  import { formatViemError } from '../utils.js';
32
- import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
33
34
  import { GSEContract } from './gse.js';
34
35
  import type { L1EventLog } from './log.js';
35
36
  import { SlasherContract } from './slasher_contract.js';
36
- import { TallySlashingProposerContract } from './tally_slashing_proposer.js';
37
+ import { SlashingProposerContract } from './slashing_proposer.js';
37
38
  import { checkBlockTag } from './utils.js';
38
39
 
39
40
  export type ViemCommitteeAttestation = {
@@ -55,7 +56,6 @@ export type L1RollupContractAddresses = Pick<
55
56
  | 'feeJuiceAddress'
56
57
  | 'stakingAssetAddress'
57
58
  | 'rewardDistributorAddress'
58
- | 'slashFactoryAddress'
59
59
  | 'gseAddress'
60
60
  >;
61
61
 
@@ -85,12 +85,6 @@ export type ViemGasFees = {
85
85
  feePerL2Gas: bigint;
86
86
  };
87
87
 
88
- export enum SlashingProposerType {
89
- None = 0,
90
- Tally = 1,
91
- Empire = 2,
92
- }
93
-
94
88
  /**
95
89
  * Status of a validator/attester in the staking system.
96
90
  * Matches the Status enum in StakingLib.sol
@@ -134,6 +128,14 @@ export type L1FeeData = {
134
128
  blobFee: bigint;
135
129
  };
136
130
 
131
+ /** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
132
+ export type ManaMinFeeComponents = {
133
+ sequencerCost: bigint;
134
+ proverCost: bigint;
135
+ congestionCost: bigint;
136
+ congestionMultiplier: bigint;
137
+ };
138
+
137
139
  /**
138
140
  * Reward configuration for the rollup
139
141
  */
@@ -193,10 +195,10 @@ export type CheckpointProposedArgs = {
193
195
  checkpointNumber: CheckpointNumber;
194
196
  archive: Fr;
195
197
  versionedBlobHashes: Buffer[];
196
- /** Hash of attestations. Undefined for older events (backwards compatibility). */
197
- attestationsHash?: Buffer32;
198
- /** Digest of the payload. Undefined for older events (backwards compatibility). */
199
- payloadDigest?: Buffer32;
198
+ /** Hash of attestations emitted in the CheckpointProposed event. */
199
+ attestationsHash: Buffer32;
200
+ /** Digest of the payload emitted in the CheckpointProposed event. */
201
+ payloadDigest: Buffer32;
200
202
  };
201
203
 
202
204
  /** Log type for CheckpointProposed events. */
@@ -204,6 +206,7 @@ export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
204
206
 
205
207
  export class RollupContract {
206
208
  private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
209
+ private readonly logger = createLogger('ethereum:rollup');
207
210
 
208
211
  private static cachedStfStorageSlot: Hex | undefined;
209
212
  private cachedEscapeHatch?: {
@@ -259,34 +262,18 @@ export class RollupContract {
259
262
  return this.rollup;
260
263
  }
261
264
 
262
- public async getSlashingProposer(): Promise<
263
- EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
264
- > {
265
+ public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
265
266
  const slasher = await this.getSlasherContract();
266
267
  if (!slasher) {
267
268
  return undefined;
268
269
  }
269
270
 
270
271
  const proposerAddress = await slasher.getProposer();
271
- const proposerAbi = [
272
- {
273
- type: 'function',
274
- name: 'SLASHING_PROPOSER_TYPE',
275
- inputs: [],
276
- outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
277
- stateMutability: 'view',
278
- },
279
- ] as const;
280
-
281
- const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
282
- const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
283
- if (proposerType === SlashingProposerType.Tally.valueOf()) {
284
- return new TallySlashingProposerContract(this.client, proposerAddress);
285
- } else if (proposerType === SlashingProposerType.Empire.valueOf()) {
286
- return new EmpireSlashingProposerContract(this.client, proposerAddress);
287
- } else {
288
- throw new Error(`Unknown slashing proposer type: ${proposerType}`);
272
+ if (proposerAddress.isZero()) {
273
+ return undefined;
289
274
  }
275
+
276
+ return new SlashingProposerContract(this.client, proposerAddress);
290
277
  }
291
278
 
292
279
  @memoize
@@ -379,6 +366,20 @@ export class RollupContract {
379
366
  return Fr.fromString(await this.rollup.read.archiveAt([0n]));
380
367
  }
381
368
 
369
+ @memoize
370
+ async getVkTreeRoot(): Promise<Fr> {
371
+ const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
372
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
373
+ return Fr.fromString(value ?? '0x0');
374
+ }
375
+
376
+ @memoize
377
+ async getProtocolContractsHash(): Promise<Fr> {
378
+ const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
379
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
380
+ return Fr.fromString(value ?? '0x0');
381
+ }
382
+
382
383
  /**
383
384
  * Returns rollup constants used for epoch queries.
384
385
  * Return type is `L1RollupConstants` which is defined in stdlib,
@@ -391,13 +392,25 @@ export class RollupContract {
391
392
  slotDuration: number;
392
393
  epochDuration: number;
393
394
  proofSubmissionEpochs: number;
395
+ targetCommitteeSize: number;
396
+ rollupManaLimit: number;
394
397
  }> {
395
- const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs] = await Promise.all([
398
+ const [
399
+ l1StartBlock,
400
+ l1GenesisTime,
401
+ slotDuration,
402
+ epochDuration,
403
+ proofSubmissionEpochs,
404
+ targetCommitteeSize,
405
+ rollupManaLimit,
406
+ ] = await Promise.all([
396
407
  this.getL1StartBlock(),
397
408
  this.getL1GenesisTime(),
398
409
  this.getSlotDuration(),
399
410
  this.getEpochDuration(),
400
411
  this.getProofSubmissionEpochs(),
412
+ this.getTargetCommitteeSize(),
413
+ this.getManaLimit(),
401
414
  ]);
402
415
  return {
403
416
  l1StartBlock,
@@ -405,6 +418,8 @@ export class RollupContract {
405
418
  slotDuration,
406
419
  epochDuration: Number(epochDuration),
407
420
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
421
+ targetCommitteeSize,
422
+ rollupManaLimit: Number(rollupManaLimit),
408
423
  };
409
424
  }
410
425
 
@@ -459,7 +474,11 @@ export class RollupContract {
459
474
 
460
475
  const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
461
476
  return isOpen;
462
- } catch {
477
+ } catch (err) {
478
+ this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
479
+ epoch: Number(epoch),
480
+ error: err,
481
+ });
463
482
  return false;
464
483
  }
465
484
  }
@@ -499,8 +518,9 @@ export class RollupContract {
499
518
  return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber());
500
519
  }
501
520
 
502
- async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
503
- return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber());
521
+ async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
522
+ await checkBlockTag(options?.blockNumber, this.client);
523
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
504
524
  }
505
525
 
506
526
  async getSlotNumber(): Promise<SlotNumber> {
@@ -741,14 +761,13 @@ export class RollupContract {
741
761
  * timestamp of the next L1 block
742
762
  * @throws otherwise
743
763
  */
744
- public async canProposeAtNextEthBlock(
764
+ public async canProposeAt(
745
765
  archive: Buffer,
746
766
  account: `0x${string}` | Account,
747
- slotDuration: number,
748
- opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
767
+ timestamp: bigint,
768
+ stateOverride: StateOverride = [],
749
769
  ): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
750
- const latestBlock = await this.client.getBlock();
751
- const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
770
+ const timeOfNextL1Slot = timestamp;
752
771
  const who = typeof account === 'string' ? account : account.address;
753
772
 
754
773
  try {
@@ -760,7 +779,7 @@ export class RollupContract {
760
779
  functionName: 'canProposeAtTime',
761
780
  args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
762
781
  account,
763
- stateOverride: await this.makePendingCheckpointNumberOverride(opts.forcePendingCheckpointNumber),
782
+ stateOverride,
764
783
  });
765
784
 
766
785
  return {
@@ -796,6 +815,151 @@ export class RollupContract {
796
815
  ];
797
816
  }
798
817
 
818
+ /**
819
+ * Returns a state override that sets tempCheckpointLogs[checkpointNumber].feeHeader to the compressed fee header.
820
+ * Used when simulating a propose call where the parent checkpoint hasn't landed on L1 yet (pipelining).
821
+ */
822
+ public async makeFeeHeaderOverride(checkpointNumber: CheckpointNumber, feeHeader: FeeHeader): Promise<StateOverride> {
823
+ const { epochDuration, proofSubmissionEpochs } = await this.getRollupConstants();
824
+ const roundaboutSize = BigInt(epochDuration * (proofSubmissionEpochs + 1) + 1);
825
+ const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
826
+
827
+ // tempCheckpointLogs is at offset 2 in RollupStore
828
+ const tempCheckpointLogsMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 2n;
829
+
830
+ // Solidity mapping slot: keccak256(abi.encode(key, baseSlot))
831
+ const structBaseSlot = hexToBigInt(
832
+ keccak256(
833
+ encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsMappingBase]),
834
+ ),
835
+ );
836
+
837
+ // feeHeader is the 7th field (offset 6) in CompressedTempCheckpointLog
838
+ const feeHeaderSlot = structBaseSlot + 6n;
839
+ const compressed = RollupContract.compressFeeHeader(feeHeader);
840
+
841
+ return [
842
+ {
843
+ address: this.address,
844
+ stateDiff: [
845
+ {
846
+ slot: `0x${feeHeaderSlot.toString(16).padStart(64, '0')}`,
847
+ value: `0x${compressed.toString(16).padStart(64, '0')}`,
848
+ },
849
+ ],
850
+ },
851
+ ];
852
+ }
853
+
854
+ /**
855
+ * Returns a state override that sets archives[checkpointNumber] to the given archive value.
856
+ * Used when simulating a canProposeAtTime call where the local archive differs from L1
857
+ * (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
858
+ */
859
+ public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
860
+ const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
861
+ const archiveSlot = hexToBigInt(
862
+ keccak256(
863
+ encodeAbiParameters(
864
+ [{ type: 'uint256' }, { type: 'uint256' }],
865
+ [BigInt(checkpointNumber), archivesMappingBase],
866
+ ),
867
+ ),
868
+ );
869
+ return [
870
+ {
871
+ address: this.address,
872
+ stateDiff: [
873
+ {
874
+ slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
875
+ value: archive.toString(),
876
+ },
877
+ ],
878
+ },
879
+ ];
880
+ }
881
+
882
+ /** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
883
+ public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
884
+ type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
885
+ const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
886
+ for (const override of overrides) {
887
+ for (const entry of override) {
888
+ const key = entry.address.toLowerCase();
889
+ const existing = byAddress.get(key);
890
+ if (existing) {
891
+ existing.stateDiff.push(...(entry.stateDiff ?? []));
892
+ if (entry.balance !== undefined) {
893
+ existing.balance = entry.balance;
894
+ }
895
+ } else {
896
+ byAddress.set(key, {
897
+ address: entry.address,
898
+ balance: entry.balance,
899
+ stateDiff: [...(entry.stateDiff ?? [])],
900
+ });
901
+ }
902
+ }
903
+ }
904
+ return [...byAddress.values()];
905
+ }
906
+
907
+ /** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
908
+ public static compressFeeHeader(feeHeader: FeeHeader): bigint {
909
+ const MASK_48_BITS = (1n << 48n) - 1n;
910
+ const MASK_64_BITS = (1n << 64n) - 1n;
911
+ const MASK_63_BITS = (1n << 63n) - 1n;
912
+
913
+ let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
914
+ value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
915
+ value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
916
+ value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
917
+ value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
918
+ value |= 1n << 255n; // preheat flag
919
+ return value;
920
+ }
921
+
922
+ /** Computes the fee header for a child checkpoint given parent fee header and child data.
923
+ * Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
924
+ public static computeChildFeeHeader(
925
+ parentFeeHeader: FeeHeader,
926
+ childManaUsed: bigint,
927
+ feeAssetPriceModifier: bigint,
928
+ manaTarget: bigint,
929
+ ): FeeHeader {
930
+ const MIN_ETH_PER_FEE_ASSET = 100n;
931
+ const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
932
+
933
+ // excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
934
+ const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
935
+ const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
936
+
937
+ // ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
938
+ const parentPrice =
939
+ parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
940
+ let newPrice: bigint;
941
+ if (feeAssetPriceModifier >= 0n) {
942
+ newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
943
+ } else {
944
+ const absMod = -feeAssetPriceModifier;
945
+ newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
946
+ }
947
+ if (newPrice < MIN_ETH_PER_FEE_ASSET) {
948
+ newPrice = MIN_ETH_PER_FEE_ASSET;
949
+ }
950
+ if (newPrice > MAX_ETH_PER_FEE_ASSET) {
951
+ newPrice = MAX_ETH_PER_FEE_ASSET;
952
+ }
953
+
954
+ return {
955
+ excessMana,
956
+ manaUsed: childManaUsed,
957
+ ethPerFeeAsset: newPrice,
958
+ congestionCost: 0n,
959
+ proverCost: 0n,
960
+ };
961
+ }
962
+
799
963
  /** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
800
964
  public buildInvalidateBadAttestationRequest(
801
965
  checkpointNumber: CheckpointNumber,
@@ -805,6 +969,7 @@ export class RollupContract {
805
969
  ): L1TxRequest {
806
970
  return {
807
971
  to: this.address,
972
+ abi: RollupAbi,
808
973
  data: encodeFunctionData({
809
974
  abi: RollupAbi,
810
975
  functionName: 'invalidateBadAttestation',
@@ -826,6 +991,7 @@ export class RollupContract {
826
991
  ): L1TxRequest {
827
992
  return {
828
993
  to: this.address,
994
+ abi: RollupAbi,
829
995
  data: encodeFunctionData({
830
996
  abi: RollupAbi,
831
997
  functionName: 'invalidateInsufficientAttestations',
@@ -842,8 +1008,18 @@ export class RollupContract {
842
1008
  return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
843
1009
  }
844
1010
 
845
- getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
846
- return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
1011
+ getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
1012
+ return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
1013
+ }
1014
+
1015
+ async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
1016
+ const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
1017
+ return {
1018
+ sequencerCost: result.sequencerCost,
1019
+ proverCost: result.proverCost,
1020
+ congestionCost: result.congestionCost,
1021
+ congestionMultiplier: result.congestionMultiplier,
1022
+ };
847
1023
  }
848
1024
 
849
1025
  async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
@@ -889,11 +1065,10 @@ export class RollupContract {
889
1065
  return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
890
1066
  }
891
1067
 
892
- async getAttesters(): Promise<EthAddress[]> {
1068
+ async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
893
1069
  const attesterSize = await this.getActiveAttesterCount();
894
1070
  const gse = new GSEContract(this.client, await this.getGSE());
895
- const ts = (await this.client.getBlock()).timestamp;
896
-
1071
+ const ts = timestamp ?? (await this.client.getBlock()).timestamp;
897
1072
  const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
898
1073
  const chunks = chunk(indices, 1000);
899
1074
 
@@ -959,6 +1134,7 @@ export class RollupContract {
959
1134
  setupEpoch(l1TxUtils: L1TxUtils) {
960
1135
  return l1TxUtils.sendAndMonitorTransaction({
961
1136
  to: this.address,
1137
+ abi: RollupAbi,
962
1138
  data: encodeFunctionData({
963
1139
  abi: RollupAbi,
964
1140
  functionName: 'setupEpoch',
@@ -970,6 +1146,7 @@ export class RollupContract {
970
1146
  vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
971
1147
  return l1TxUtils.sendAndMonitorTransaction({
972
1148
  to: this.address,
1149
+ abi: RollupAbi,
973
1150
  data: encodeFunctionData({
974
1151
  abi: RollupAbi,
975
1152
  functionName: 'vote',
@@ -1052,8 +1229,22 @@ export class RollupContract {
1052
1229
  checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
1053
1230
  archive: Fr.fromString(log.args.archive!),
1054
1231
  versionedBlobHashes: log.args.versionedBlobHashes!.map(h => Buffer.from(h.slice(2), 'hex')),
1055
- attestationsHash: log.args.attestationsHash ? Buffer32.fromString(log.args.attestationsHash) : undefined,
1056
- payloadDigest: log.args.payloadDigest ? Buffer32.fromString(log.args.payloadDigest) : undefined,
1232
+ attestationsHash: (() => {
1233
+ if (!log.args.attestationsHash) {
1234
+ throw new Error(
1235
+ `CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`,
1236
+ );
1237
+ }
1238
+ return Buffer32.fromString(log.args.attestationsHash);
1239
+ })(),
1240
+ payloadDigest: (() => {
1241
+ if (!log.args.payloadDigest) {
1242
+ throw new Error(
1243
+ `CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`,
1244
+ );
1245
+ }
1246
+ return Buffer32.fromString(log.args.payloadDigest);
1247
+ })(),
1057
1248
  },
1058
1249
  }));
1059
1250
  }
@@ -1,12 +1,13 @@
1
1
  import type { L1TxRequest } from '@aztec/ethereum/l1-tx-utils';
2
2
  import type { ViemClient } from '@aztec/ethereum/types';
3
- import { tryExtractEvent } from '@aztec/ethereum/utils';
3
+ import { mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
4
4
  import { SlotNumber } from '@aztec/foundation/branded-types';
5
5
  import { Buffer32 } from '@aztec/foundation/buffer';
6
6
  import { EthAddress } from '@aztec/foundation/eth-address';
7
7
  import { Signature } from '@aztec/foundation/eth-signature';
8
8
  import { hexToBuffer } from '@aztec/foundation/string';
9
- import { TallySlashingProposerAbi } from '@aztec/l1-artifacts/TallySlashingProposerAbi';
9
+ import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
10
+ import { SlashingProposerAbi } from '@aztec/l1-artifacts/SlashingProposerAbi';
10
11
 
11
12
  import {
12
13
  type GetContractReturnType,
@@ -18,13 +19,11 @@ import {
18
19
  } from 'viem';
19
20
 
20
21
  /**
21
- * Wrapper around the TallySlashingProposer contract that provides
22
+ * Wrapper around the SlashingProposer contract that provides
22
23
  * a TypeScript interface for interacting with the consensus-based slashing system.
23
24
  */
24
- export class TallySlashingProposerContract {
25
- private readonly contract: GetContractReturnType<typeof TallySlashingProposerAbi, ViemClient>;
26
-
27
- public readonly type = 'tally' as const;
25
+ export class SlashingProposerContract {
26
+ private readonly contract: GetContractReturnType<typeof SlashingProposerAbi, ViemClient>;
28
27
 
29
28
  constructor(
30
29
  public readonly client: ViemClient,
@@ -32,7 +31,7 @@ export class TallySlashingProposerContract {
32
31
  ) {
33
32
  this.contract = getContract({
34
33
  address: typeof address === 'string' ? address : address.toString(),
35
- abi: TallySlashingProposerAbi,
34
+ abi: SlashingProposerAbi,
36
35
  client,
37
36
  });
38
37
  }
@@ -135,12 +134,12 @@ export class TallySlashingProposerContract {
135
134
 
136
135
  /** Tries to extract a VoteCast event from the given logs. */
137
136
  public tryExtractVoteCastEvent(logs: Log[]) {
138
- return tryExtractEvent(logs, this.address.toString(), TallySlashingProposerAbi, 'VoteCast');
137
+ return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'VoteCast');
139
138
  }
140
139
 
141
140
  /** Tries to extract a RoundExecuted event from the given logs. */
142
141
  public tryExtractRoundExecutedEvent(logs: Log[]) {
143
- return tryExtractEvent(logs, this.address.toString(), TallySlashingProposerAbi, 'RoundExecuted');
142
+ return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'RoundExecuted');
144
143
  }
145
144
 
146
145
  /**
@@ -160,8 +159,9 @@ export class TallySlashingProposerContract {
160
159
 
161
160
  return {
162
161
  to: this.contract.address,
162
+ abi: SlashingProposerAbi,
163
163
  data: encodeFunctionData({
164
- abi: TallySlashingProposerAbi,
164
+ abi: SlashingProposerAbi,
165
165
  functionName: 'vote',
166
166
  args: [votes, signature.toViemSignature()],
167
167
  }),
@@ -171,7 +171,7 @@ export class TallySlashingProposerContract {
171
171
  /** Returns the typed data definition to EIP712-sign for voting */
172
172
  public buildVoteTypedData(votes: Hex, slot: SlotNumber): TypedDataDefinition {
173
173
  const domain = {
174
- name: 'TallySlashingProposer',
174
+ name: 'SlashingProposer',
175
175
  version: '1',
176
176
  chainId: this.client.chain.id,
177
177
  verifyingContract: this.contract.address,
@@ -207,8 +207,9 @@ export class TallySlashingProposerContract {
207
207
  public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
208
208
  return {
209
209
  to: this.contract.address,
210
+ abi: SlashingProposerAbi,
210
211
  data: encodeFunctionData({
211
- abi: TallySlashingProposerAbi,
212
+ abi: SlashingProposerAbi,
212
213
  functionName: 'vote',
213
214
  args: [votes, signature],
214
215
  }),
@@ -224,8 +225,9 @@ export class TallySlashingProposerContract {
224
225
  public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
225
226
  return {
226
227
  to: this.contract.address,
228
+ abi: mergeAbis([SlashingProposerAbi, SlasherAbi]),
227
229
  data: encodeFunctionData({
228
- abi: TallySlashingProposerAbi,
230
+ abi: SlashingProposerAbi,
229
231
  functionName: 'executeRound',
230
232
  args: [round, committees.map(c => c.map(addr => addr.toString()))],
231
233
  }),