@aztec/ethereum 0.0.1-commit.5de5ca79e → 0.0.1-commit.6201a7b05

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 (83) hide show
  1. package/dest/client.d.ts +7 -1
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/client.js +14 -0
  4. package/dest/config.d.ts +6 -6
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +5 -9
  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/inbox.d.ts +3 -3
  11. package/dest/contracts/inbox.d.ts.map +1 -1
  12. package/dest/contracts/inbox.js +11 -6
  13. package/dest/contracts/index.d.ts +3 -3
  14. package/dest/contracts/index.d.ts.map +1 -1
  15. package/dest/contracts/index.js +2 -2
  16. package/dest/contracts/multicall.d.ts +51 -2
  17. package/dest/contracts/multicall.d.ts.map +1 -1
  18. package/dest/contracts/multicall.js +85 -0
  19. package/dest/contracts/rollup.d.ts +4996 -20
  20. package/dest/contracts/rollup.d.ts.map +1 -1
  21. package/dest/contracts/rollup.js +257 -55
  22. package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +3 -4
  23. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  24. package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +13 -15
  25. package/dest/deploy_aztec_l1_contracts.d.ts +3 -6
  26. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  27. package/dest/deploy_aztec_l1_contracts.js +2 -4
  28. package/dest/generated/l1-contracts-defaults.d.ts +1 -1
  29. package/dest/generated/l1-contracts-defaults.js +1 -1
  30. package/dest/l1_artifacts.d.ts +8675 -16072
  31. package/dest/l1_artifacts.d.ts.map +1 -1
  32. package/dest/l1_artifacts.js +9 -24
  33. package/dest/l1_contract_addresses.d.ts +1 -5
  34. package/dest/l1_contract_addresses.d.ts.map +1 -1
  35. package/dest/l1_contract_addresses.js +0 -6
  36. package/dest/l1_reader.js +1 -1
  37. package/dest/l1_tx_utils/l1_tx_utils.d.ts +3 -1
  38. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  39. package/dest/l1_tx_utils/l1_tx_utils.js +34 -26
  40. package/dest/publisher_manager.d.ts +21 -7
  41. package/dest/publisher_manager.d.ts.map +1 -1
  42. package/dest/publisher_manager.js +81 -7
  43. package/dest/queries.d.ts +13 -2
  44. package/dest/queries.d.ts.map +1 -1
  45. package/dest/queries.js +62 -3
  46. package/dest/test/chain_monitor.d.ts +2 -1
  47. package/dest/test/chain_monitor.d.ts.map +1 -1
  48. package/dest/test/chain_monitor.js +5 -0
  49. package/dest/test/eth_cheat_codes.d.ts +6 -4
  50. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  51. package/dest/test/eth_cheat_codes.js +6 -4
  52. package/dest/test/rollup_cheat_codes.d.ts +5 -1
  53. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  54. package/dest/test/rollup_cheat_codes.js +9 -1
  55. package/dest/test/start_anvil.d.ts +8 -1
  56. package/dest/test/start_anvil.d.ts.map +1 -1
  57. package/dest/test/start_anvil.js +16 -2
  58. package/package.json +5 -5
  59. package/src/client.ts +18 -0
  60. package/src/config.ts +9 -13
  61. package/src/contracts/chain_state_override.ts +147 -0
  62. package/src/contracts/inbox.ts +12 -4
  63. package/src/contracts/index.ts +2 -2
  64. package/src/contracts/multicall.ts +65 -1
  65. package/src/contracts/rollup.ts +274 -52
  66. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +14 -16
  67. package/src/deploy_aztec_l1_contracts.ts +1 -5
  68. package/src/generated/l1-contracts-defaults.ts +1 -1
  69. package/src/l1_artifacts.ts +12 -35
  70. package/src/l1_contract_addresses.ts +0 -7
  71. package/src/l1_reader.ts +1 -1
  72. package/src/l1_tx_utils/l1_tx_utils.ts +24 -14
  73. package/src/publisher_manager.ts +105 -10
  74. package/src/queries.ts +70 -4
  75. package/src/test/chain_monitor.ts +5 -0
  76. package/src/test/eth_cheat_codes.ts +6 -4
  77. package/src/test/rollup_cheat_codes.ts +11 -1
  78. package/src/test/start_anvil.ts +25 -2
  79. package/dest/contracts/empire_slashing_proposer.d.ts +0 -69
  80. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  81. package/dest/contracts/empire_slashing_proposer.js +0 -216
  82. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  83. package/src/contracts/empire_slashing_proposer.ts +0 -265
