@aztec/ethereum 0.0.1-commit.96bb3f7 → 0.0.1-commit.993d240

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 (172) hide show
  1. package/dest/client.d.ts +16 -2
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/client.js +27 -7
  4. package/dest/config.d.ts +22 -33
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +59 -64
  7. package/dest/contracts/chain_state_override.d.ts +78 -0
  8. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  9. package/dest/contracts/chain_state_override.js +137 -0
  10. package/dest/contracts/fee_asset_handler.d.ts +1 -1
  11. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  12. package/dest/contracts/fee_asset_handler.js +2 -0
  13. package/dest/contracts/fee_asset_price_oracle.d.ts +105 -0
  14. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  15. package/dest/contracts/fee_asset_price_oracle.js +656 -0
  16. package/dest/contracts/governance.d.ts +110 -33
  17. package/dest/contracts/governance.d.ts.map +1 -1
  18. package/dest/contracts/governance.js +207 -16
  19. package/dest/contracts/governance_proposer.d.ts +33 -1
  20. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  21. package/dest/contracts/governance_proposer.js +57 -3
  22. package/dest/contracts/inbox.d.ts +18 -1
  23. package/dest/contracts/inbox.d.ts.map +1 -1
  24. package/dest/contracts/inbox.js +38 -2
  25. package/dest/contracts/index.d.ts +5 -3
  26. package/dest/contracts/index.d.ts.map +1 -1
  27. package/dest/contracts/index.js +4 -2
  28. package/dest/contracts/log.d.ts +13 -0
  29. package/dest/contracts/log.d.ts.map +1 -0
  30. package/dest/contracts/log.js +1 -0
  31. package/dest/contracts/multicall.d.ts +127 -12
  32. package/dest/contracts/multicall.d.ts.map +1 -1
  33. package/dest/contracts/multicall.js +201 -93
  34. package/dest/contracts/outbox.d.ts +21 -4
  35. package/dest/contracts/outbox.d.ts.map +1 -1
  36. package/dest/contracts/outbox.js +29 -6
  37. package/dest/contracts/registry.d.ts +3 -1
  38. package/dest/contracts/registry.d.ts.map +1 -1
  39. package/dest/contracts/registry.js +30 -1
  40. package/dest/contracts/rollup.d.ts +5288 -27
  41. package/dest/contracts/rollup.d.ts.map +1 -1
  42. package/dest/contracts/rollup.js +432 -69
  43. package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +3 -4
  44. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  45. package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +18 -13
  46. package/dest/deploy_aztec_l1_contracts.d.ts +9 -8
  47. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  48. package/dest/deploy_aztec_l1_contracts.js +58 -32
  49. package/dest/deploy_l1_contract.js +3 -3
  50. package/dest/generated/l1-contracts-defaults.d.ts +30 -0
  51. package/dest/generated/l1-contracts-defaults.d.ts.map +1 -0
  52. package/dest/generated/l1-contracts-defaults.js +30 -0
  53. package/dest/l1_artifacts.d.ts +15699 -16241
  54. package/dest/l1_artifacts.d.ts.map +1 -1
  55. package/dest/l1_artifacts.js +9 -24
  56. package/dest/l1_contract_addresses.d.ts +61 -65
  57. package/dest/l1_contract_addresses.d.ts.map +1 -1
  58. package/dest/l1_contract_addresses.js +101 -82
  59. package/dest/l1_reader.d.ts +5 -5
  60. package/dest/l1_reader.d.ts.map +1 -1
  61. package/dest/l1_reader.js +9 -7
  62. package/dest/l1_tx_utils/config.d.ts +7 -1
  63. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  64. package/dest/l1_tx_utils/config.js +14 -1
  65. package/dest/l1_tx_utils/constants.d.ts +1 -1
  66. package/dest/l1_tx_utils/constants.js +2 -2
  67. package/dest/l1_tx_utils/factory.d.ts +18 -10
  68. package/dest/l1_tx_utils/factory.d.ts.map +1 -1
  69. package/dest/l1_tx_utils/factory.js +17 -7
  70. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +1 -1
  71. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +1 -1
  72. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
  73. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
  74. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
  75. package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
  76. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
  77. package/dest/l1_tx_utils/index-blobs.js +2 -2
  78. package/dest/l1_tx_utils/index.d.ts +2 -1
  79. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  80. package/dest/l1_tx_utils/index.js +1 -0
  81. package/dest/l1_tx_utils/l1_tx_utils.d.ts +26 -7
  82. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  83. package/dest/l1_tx_utils/l1_tx_utils.js +101 -66
  84. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
  85. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  86. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +11 -7
  87. package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
  88. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  89. package/dest/{test → l1_tx_utils}/tx_delayer.js +67 -37
  90. package/dest/publisher_manager.d.ts +21 -6
  91. package/dest/publisher_manager.d.ts.map +1 -1
  92. package/dest/publisher_manager.js +81 -7
  93. package/dest/queries.d.ts +14 -3
  94. package/dest/queries.d.ts.map +1 -1
  95. package/dest/queries.js +66 -4
  96. package/dest/test/chain_monitor.d.ts +23 -3
  97. package/dest/test/chain_monitor.d.ts.map +1 -1
  98. package/dest/test/chain_monitor.js +38 -2
  99. package/dest/test/eth_cheat_codes.d.ts +24 -4
  100. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  101. package/dest/test/eth_cheat_codes.js +17 -4
  102. package/dest/test/index.d.ts +1 -3
  103. package/dest/test/index.d.ts.map +1 -1
  104. package/dest/test/index.js +0 -2
  105. package/dest/test/rollup_cheat_codes.d.ts +34 -4
  106. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  107. package/dest/test/rollup_cheat_codes.js +69 -8
  108. package/dest/test/start_anvil.d.ts +23 -3
  109. package/dest/test/start_anvil.d.ts.map +1 -1
  110. package/dest/test/start_anvil.js +143 -29
  111. package/dest/test/upgrade_utils.js +2 -2
  112. package/dest/utils.d.ts +9 -1
  113. package/dest/utils.d.ts.map +1 -1
  114. package/dest/utils.js +81 -12
  115. package/package.json +9 -10
  116. package/src/client.ts +28 -2
  117. package/src/config.ts +75 -65
  118. package/src/contracts/README.md +157 -0
  119. package/src/contracts/chain_state_override.ts +200 -0
  120. package/src/contracts/fee_asset_handler.ts +2 -0
  121. package/src/contracts/fee_asset_price_oracle.ts +285 -0
  122. package/src/contracts/governance.ts +279 -13
  123. package/src/contracts/governance_proposer.ts +58 -2
  124. package/src/contracts/inbox.ts +57 -2
  125. package/src/contracts/index.ts +4 -2
  126. package/src/contracts/log.ts +13 -0
  127. package/src/contracts/multicall.ts +243 -102
  128. package/src/contracts/outbox.ts +38 -8
  129. package/src/contracts/registry.ts +31 -1
  130. package/src/contracts/rollup.ts +500 -68
  131. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +16 -14
  132. package/src/deploy_aztec_l1_contracts.ts +81 -40
  133. package/src/deploy_l1_contract.ts +3 -3
  134. package/src/generated/l1-contracts-defaults.ts +32 -0
  135. package/src/l1_artifacts.ts +12 -35
  136. package/src/l1_contract_addresses.ts +120 -94
  137. package/src/l1_reader.ts +17 -10
  138. package/src/l1_tx_utils/config.ts +20 -0
  139. package/src/l1_tx_utils/constants.ts +2 -2
  140. package/src/l1_tx_utils/factory.ts +31 -31
  141. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +1 -1
  142. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +1 -1
  143. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
  144. package/src/l1_tx_utils/index-blobs.ts +2 -2
  145. package/src/l1_tx_utils/index.ts +1 -0
  146. package/src/l1_tx_utils/l1_tx_utils.ts +99 -48
  147. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +11 -7
  148. package/src/{test → l1_tx_utils}/tx_delayer.ts +93 -56
  149. package/src/publisher_manager.ts +107 -10
  150. package/src/queries.ts +73 -5
  151. package/src/test/chain_monitor.ts +65 -3
  152. package/src/test/eth_cheat_codes.ts +16 -4
  153. package/src/test/index.ts +0 -2
  154. package/src/test/rollup_cheat_codes.ts +75 -8
  155. package/src/test/start_anvil.ts +177 -29
  156. package/src/test/upgrade_utils.ts +2 -2
  157. package/src/utils.ts +88 -14
  158. package/dest/contracts/empire_slashing_proposer.d.ts +0 -67
  159. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  160. package/dest/contracts/empire_slashing_proposer.js +0 -209
  161. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  162. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  163. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  164. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  165. package/dest/test/delayed_tx_utils.d.ts +0 -13
  166. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  167. package/dest/test/delayed_tx_utils.js +0 -28
  168. package/dest/test/tx_delayer.d.ts +0 -36
  169. package/dest/test/tx_delayer.d.ts.map +0 -1
  170. package/src/contracts/empire_slashing_proposer.ts +0 -270
  171. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  172. package/src/test/delayed_tx_utils.ts +0 -52