@@ -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';
@@ -14,8 +15,10 @@ import {
14
15
  type Account,
15
16
  type GetContractReturnType,
16
17
  type Hex,
18
+ type Log,
17
19
  type StateOverride,
18
20
  type WatchContractEventReturnType,
21
+ encodeAbiParameters,
19
22
  encodeFunctionData,
20
23
  getContract,
21
24
  hexToBigInt,
@@ -29,11 +32,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
29
32
  import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
30
33
  import type { ViemClient } from '../types.js';
31
34
  import { formatViemError } from '../utils.js';
32
- import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
33
35
  import { GSEContract } from './gse.js';
34
36
  import type { L1EventLog } from './log.js';
35
37
  import { SlasherContract } from './slasher_contract.js';
36
- import { TallySlashingProposerContract } from './tally_slashing_proposer.js';
38
+ import { SlashingProposerContract } from './slashing_proposer.js';
37
39
  import { checkBlockTag } from './utils.js';
38
40
 
39
41
  export type ViemCommitteeAttestation = {
@@ -55,7 +57,6 @@ export type L1RollupContractAddresses = Pick<
55
57
  | 'feeJuiceAddress'
56
58
  | 'stakingAssetAddress'
57
59
  | 'rewardDistributorAddress'
58
- | 'slashFactoryAddress'
59
60
  | 'gseAddress'
60
61
  >;
61
62
 
@@ -85,12 +86,6 @@ export type ViemGasFees = {
85
86
  feePerL2Gas: bigint;
86
87
  };
87
88
 
88
- export enum SlashingProposerType {
89
- None = 0,
90
- Tally = 1,
91
- Empire = 2,
92
- }
93
-
94
89
  /**
95
90
  * Status of a validator/attester in the staking system.
96
91
  * Matches the Status enum in StakingLib.sol
@@ -134,6 +129,17 @@ export type L1FeeData = {
134
129
  blobFee: bigint;
135
130
  };
136
131
 
132
+ /** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */
133
+ export enum TempCheckpointLogField {
134
+ HeaderHash = 0,
135
+ BlobCommitmentsHash = 1,
136
+ OutHash = 2,
137
+ AttestationsHash = 3,
138
+ PayloadDigest = 4,
139
+ SlotNumber = 5,
140
+ FeeHeader = 6,
141
+ }
142
+
137
143
  /** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
138
144
  export type ManaMinFeeComponents = {
139
145
  sequencerCost: bigint;
@@ -212,6 +218,7 @@ export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
212
218
 
213
219
  export class RollupContract {
214
220
  private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
221
+ private readonly logger = createLogger('ethereum:rollup');
215
222
 
216
223
  private static cachedStfStorageSlot: Hex | undefined;
217
224
  private cachedEscapeHatch?: {
@@ -267,34 +274,18 @@ export class RollupContract {
267
274
  return this.rollup;
268
275
  }
269
276
 
270
- public async getSlashingProposer(): Promise<
271
- EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
272
- > {
277
+ public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
273
278
  const slasher = await this.getSlasherContract();
274
279
  if (!slasher) {
275
280
  return undefined;
276
281
  }
277
282
 
278
283
  const proposerAddress = await slasher.getProposer();
279
- const proposerAbi = [
280
- {
281
- type: 'function',
282
- name: 'SLASHING_PROPOSER_TYPE',
283
- inputs: [],
284
- outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
285
- stateMutability: 'view',
286
- },
287
- ] as const;
288
-
289
- const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
290
- const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
291
- if (proposerType === SlashingProposerType.Tally.valueOf()) {
292
- return new TallySlashingProposerContract(this.client, proposerAddress);
293
- } else if (proposerType === SlashingProposerType.Empire.valueOf()) {
294
- return new EmpireSlashingProposerContract(this.client, proposerAddress);
295
- } else {
296
- throw new Error(`Unknown slashing proposer type: ${proposerType}`);
284
+ if (proposerAddress.isZero()) {
285
+ return undefined;
297
286
  }
287
+
288
+ return new SlashingProposerContract(this.client, proposerAddress);
298
289
  }
299
290
 
300
291
  @memoize
@@ -495,7 +486,11 @@ export class RollupContract {
495
486
 
496
487
  const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
497
488
  return isOpen;
498
- } catch {
489
+ } catch (err) {
490
+ this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
491
+ epoch: Number(epoch),
492
+ error: err,
493
+ });
499
494
  return false;
500
495
  }
501
496
  }
@@ -531,8 +526,9 @@ export class RollupContract {
531
526
  return this.rollup.read.getCheckpointReward();
532
527
  }
533
528
 
534
- async getCheckpointNumber(): Promise<CheckpointNumber> {
535
- return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber());
529
+ async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
530
+ await checkBlockTag(options?.blockNumber, this.client);
531
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
536
532
  }
537
533
 
538
534
  async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
@@ -540,18 +536,31 @@ export class RollupContract {
540
536
  return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
541
537
  }
542
538
 
543
- async getSlotNumber(): Promise<SlotNumber> {
544
- return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot());
539
+ async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
540
+ await checkBlockTag(options?.blockNumber, this.client);
541
+ return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
545
542
  }
546
543
 
547
- async getL1FeesAt(timestamp: bigint): Promise<L1FeeData> {
548
- const result = await this.rollup.read.getL1FeesAt([timestamp]);
544
+ async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
545
+ await checkBlockTag(options?.blockNumber, this.client);
546
+ const result = await this.rollup.read.getL1FeesAt([timestamp], options);
549
547
  return {
550
548
  baseFee: result.baseFee,
551
549
  blobFee: result.blobFee,
552
550
  };
553
551
  }
554
552
 
553
+ async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
554
+ const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
555
+ return {
556
+ excessMana: result.excessMana,
557
+ manaUsed: result.manaUsed,
558
+ ethPerFeeAsset: result.ethPerFeeAsset,
559
+ congestionCost: result.congestionCost,
560
+ proverCost: result.proverCost,
561
+ };
562
+ }
563
+
555
564
  getEthPerFeeAsset(): Promise<bigint> {
556
565
  return this.rollup.read.getEthPerFeeAsset();
557
566
  }
@@ -626,8 +635,9 @@ export class RollupContract {
626
635
  return EthAddress.fromString(result);
627
636
  }
628
637
 
629
- async getCheckpoint(checkpointNumber: CheckpointNumber): Promise<CheckpointLog> {
630
- const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)]);
638
+ async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
639
+ await checkBlockTag(options?.blockNumber, this.client);
640
+ const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
631
641
  return {
632
642
  archive: Fr.fromString(result.archive),
633
643
  headerHash: Buffer32.fromString(result.headerHash),
@@ -660,6 +670,30 @@ export class RollupContract {
660
670
  );
661
671
  }
662
672
 
673
+ /**
674
+ * Returns the effective pending checkpoint, accounting for potential prunes.
675
+ * When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
676
+ * This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
677
+ * @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
678
+ * @param options - Optional L1 block number to pin the queries to.
679
+ */
680
+ getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
681
+ return retry(
682
+ async () => {
683
+ const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
684
+ const canPrune = await this.canPruneAtTime(timestamp, options);
685
+ if (canPrune) {
686
+ const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
687
+ return await this.getCheckpoint(provenCheckpointNumber, options);
688
+ }
689
+ const pendingCheckpointNumber = await this.getCheckpointNumber(options);
690
+ return await this.getCheckpoint(pendingCheckpointNumber, options);
691
+ },
692
+ 'getting effective pending checkpoint',
693
+ makeBackoff([0.5, 0.5, 0.5]),
694
+ );
695
+ }
696
+
663
697
  async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
664
698
  const { pending, proven } = await this.rollup.read.getTips();
665
699
  return {
@@ -781,12 +815,10 @@ export class RollupContract {
781
815
  public async canProposeAt(
782
816
  archive: Buffer,
783
817
  account: `0x${string}` | Account,
784
- slotDuration: bigint,
785
- slotOffset: bigint,
786
- opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
818
+ timestamp: bigint,
819
+ stateOverride: StateOverride = [],
787
820
  ): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
788
- const latestBlock = await this.client.getBlock();
789
- const timeOfNextL1Slot = latestBlock.timestamp + slotDuration + slotOffset;
821
+ const timeOfNextL1Slot = timestamp;
790
822
  const who = typeof account === 'string' ? account : account.address;
791
823
 
792
824
  try {
@@ -798,7 +830,7 @@ export class RollupContract {
798
830
  functionName: 'canProposeAtTime',
799
831
  args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
800
832
  account,
801
- stateOverride: await this.makePendingCheckpointNumberOverride(opts.forcePendingCheckpointNumber),
833
+ stateOverride,
802
834
  });
803
835
 
804
836
  return {
@@ -834,6 +866,151 @@ export class RollupContract {
834
866
  ];
835
867
  }
836
868
 
869
+ /**
870
+ * Returns a state override that sets tempCheckpointLogs[checkpointNumber].feeHeader to the compressed fee header.
871
+ * Used when simulating a propose call where the parent checkpoint hasn't landed on L1 yet (pipelining).
872
+ */
873
+ public async makeFeeHeaderOverride(checkpointNumber: CheckpointNumber, feeHeader: FeeHeader): Promise<StateOverride> {
874
+ const { epochDuration, proofSubmissionEpochs } = await this.getRollupConstants();
875
+ const roundaboutSize = BigInt(epochDuration * (proofSubmissionEpochs + 1) + 1);
876
+ const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
877
+
878
+ // tempCheckpointLogs is at offset 2 in RollupStore
879
+ const tempCheckpointLogsMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 2n;
880
+
881
+ // Solidity mapping slot: keccak256(abi.encode(key, baseSlot))
882
+ const structBaseSlot = hexToBigInt(
883
+ keccak256(
884
+ encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsMappingBase]),
885
+ ),
886
+ );
887
+
888
+ // feeHeader is the 7th field (offset 6) in CompressedTempCheckpointLog
889
+ const feeHeaderSlot = structBaseSlot + 6n;
890
+ const compressed = RollupContract.compressFeeHeader(feeHeader);
891
+
892
+ return [
893
+ {
894
+ address: this.address,
895
+ stateDiff: [
896
+ {
897
+ slot: `0x${feeHeaderSlot.toString(16).padStart(64, '0')}`,
898
+ value: `0x${compressed.toString(16).padStart(64, '0')}`,
899
+ },
900
+ ],
901
+ },
902
+ ];
903
+ }
904
+
905
+ /**
906
+ * Returns a state override that sets archives[checkpointNumber] to the given archive value.
907
+ * Used when simulating a canProposeAtTime call where the local archive differs from L1
908
+ * (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
909
+ */
910
+ public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
911
+ const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
912
+ const archiveSlot = hexToBigInt(
913
+ keccak256(
914
+ encodeAbiParameters(
915
+ [{ type: 'uint256' }, { type: 'uint256' }],
916
+ [BigInt(checkpointNumber), archivesMappingBase],
917
+ ),
918
+ ),
919
+ );
920
+ return [
921
+ {
922
+ address: this.address,
923
+ stateDiff: [
924
+ {
925
+ slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
926
+ value: archive.toString(),
927
+ },
928
+ ],
929
+ },
930
+ ];
931
+ }
932
+
933
+ /** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
934
+ public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
935
+ type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
936
+ const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
937
+ for (const override of overrides) {
938
+ for (const entry of override) {
939
+ const key = entry.address.toLowerCase();
940
+ const existing = byAddress.get(key);
941
+ if (existing) {
942
+ existing.stateDiff.push(...(entry.stateDiff ?? []));
943
+ if (entry.balance !== undefined) {
944
+ existing.balance = entry.balance;
945
+ }
946
+ } else {
947
+ byAddress.set(key, {
948
+ address: entry.address,
949
+ balance: entry.balance,
950
+ stateDiff: [...(entry.stateDiff ?? [])],
951
+ });
952
+ }
953
+ }
954
+ }
955
+ return [...byAddress.values()];
956
+ }
957
+
958
+ /** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
959
+ public static compressFeeHeader(feeHeader: FeeHeader): bigint {
960
+ const MASK_48_BITS = (1n << 48n) - 1n;
961
+ const MASK_64_BITS = (1n << 64n) - 1n;
962
+ const MASK_63_BITS = (1n << 63n) - 1n;
963
+
964
+ let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
965
+ value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
966
+ value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
967
+ value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
968
+ value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
969
+ value |= 1n << 255n; // preheat flag
970
+ return value;
971
+ }
972
+
973
+ /** Computes the fee header for a child checkpoint given parent fee header and child data.
974
+ * Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
975
+ public static computeChildFeeHeader(
976
+ parentFeeHeader: FeeHeader,
977
+ childManaUsed: bigint,
978
+ feeAssetPriceModifier: bigint,
979
+ manaTarget: bigint,
980
+ ): FeeHeader {
981
+ const MIN_ETH_PER_FEE_ASSET = 100n;
982
+ const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
983
+
984
+ // excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
985
+ const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
986
+ const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
987
+
988
+ // ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
989
+ const parentPrice =
990
+ parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
991
+ let newPrice: bigint;
992
+ if (feeAssetPriceModifier >= 0n) {
993
+ newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
994
+ } else {
995
+ const absMod = -feeAssetPriceModifier;
996
+ newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
997
+ }
998
+ if (newPrice < MIN_ETH_PER_FEE_ASSET) {
999
+ newPrice = MIN_ETH_PER_FEE_ASSET;
1000
+ }
1001
+ if (newPrice > MAX_ETH_PER_FEE_ASSET) {
1002
+ newPrice = MAX_ETH_PER_FEE_ASSET;
1003
+ }
1004
+
1005
+ return {
1006
+ excessMana,
1007
+ manaUsed: childManaUsed,
1008
+ ethPerFeeAsset: newPrice,
1009
+ congestionCost: 0n,
1010
+ proverCost: 0n,
1011
+ };
1012
+ }
1013
+
837
1014
  /** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
838
1015
  public buildInvalidateBadAttestationRequest(
839
1016
  checkpointNumber: CheckpointNumber,
@@ -882,8 +1059,8 @@ export class RollupContract {
882
1059
  return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
883
1060
  }
884
1061
 
885
- getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean): Promise<bigint> {
886
- return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset]);
1062
+ getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
1063
+ return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
887
1064
  }
888
1065
 
889
1066
  async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
@@ -939,11 +1116,10 @@ export class RollupContract {
939
1116
  return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
940
1117
  }
941
1118
 
942
- async getAttesters(): Promise<EthAddress[]> {
1119
+ async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
943
1120
  const attesterSize = await this.getActiveAttesterCount();
944
1121
  const gse = new GSEContract(this.client, await this.getGSE());
945
- const ts = (await this.client.getBlock()).timestamp;
946
-
1122
+ const ts = timestamp ?? (await this.client.getBlock()).timestamp;
947
1123
  const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
948
1124
  const chunks = chunk(indices, 1000);
949
1125
 
@@ -1049,7 +1225,7 @@ export class RollupContract {
1049
1225
  }
1050
1226
 
1051
1227
  public listenToCheckpointInvalidated(
1052
- callback: (args: { checkpointNumber: CheckpointNumber }) => unknown,
1228
+ callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
1053
1229
  ): WatchContractEventReturnType {
1054
1230
  return this.rollup.watchEvent.CheckpointInvalidated(
1055
1231
  {},
@@ -1058,7 +1234,7 @@ export class RollupContract {
1058
1234
  for (const log of logs) {
1059
1235
  const args = log.args;
1060
1236
  if (args.checkpointNumber !== undefined) {
1061
- callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber) });
1237
+ callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber), event: log });
1062
1238
  }
1063
1239
  }
1064
1240
  },
@@ -1091,6 +1267,17 @@ export class RollupContract {
1091
1267
  );
1092
1268
  }
1093
1269
 
1270
+ /**
1271
+ * Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
1272
+ * The Rollup inherits from Ownable and emits this event in its constructor, so the event
1273
+ * is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
1274
+ * probe to detect RPC nodes that prune historical logs.
1275
+ */
1276
+ async getOwnershipTransferredEventsAtDeploy() {
1277
+ const l1StartBlock = await this.getL1StartBlock();
1278
+ return await this.rollup.getEvents.OwnershipTransferred({}, { fromBlock: l1StartBlock, toBlock: l1StartBlock });
1279
+ }
1280
+
1094
1281
  /** Fetches CheckpointProposed events within the given block range. */