@@ -376,24 +376,19 @@ import { Buffer32 } from '@aztec/foundation/buffer';
376
376
  import { Fr } from '@aztec/foundation/curves/bn254';
377
377
  import { memoize } from '@aztec/foundation/decorators';
378
378
  import { EthAddress } from '@aztec/foundation/eth-address';
379
+ import { createLogger } from '@aztec/foundation/log';
379
380
  import { makeBackoff, retry } from '@aztec/foundation/retry';
381
+ import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
380
382
  import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
381
383
  import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
382
384
  import chunk from 'lodash.chunk';
383
- import { encodeFunctionData, getContract, hexToBigInt, keccak256 } from 'viem';
385
+ import { encodeAbiParameters, encodeFunctionData, getContract, hexToBigInt, keccak256 } from 'viem';
384
386
  import { getPublicClient } from '../client.js';
385
387
  import { formatViemError } from '../utils.js';
386
- import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
387
388
  import { GSEContract } from './gse.js';
388
389
  import { SlasherContract } from './slasher_contract.js';
389
- import { TallySlashingProposerContract } from './tally_slashing_proposer.js';
390
+ import { SlashingProposerContract } from './slashing_proposer.js';
390
391
  import { checkBlockTag } from './utils.js';
391
- export var SlashingProposerType = /*#__PURE__*/ function(SlashingProposerType) {
392
- SlashingProposerType[SlashingProposerType["None"] = 0] = "None";
393
- SlashingProposerType[SlashingProposerType["Tally"] = 1] = "Tally";
394
- SlashingProposerType[SlashingProposerType["Empire"] = 2] = "Empire";
395
- return SlashingProposerType;
396
- }({});
397
392
  /**
398
393
  * Status of a validator/attester in the staking system.
399
394
  * Matches the Status enum in StakingLib.sol
@@ -404,6 +399,16 @@ export var SlashingProposerType = /*#__PURE__*/ function(SlashingProposerType) {
404
399
  AttesterStatus[AttesterStatus["EXITING"] = 3] = "EXITING";
405
400
  return AttesterStatus;
406
401
  }({});