1095
1282
  async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
1096
1283
  const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
@@ -1123,4 +1310,39 @@ export class RollupContract {
1123
1310
  },
1124
1311
  }));
1125
1312
  }
1313
+
1314
+ /** Packs pending and proven checkpoint numbers into the chain tips storage format. */
1315
+ static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
1316
+ return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
1317
+ }
1318
+
1319
+ /** Storage slot for the chain tips (offset 0 within the STF storage struct). */
1320
+ static get chainTipsStorageSlot(): bigint {
1321
+ return BigInt(RollupContract.stfStorageSlot);
1322
+ }
1323
+
1324
+ /**
1325
+ * Computes the storage slot for a field within a tempCheckpointLog entry.
1326
+ * @param checkpointNumber - The checkpoint number
1327
+ * @param field - The field within the CompressedTempCheckpointLog struct
1328
+ */
1329
+ async getTempCheckpointLogStorageSlot(
1330
+ checkpointNumber: CheckpointNumber,
1331
+ field: TempCheckpointLogField,
1332
+ ): Promise<bigint> {
1333
+ const fieldOffset = BigInt(field);
1334
+ const [epochDuration, proofSubmissionEpochs] = await Promise.all([
1335
+ this.getEpochDuration(),
1336
+ this.getProofSubmissionEpochs(),
1337
+ ]);
1338
+ const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
1339
+ const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
1340
+ const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
1341
+ const entryBase = BigInt(
1342
+ keccak256(
1343
+ encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
1344
+ ),
1345
+ );
1346
+ return entryBase + fieldOffset;
1347
+ }
1126
1348
  }