402
+ /** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */ export var TempCheckpointLogField = /*#__PURE__*/ function(TempCheckpointLogField) {
403
+ TempCheckpointLogField[TempCheckpointLogField["HeaderHash"] = 0] = "HeaderHash";
404
+ TempCheckpointLogField[TempCheckpointLogField["BlobCommitmentsHash"] = 1] = "BlobCommitmentsHash";
405
+ TempCheckpointLogField[TempCheckpointLogField["OutHash"] = 2] = "OutHash";
406
+ TempCheckpointLogField[TempCheckpointLogField["AttestationsHash"] = 3] = "AttestationsHash";
407
+ TempCheckpointLogField[TempCheckpointLogField["PayloadDigest"] = 4] = "PayloadDigest";
408
+ TempCheckpointLogField[TempCheckpointLogField["SlotNumber"] = 5] = "SlotNumber";
409
+ TempCheckpointLogField[TempCheckpointLogField["FeeHeader"] = 6] = "FeeHeader";
410
+ return TempCheckpointLogField;
411
+ }({});
407
412
  export class RollupContract {
408
413
  client;
409
414
  static{
@@ -498,6 +503,16 @@ export class RollupContract {
498
503
  2,
499
504
  "getGenesisArchiveTreeRoot"
500
505
  ],
506
+ [
507
+ memoize,
508
+ 2,
509
+ "getVkTreeRoot"
510
+ ],
511
+ [
512
+ memoize,
513
+ 2,
514
+ "getProtocolContractsHash"
515
+ ],
501
516
  [
502
517
  memoize,
503
518
  2,
@@ -506,7 +521,9 @@ export class RollupContract {
506
521
  ], []));
507
522
  }
508
523
  rollup;
524
+ logger;
509
525
  static cachedStfStorageSlot;