@@ -7,7 +7,7 @@ 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
9
  import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
10
- import { TallySlashingProposerAbi } from '@aztec/l1-artifacts/TallySlashingProposerAbi';
10
+ import { SlashingProposerAbi } from '@aztec/l1-artifacts/SlashingProposerAbi';
11
11
 
12
12
  import {
13
13
  type GetContractReturnType,
@@ -19,13 +19,11 @@ import {
19
19
  } from 'viem';
20
20
 
21
21
  /**
22
- * Wrapper around the TallySlashingProposer contract that provides
22
+ * Wrapper around the SlashingProposer contract that provides
23
23
  * a TypeScript interface for interacting with the consensus-based slashing system.
24
24
  */
25
- export class TallySlashingProposerContract {
26
- private readonly contract: GetContractReturnType<typeof TallySlashingProposerAbi, ViemClient>;
27
-
28
- public readonly type = 'tally' as const;
25
+ export class SlashingProposerContract {
26
+ private readonly contract: GetContractReturnType<typeof SlashingProposerAbi, ViemClient>;
29
27
 
30
28
  constructor(
31
29
  public readonly client: ViemClient,
@@ -33,7 +31,7 @@ export class TallySlashingProposerContract {
33
31
  ) {
34
32
  this.contract = getContract({
35
33
  address: typeof address === 'string' ? address : address.toString(),
36
- abi: TallySlashingProposerAbi,
34
+ abi: SlashingProposerAbi,
37
35
  client,
38
36
  });
39
37
  }
@@ -136,12 +134,12 @@ export class TallySlashingProposerContract {
136
134
 
137
135
  /** Tries to extract a VoteCast event from the given logs. */
138
136
  public tryExtractVoteCastEvent(logs: Log[]) {
139
- return tryExtractEvent(logs, this.address.toString(), TallySlashingProposerAbi, 'VoteCast');
137
+ return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'VoteCast');
140
138
  }
141
139
 
142
140
  /** Tries to extract a RoundExecuted event from the given logs. */
143
141
  public tryExtractRoundExecutedEvent(logs: Log[]) {
144
- return tryExtractEvent(logs, this.address.toString(), TallySlashingProposerAbi, 'RoundExecuted');
142
+ return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'RoundExecuted');
145
143
  }
146
144
 
147
145
  /**
@@ -161,9 +159,9 @@ export class TallySlashingProposerContract {
161
159
 
162
160
  return {
163
161
  to: this.contract.address,
164
- abi: TallySlashingProposerAbi,
162
+ abi: SlashingProposerAbi,
165
163
  data: encodeFunctionData({
166
- abi: TallySlashingProposerAbi,
164
+ abi: SlashingProposerAbi,
167
165
  functionName: 'vote',
168
166
  args: [votes, signature.toViemSignature()],
169
167
  }),
@@ -173,7 +171,7 @@ export class TallySlashingProposerContract {
173
171
  /** Returns the typed data definition to EIP712-sign for voting */
174
172
  public buildVoteTypedData(votes: Hex, slot: SlotNumber): TypedDataDefinition {
175
173
  const domain = {
176
- name: 'TallySlashingProposer',
174
+ name: 'SlashingProposer',
177
175
  version: '1',
178
176
  chainId: this.client.chain.id,
179
177
  verifyingContract: this.contract.address,
@@ -209,9 +207,9 @@ export class TallySlashingProposerContract {
209
207
  public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
210
208
  return {
211
209
  to: this.contract.address,
212
- abi: TallySlashingProposerAbi,
210
+ abi: SlashingProposerAbi,
213
211
  data: encodeFunctionData({
214
- abi: TallySlashingProposerAbi,
212
+ abi: SlashingProposerAbi,
215
213
  functionName: 'vote',
216
214
  args: [votes, signature],
217
215
  }),
@@ -227,9 +225,9 @@ export class TallySlashingProposerContract {
227
225
  public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
228
226
  return {
229
227
  to: this.contract.address,
230
- abi: mergeAbis([TallySlashingProposerAbi, SlasherAbi]),
228
+ abi: mergeAbis([SlashingProposerAbi, SlasherAbi]),
231
229
  data: encodeFunctionData({
232
- abi: TallySlashingProposerAbi,
230
+ abi: SlashingProposerAbi,
233
231
  functionName: 'executeRound',
234
232
  args: [round, committees.map(c => c.map(addr => addr.toString()))],
235
233
  }),
@@ -230,7 +230,6 @@ export function computeValidatorData(operator: Operator): ValidatorJson {
230
230
  export interface RollupUpgradeAddresses {
231
231
  rollupAddress: string;
232
232
  verifierAddress: string;
233
- slashFactoryAddress: string;
234
233
  inboxAddress: string;
235
234
  outboxAddress: string;
236
235
  feeJuicePortalAddress: string;
@@ -243,7 +242,6 @@ export interface RollupUpgradeAddresses {
243
242
  export interface ForgeRollupUpgradeResult {
244
243
  rollupAddress: Hex;
245
244
  verifierAddress: Hex;
246
- slashFactoryAddress: Hex;
247
245
  inboxAddress: Hex;
248
246
  outboxAddress: Hex;
249
247
  feeJuicePortalAddress: Hex;
@@ -403,7 +401,6 @@ export async function deployAztecL1Contracts(
403
401
  governanceProposerAddress: EthAddress.fromString(result.governanceProposerAddress),
404
402
  governanceAddress: EthAddress.fromString(result.governanceAddress),
405
403
  stakingAssetAddress: EthAddress.fromString(result.stakingAssetAddress),
406
- slashFactoryAddress: result.slashFactoryAddress ? EthAddress.fromString(result.slashFactoryAddress) : undefined,
407
404
  feeAssetHandlerAddress: result.feeAssetHandlerAddress
408
405
  ? EthAddress.fromString(result.feeAssetHandlerAddress)
409
406
  : undefined,
@@ -585,7 +582,7 @@ export function getDeployRollupForUpgradeEnvVars(
585
582
  AZTEC_EXIT_DELAY_SECONDS: args.exitDelaySeconds.toString(),
586
583
  AZTEC_PROVING_COST_PER_MANA: args.provingCostPerMana.toString(),
587
584
  AZTEC_INITIAL_ETH_PER_FEE_ASSET: args.initialEthPerFeeAsset.toString(),
588
- AZTEC_SLASHER_FLAVOR: args.slasherFlavor,
585
+ AZTEC_SLASHER_ENABLED: args.slasherEnabled ? 'true' : 'false',
589
586
  AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: args.slashingRoundSizeInEpochs.toString(),
590
587
  AZTEC_SLASHING_QUORUM: args.slashingQuorum?.toString(),
591
588
  AZTEC_SLASHING_OFFSET_IN_ROUNDS: args.slashingOffsetInRounds.toString(),
@@ -645,6 +642,5 @@ export const deployRollupForUpgrade = async (
645
642
 
646
643
  return {
647
644
  rollup,
648
- slashFactoryAddress: result.slashFactoryAddress,
649
645
  };
650
646
  };
@@ -18,7 +18,7 @@ export const l1ContractsDefaultEnv = {
18
18
  AZTEC_MANA_TARGET: 100000000,
19
19
  AZTEC_PROVING_COST_PER_MANA: 100,
20
20
  AZTEC_INITIAL_ETH_PER_FEE_ASSET: 10000000,
21
- AZTEC_SLASHER_FLAVOR: 'tally',
21
+ AZTEC_SLASHER_ENABLED: true,
22
22
  AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: 4,
23
23
  AZTEC_SLASHING_LIFETIME_IN_ROUNDS: 5,
24
24
  AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS: 0,