526
+ cachedEscapeHatch;
510
527
  static get checkBlobStorageSlot() {
511
528
  const asString = RollupStorage.find((storage)=>storage.label === 'checkBlob')?.slot;
512
529
  if (asString === undefined) {
@@ -523,12 +540,12 @@ export class RollupContract {
523
540
  }
524
541
  static getFromConfig(config) {
525
542
  const client = getPublicClient(config);
526
- const address = config.l1Contracts.rollupAddress.toString();
543
+ const address = config.rollupAddress.toString();
527
544
  return new RollupContract(client, address);
528
545
  }
529
546
  constructor(client, address){
530
547
  this.client = client;
531
- _initProto(this);
548
+ this.logger = (_initProto(this), createLogger('ethereum:rollup'));
532
549
  if (address instanceof EthAddress) {
533
550
  address = address.toString();
534
551
  }
@@ -553,34 +570,10 @@ export class RollupContract {
553
570
  return undefined;
554
571
  }
555
572
  const proposerAddress = await slasher.getProposer();
556
- const proposerAbi = [
557
- {
558
- type: 'function',
559
- name: 'SLASHING_PROPOSER_TYPE',
560
- inputs: [],
561
- outputs: [
562
- {
563
- name: '',
564
- type: 'uint8',
565
- internalType: 'enum SlasherFlavor'
566
- }
567
- ],
568
- stateMutability: 'view'
569
- }
570
- ];
571
- const proposer = getContract({
572
- address: proposerAddress.toString(),
573
- abi: proposerAbi,
574
- client: this.client
575
- });
576
- const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
577
- if (proposerType === 1..valueOf()) {
578
- return new TallySlashingProposerContract(this.client, proposerAddress);
579
- } else if (proposerType === 2..valueOf()) {
580
- return new EmpireSlashingProposerContract(this.client, proposerAddress);
581
- } else {
582
- throw new Error(`Unknown slashing proposer type: ${proposerType}`);
573
+ if (proposerAddress.isZero()) {
574
+ return undefined;
583
575
  }
576
+ return new SlashingProposerContract(this.client, proposerAddress);
584
577
  }
585
578
  getL1StartBlock() {
586
579
  return this.rollup.read.L1_BLOCK_AT_GENESIS();
@@ -638,30 +631,98 @@ export class RollupContract {
638
631
  0n
639
632
  ]));
640
633
  }
634
+ async getVkTreeRoot() {
635
+ const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
636
+ const value = await this.client.getStorageAt({
637
+ address: this.address,
638
+ slot: `0x${slot.toString(16)}`
639
+ });
640
+ return Fr.fromString(value ?? '0x0');
641
+ }
642
+ async getProtocolContractsHash() {
643
+ const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
644
+ const value = await this.client.getStorageAt({
645
+ address: this.address,
646
+ slot: `0x${slot.toString(16)}`
647
+ });
648
+ return Fr.fromString(value ?? '0x0');
649
+ }
641
650
  /**
642
651
  * Returns rollup constants used for epoch queries.
643
652
  * Return type is `L1RollupConstants` which is defined in stdlib,
644
653
  * so we cant reference it until we move this contract to that package.
645
654
  */ async getRollupConstants() {
646
- const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs] = await Promise.all([
655
+ const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs, targetCommitteeSize, rollupManaLimit] = await Promise.all([
647
656
  this.getL1StartBlock(),
648
657
  this.getL1GenesisTime(),
649
658
  this.getSlotDuration(),
650
659
  this.getEpochDuration(),
651
- this.getProofSubmissionEpochs()
660
+ this.getProofSubmissionEpochs(),
661
+ this.getTargetCommitteeSize(),
662
+ this.getManaLimit()
652
663
  ]);
653
664
  return {
654
665
  l1StartBlock,
655
666
  l1GenesisTime,
656
667
  slotDuration,
657
668
  epochDuration: Number(epochDuration),
658
- proofSubmissionEpochs: Number(proofSubmissionEpochs)
669
+ proofSubmissionEpochs: Number(proofSubmissionEpochs),
670
+ targetCommitteeSize,
671
+ rollupManaLimit: Number(rollupManaLimit)
659
672
  };
660
673
  }
661
674
  async getSlasherAddress() {
662
675
  return EthAddress.fromString(await this.rollup.read.getSlasher());
663
676
  }
664
677
  /**
678
+ * Returns the configured escape hatch contract address, or zero if disabled.
679
+ */ async getEscapeHatchAddress() {
680
+ return EthAddress.fromString(await this.rollup.read.getEscapeHatch());
681
+ }
682
+ async getEscapeHatchContract() {
683
+ const escapeHatchAddress = await this.getEscapeHatchAddress();
684
+ if (escapeHatchAddress.isZero()) {
685
+ return undefined;
686
+ }
687
+ // Cache the viem contract wrapper since it will be used frequently.
688
+ if (!this.cachedEscapeHatch || !this.cachedEscapeHatch.address.equals(escapeHatchAddress)) {
689
+ this.cachedEscapeHatch = {
690
+ address: escapeHatchAddress,
691
+ contract: getContract({
692
+ address: escapeHatchAddress.toString(),
693
+ abi: EscapeHatchAbi,
694
+ client: this.client
695
+ })
696
+ };
697
+ }
698
+ return this.cachedEscapeHatch.contract;
699
+ }
700
+ /**
701
+ * Returns whether the escape hatch is open for the given epoch.
702
+ * If escape hatch is not configured, returns false.
703
+ *
704
+ * This function is intentionally defensive: any failure to query the escape hatch
705
+ * (RPC issues, transient errors, etc.) is treated as "closed" to avoid callers
706
+ * needing to sprinkle try/catch everywhere.
707
+ */ async isEscapeHatchOpen(epoch) {
708
+ try {
709
+ const escapeHatch = await this.getEscapeHatchContract();
710
+ if (!escapeHatch) {
711
+ return false;
712
+ }
713
+ const [isOpen] = await escapeHatch.read.isHatchOpen([
714
+ BigInt(epoch)
715
+ ]);
716
+ return isOpen;
717
+ } catch (err) {
718
+ this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
719
+ epoch: Number(epoch),
720
+ error: err
721
+ });
722
+ return false;
723
+ }
724
+ }
725
+ /**
665
726
  * Returns a SlasherContract instance for interacting with the slasher contract.
666
727
  */ async getSlasherContract() {
667
728
  const slasherAddress = await this.getSlasherAddress();
@@ -686,26 +747,42 @@ export class RollupContract {
686
747
  getCheckpointReward() {
687
748
  return this.rollup.read.getCheckpointReward();
688
749
  }
689
- async getCheckpointNumber() {
690
- return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber());
750
+ async getCheckpointNumber(options) {
751
+ await checkBlockTag(options?.blockNumber, this.client);
752
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
691
753
  }
692
- async getProvenCheckpointNumber() {
693
- return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber());
754
+ async getProvenCheckpointNumber(options) {
755
+ await checkBlockTag(options?.blockNumber, this.client);
756
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
694
757
  }
695
- async getSlotNumber() {
696
- return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot());
758
+ async getSlotNumber(options) {
759
+ await checkBlockTag(options?.blockNumber, this.client);
760
+ return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
697
761
  }
698
- async getL1FeesAt(timestamp) {
762
+ async getL1FeesAt(timestamp, options) {
763
+ await checkBlockTag(options?.blockNumber, this.client);
699
764
  const result = await this.rollup.read.getL1FeesAt([
700
765
  timestamp
701
- ]);
766
+ ], options);
702
767
  return {
703
768
  baseFee: result.baseFee,
704
769
  blobFee: result.blobFee
705
770
  };
706
771
  }
707
- getFeeAssetPerEth() {
708
- return this.rollup.read.getFeeAssetPerEth();
772
+ async getFeeHeader(checkpointNumber) {
773
+ const result = await this.rollup.read.getFeeHeader([
774
+ checkpointNumber
775
+ ]);
776
+ return {
777
+ excessMana: result.excessMana,
778
+ manaUsed: result.manaUsed,
779
+ ethPerFeeAsset: result.ethPerFeeAsset,
780
+ congestionCost: result.congestionCost,
781
+ proverCost: result.proverCost
782
+ };
783
+ }
784
+ getEthPerFeeAsset() {
785
+ return this.rollup.read.getEthPerFeeAsset();
709
786
  }
710
787
  async getCommitteeAt(timestamp) {
711
788
  const { result } = await this.client.simulateContract({
@@ -772,10 +849,11 @@ export class RollupContract {
772
849
  });
773
850
  return EthAddress.fromString(result);
774
851
  }
775
- async getCheckpoint(checkpointNumber) {
852
+ async getCheckpoint(checkpointNumber, options) {
853
+ await checkBlockTag(options?.blockNumber, this.client);
776
854
  const result = await this.rollup.read.getCheckpoint([
777
855
  BigInt(checkpointNumber)
778
- ]);
856
+ ], options);
779
857
  return {
780
858
  archive: Fr.fromString(result.archive),
781
859
  headerHash: Buffer32.fromString(result.headerHash),
@@ -786,7 +864,7 @@ export class RollupContract {
786
864
  feeHeader: {
787
865
  excessMana: result.feeHeader.excessMana,
788
866
  manaUsed: result.feeHeader.manaUsed,
789
- feeAssetPriceNumerator: result.feeHeader.feeAssetPriceNumerator,
867
+ ethPerFeeAsset: result.feeHeader.ethPerFeeAsset,
790
868
  congestionCost: result.feeHeader.congestionCost,
791
869
  proverCost: result.feeHeader.proverCost
792
870
  }
@@ -805,6 +883,28 @@ export class RollupContract {
805
883
  0.5
806
884
  ]));
807
885
  }
886
+ /**
887
+ * Returns the effective pending checkpoint, accounting for potential prunes.
888
+ * When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
889
+ * This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
890
+ * @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
891
+ * @param options - Optional L1 block number to pin the queries to.
892
+ */ getEffectivePendingCheckpoint(atTimestamp, options) {
893
+ return retry(async ()=>{
894
+ const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
895
+ const canPrune = await this.canPruneAtTime(timestamp, options);
896
+ if (canPrune) {
897
+ const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
898
+ return await this.getCheckpoint(provenCheckpointNumber, options);
899
+ }
900
+ const pendingCheckpointNumber = await this.getCheckpointNumber(options);
901
+ return await this.getCheckpoint(pendingCheckpointNumber, options);
902
+ }, 'getting effective pending checkpoint', makeBackoff([
903
+ 0.5,
904
+ 0.5,
905
+ 0.5
906
+ ]));
907
+ }
808
908
  async getTips() {
809
909
  const { pending, proven } = await this.rollup.read.getTips();
810
910
  return {
@@ -889,9 +989,8 @@ export class RollupContract {
889
989
  * @return [slot, checkpointNumber, timeOfNextL1Slot] - If you can propose, the L2 slot number, checkpoint number and
890
990
  * timestamp of the next L1 block
891
991
  * @throws otherwise
892
- */ async canProposeAtNextEthBlock(archive, account, slotDuration, opts = {}) {
893
- const latestBlock = await this.client.getBlock();
894
- const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
992
+ */ async canProposeAt(archive, account, timestamp, stateOverride = []) {
993
+ const timeOfNextL1Slot = timestamp;
895
994
  const who = typeof account === 'string' ? account : account.address;
896
995
  try {
897
996
  const { result: [slot, checkpointNumber] } = await this.client.simulateContract({
@@ -904,7 +1003,7 @@ export class RollupContract {
904
1003
  who
905
1004
  ],
906
1005
  account,
907
- stateOverride: await this.makePendingCheckpointNumberOverride(opts.forcePendingCheckpointNumber)
1006
+ stateOverride
908
1007
  });
909
1008
  return {
910
1009
  slot: SlotNumber.fromBigInt(slot),
@@ -916,11 +1015,15 @@ export class RollupContract {
916
1015
  }
917
1016
  }
918
1017
  /**
919
- * Returns a state override that sets the pending checkpoint number to the specified value. Useful for simulations.
920
- * Requires querying the current state of the contract to get the current proven checkpoint number, as they are both
921
- * stored in the same slot. If the argument is undefined, it returns an empty override.
922
- */ async makePendingCheckpointNumberOverride(forcePendingCheckpointNumber) {
923
- if (forcePendingCheckpointNumber === undefined) {
1018
+ * Returns a state override that sets the pending and/or proven checkpoint numbers. Useful for simulations.
1019
+ * Both values share a single storage slot (pending in the upper 128 bits, proven in the lower 128 bits), so
1020
+ * a single combined override is emitted to avoid the second state-diff clobbering the first. The current live
1021
+ * value is read once to preserve any half not being overridden. Returns an empty override if neither is set.
1022
+ *
1023
+ * Throws if the resulting `proven > pending`, which would crash the simulation: `STFLib.canPruneAtTime` calls
1024
+ * `getEpochForCheckpoint(proven + 1)` whenever `pending != proven`, and that asserts `_n <= tips.pending`.
1025
+ */ async makeChainTipsOverride(override) {
1026
+ if (override.pending === undefined && override.proven === undefined) {
924
1027
  return [];
925
1028
  }
926
1029
  const slot = RollupContract.stfStorageSlot;
@@ -928,8 +1031,15 @@ export class RollupContract {
928
1031
  address: this.address,
929
1032
  slot
930
1033
  });
931
- const currentProvenCheckpointNumber = currentValue ? hexToBigInt(currentValue) & (1n << 128n) - 1n : 0n;
932
- const newValue = BigInt(forcePendingCheckpointNumber) << 128n | currentProvenCheckpointNumber;
1034
+ const currentRaw = currentValue ? hexToBigInt(currentValue) : 0n;
1035
+ const currentPending = currentRaw >> 128n;
1036
+ const currentProven = currentRaw & (1n << 128n) - 1n;
1037
+ const newPending = override.pending !== undefined ? BigInt(override.pending) : currentPending;
1038
+ const newProven = override.proven !== undefined ? BigInt(override.proven) : currentProven;
1039
+ if (newProven > newPending) {
1040
+ throw new Error(`Invalid chain tips override: proven (${newProven}) > pending (${newPending})`);
1041
+ }
1042
+ const newValue = newPending << 128n | newProven;
933
1043
  return [
934
1044
  {
935
1045
  address: this.address,
@@ -942,9 +1052,165 @@ export class RollupContract {
942
1052
  }
943
1053
  ];
944
1054
  }
1055
+ /**
1056
+ * Returns a state override that patches `tempCheckpointLogs[checkpointNumber]` with every field that
1057
+ * the L1 contract reads during `propose()` for the checkpoint that builds on top of it (proposer
1058
+ * pipelining simulation). Mirrors the writes done by `ProposeLib.addTempCheckpointLog`.
1059
+ *
1060
+ * `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path
1061
+ * never asserts against them, so leaving them at storage zero is harmless.
1062
+ */ async makeTempCheckpointLogOverride(checkpointNumber, fields) {
1063
+ const constants = await this.getRollupConstants();
1064
+ const slotAt = (field)=>`0x${this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants).toString(16).padStart(64, '0')}`;
1065
+ const word = (v)=>`0x${v.toString(16).padStart(64, '0')}`;
1066
+ const stateDiff = [];
1067
+ if (fields.headerHash) {
1068
+ stateDiff.push({
1069
+ slot: slotAt(0),
1070
+ value: fields.headerHash.toString()
1071
+ });
1072
+ }
1073
+ if (fields.outHash) {
1074
+ stateDiff.push({
1075
+ slot: slotAt(2),
1076
+ value: fields.outHash.toString()
1077
+ });
1078
+ }
1079
+ if (fields.payloadDigest) {
1080
+ stateDiff.push({
1081
+ slot: slotAt(4),
1082
+ value: fields.payloadDigest.toString()
1083
+ });
1084
+ }
1085
+ if (fields.slotNumber !== undefined) {
1086
+ // CompressedSlot is uint32 on L1 (SafeCast.toUint32 reverts on overflow). Match that behavior here
1087
+ // so a malformed override surfaces immediately rather than silently truncating into a wrong slot.
1088
+ const slotNumber = BigInt(fields.slotNumber);
1089
+ if (slotNumber < 0n || slotNumber > 0xffffffffn) {
1090
+ throw new Error(`slotNumber ${slotNumber} does not fit in uint32`);
1091
+ }
1092
+ stateDiff.push({
1093
+ slot: slotAt(5),
1094
+ value: word(slotNumber)
1095
+ });
1096
+ }
1097
+ if (fields.feeHeader) {
1098
+ stateDiff.push({
1099
+ slot: slotAt(6),
1100
+ value: word(RollupContract.compressFeeHeader(fields.feeHeader))
1101
+ });
1102
+ }
1103
+ if (stateDiff.length === 0) {
1104
+ return [];
1105
+ }
1106
+ return [
1107
+ {
1108
+ address: this.address,
1109
+ stateDiff
1110
+ }
1111
+ ];
1112
+ }
1113
+ /**
1114
+ * Returns a state override that sets archives[checkpointNumber] to the given archive value.
1115
+ * Used when simulating a canProposeAtTime call where the local archive differs from L1
1116
+ * (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
1117
+ */ makeArchiveOverride(checkpointNumber, archive) {
1118
+ const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
1119
+ const archiveSlot = hexToBigInt(keccak256(encodeAbiParameters([
1120
+ {
1121
+ type: 'uint256'
1122
+ },
1123
+ {
1124
+ type: 'uint256'
1125
+ }
1126
+ ], [
1127
+ BigInt(checkpointNumber),
1128
+ archivesMappingBase
1129
+ ])));
1130
+ return [
1131
+ {
1132
+ address: this.address,
1133
+ stateDiff: [
1134
+ {
1135
+ slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
1136
+ value: archive.toString()
1137
+ }
1138
+ ]
1139
+ }
1140
+ ];
1141
+ }
1142
+ /** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */ static mergeStateOverrides(...overrides) {
1143
+ const byAddress = new Map();
1144
+ for (const override of overrides){
1145
+ for (const entry of override){
1146
+ const key = entry.address.toLowerCase();
1147
+ const existing = byAddress.get(key);
1148
+ if (existing) {
1149
+ existing.stateDiff.push(...entry.stateDiff ?? []);
1150
+ if (entry.balance !== undefined) {
1151
+ existing.balance = entry.balance;
1152
+ }
1153
+ } else {
1154
+ byAddress.set(key, {
1155
+ address: entry.address,
1156
+ balance: entry.balance,
1157
+ stateDiff: [
1158
+ ...entry.stateDiff ?? []
1159
+ ]
1160
+ });
1161
+ }
1162
+ }
1163
+ }
1164
+ return [
1165
+ ...byAddress.values()
1166
+ ];
1167
+ }
1168
+ /** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */ static compressFeeHeader(feeHeader) {
1169
+ const MASK_48_BITS = (1n << 48n) - 1n;
1170
+ const MASK_64_BITS = (1n << 64n) - 1n;
1171
+ const MASK_63_BITS = (1n << 63n) - 1n;
1172
+ let value = BigInt(feeHeader.manaUsed) & (1n << 32n) - 1n; // bits [0:31]
1173
+ value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
1174
+ value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
1175
+ value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
1176
+ value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
1177
+ value |= 1n << 255n; // preheat flag
1178
+ return value;
1179
+ }
1180
+ /** Computes the fee header for a child checkpoint given parent fee header and child data.
1181
+ * Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */ static computeChildFeeHeader(parentFeeHeader, childManaUsed, feeAssetPriceModifier, manaTarget) {
1182
+ const MIN_ETH_PER_FEE_ASSET = 100n;
1183
+ const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
1184
+ // excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
1185
+ const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
1186
+ const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
1187
+ // ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
1188
+ const parentPrice = parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
1189
+ let newPrice;
1190
+ if (feeAssetPriceModifier >= 0n) {
1191
+ newPrice = parentPrice * (10_000n + feeAssetPriceModifier) / 10_000n;
1192
+ } else {
1193
+ const absMod = -feeAssetPriceModifier;
1194
+ newPrice = parentPrice * (10_000n - absMod) / 10_000n;
1195
+ }
1196
+ if (newPrice < MIN_ETH_PER_FEE_ASSET) {
1197
+ newPrice = MIN_ETH_PER_FEE_ASSET;
1198
+ }
1199
+ if (newPrice > MAX_ETH_PER_FEE_ASSET) {
1200
+ newPrice = MAX_ETH_PER_FEE_ASSET;
1201
+ }
1202
+ return {
1203
+ excessMana,
1204
+ manaUsed: childManaUsed,
1205
+ ethPerFeeAsset: newPrice,
1206
+ congestionCost: 0n,
1207
+ proverCost: 0n
1208
+ };
1209
+ }
945
1210
  /** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */ buildInvalidateBadAttestationRequest(checkpointNumber, attestationsAndSigners, committee, invalidIndex) {
946
1211
  return {
947
1212
  to: this.address,
1213
+ abi: RollupAbi,
948
1214
  data: encodeFunctionData({
949
1215
  abi: RollupAbi,
950
1216
  functionName: 'invalidateBadAttestation',
@@ -960,6 +1226,7 @@ export class RollupContract {
960
1226
  /** Creates a request to Rollup#invalidateInsufficientAttestations to be simulated or sent */ buildInvalidateInsufficientAttestationsRequest(checkpointNumber, attestationsAndSigners, committee) {
961
1227
  return {
962
1228
  to: this.address,
1229
+ abi: RollupAbi,
963
1230
  data: encodeFunctionData({
964
1231
  abi: RollupAbi,
965
1232
  functionName: 'invalidateInsufficientAttestations',
@@ -981,11 +1248,25 @@ export class RollupContract {
981
1248
  prover
982
1249
  ]);
983
1250
  }
984
- getManaMinFeeAt(timestamp, inFeeAsset) {
1251
+ getManaMinFeeAt(timestamp, inFeeAsset, stateOverride) {
985
1252
  return this.rollup.read.getManaMinFeeAt([
986
1253
  timestamp,
987
1254
  inFeeAsset
1255
+ ], {
1256
+ stateOverride
1257
+ });
1258
+ }
1259
+ async getManaMinFeeComponentsAt(timestamp, inFeeAsset) {
1260
+ const result = await this.rollup.read.getManaMinFeeComponentsAt([
1261
+ timestamp,
1262
+ inFeeAsset
988
1263
  ]);
1264
+ return {
1265
+ sequencerCost: result.sequencerCost,
1266
+ proverCost: result.proverCost,
1267
+ congestionCost: result.congestionCost,
1268
+ congestionMultiplier: result.congestionMultiplier
1269
+ };
989
1270
  }
990
1271
  async getSlotAt(timestamp) {
991
1272
  return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([
@@ -1036,10 +1317,10 @@ export class RollupContract {
1036
1317
  prover
1037
1318
  ]);
1038
1319
  }
1039
- async getAttesters() {
1320
+ async getAttesters(timestamp) {
1040
1321
  const attesterSize = await this.getActiveAttesterCount();
1041
1322
  const gse = new GSEContract(this.client, await this.getGSE());
1042
- const ts = (await this.client.getBlock()).timestamp;
1323
+ const ts = timestamp ?? (await this.client.getBlock()).timestamp;
1043
1324
  const indices = Array.from({
1044
1325
  length: attesterSize
1045
1326
  }, (_, i)=>BigInt(i));
@@ -1105,6 +1386,7 @@ export class RollupContract {
1105
1386
  setupEpoch(l1TxUtils) {
1106
1387
  return l1TxUtils.sendAndMonitorTransaction({
1107
1388
  to: this.address,
1389
+ abi: RollupAbi,
1108
1390
  data: encodeFunctionData({
1109
1391
  abi: RollupAbi,
1110
1392
  functionName: 'setupEpoch',
@@ -1115,6 +1397,7 @@ export class RollupContract {
1115
1397
  vote(l1TxUtils, proposalId) {
1116
1398
  return l1TxUtils.sendAndMonitorTransaction({
1117
1399
  to: this.address,
1400
+ abi: RollupAbi,
1118
1401
  data: encodeFunctionData({
1119
1402
  abi: RollupAbi,
1120
1403
  functionName: 'vote',
@@ -1143,7 +1426,8 @@ export class RollupContract {
1143
1426
  const args = log.args;
1144
1427
  if (args.checkpointNumber !== undefined) {
1145
1428
  callback({
1146
- checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber)
1429
+ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber),
1430
+ event: log
1147
1431
  });
1148
1432
  }
1149
1433
  }
@@ -1174,4 +1458,83 @@ export class RollupContract {
1174
1458
  }
1175
1459
  });
1176
1460
  }
1461
+ /**
1462
+ * Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
1463
+ * The Rollup inherits from Ownable and emits this event in its constructor, so the event
1464
+ * is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
1465
+ * probe to detect RPC nodes that prune historical logs.
1466
+ */ async getOwnershipTransferredEventsAtDeploy() {
1467
+ const l1StartBlock = await this.getL1StartBlock();
1468
+ return await this.rollup.getEvents.OwnershipTransferred({}, {
1469
+ fromBlock: l1StartBlock,
1470
+ toBlock: l1StartBlock
1471
+ });
1472
+ }
1473
+ /** Fetches CheckpointProposed events within the given block range. */ async getCheckpointProposedEvents(fromBlock, toBlock) {
1474
+ const logs = await this.rollup.getEvents.CheckpointProposed({}, {
1475
+ fromBlock,
1476
+ toBlock
1477
+ });
1478
+ return logs.filter((log)=>log.blockNumber >= fromBlock && log.blockNumber <= toBlock).map((log)=>({
1479
+ l1BlockNumber: log.blockNumber,
1480
+ l1BlockHash: Buffer32.fromString(log.blockHash),
1481
+ l1TransactionHash: log.transactionHash,
1482
+ args: {
1483
+ checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber),
1484
+ archive: Fr.fromString(log.args.archive),
1485
+ versionedBlobHashes: log.args.versionedBlobHashes.map((h)=>Buffer.from(h.slice(2), 'hex')),
1486
+ attestationsHash: (()=>{
1487
+ if (!log.args.attestationsHash) {
1488
+ throw new Error(`CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`);
1489
+ }
1490
+ return Buffer32.fromString(log.args.attestationsHash);
1491
+ })(),
1492
+ payloadDigest: (()=>{
1493
+ if (!log.args.payloadDigest) {
1494
+ throw new Error(`CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`);
1495
+ }
1496
+ return Buffer32.fromString(log.args.payloadDigest);
1497
+ })()
1498
+ }
1499
+ }));
1500
+ }
1501
+ /** Packs pending and proven checkpoint numbers into the chain tips storage format. */ static packChainTips(pendingCheckpointNumber, provenCheckpointNumber) {
1502
+ return pendingCheckpointNumber << 128n | provenCheckpointNumber & (1n << 128n) - 1n;
1503
+ }
1504
+ /** Storage slot for the chain tips (offset 0 within the STF storage struct). */ static get chainTipsStorageSlot() {
1505
+ return BigInt(RollupContract.stfStorageSlot);
1506
+ }
1507
+ /**
1508
+ * Computes the storage slot for a field within a tempCheckpointLog entry.
1509
+ * @param checkpointNumber - The checkpoint number
1510
+ * @param field - The field within the CompressedTempCheckpointLog struct
1511
+ */ async getTempCheckpointLogStorageSlot(checkpointNumber, field) {
1512
+ const [epochDuration, proofSubmissionEpochs] = await Promise.all([
1513
+ this.getEpochDuration(),
1514
+ this.getProofSubmissionEpochs()
1515
+ ]);
1516
+ return this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, {
1517
+ epochDuration,
1518
+ proofSubmissionEpochs
1519
+ });
1520
+ }
1521
+ computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants) {
1522
+ const fieldOffset = BigInt(field);
1523
+ const { epochDuration, proofSubmissionEpochs } = constants;
1524
+ const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
1525
+ const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
1526
+ const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
1527
+ const entryBase = BigInt(keccak256(encodeAbiParameters([
1528
+ {
1529
+ type: 'uint256'
1530
+ },
1531
+ {
1532
+ type: 'uint256'
1533
+ }
1534
+ ], [
1535
+ circularIndex,
1536
+ tempCheckpointLogsBase
1537
+ ])));
1538
+ return entryBase + fieldOffset;
1539
+ }
1177
1540
  }