@aztec/ethereum 0.0.1-commit.f2ce05ee → 0.0.1-commit.f5a9928

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 (181) hide show
  1. package/dest/client.d.ts +27 -3
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/client.js +63 -8
  4. package/dest/config.d.ts +17 -6
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +38 -10
  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_price_oracle.d.ts +105 -0
  11. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  12. package/dest/contracts/fee_asset_price_oracle.js +656 -0
  13. package/dest/contracts/governance.d.ts +135 -35
  14. package/dest/contracts/governance.d.ts.map +1 -1
  15. package/dest/contracts/governance.js +229 -18
  16. package/dest/contracts/governance_proposer.d.ts +33 -2
  17. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  18. package/dest/contracts/governance_proposer.js +54 -6
  19. package/dest/contracts/gse.d.ts +4 -2
  20. package/dest/contracts/gse.d.ts.map +1 -1
  21. package/dest/contracts/gse.js +2 -2
  22. package/dest/contracts/inbox.d.ts +3 -3
  23. package/dest/contracts/inbox.d.ts.map +1 -1
  24. package/dest/contracts/inbox.js +12 -7
  25. package/dest/contracts/index.d.ts +4 -3
  26. package/dest/contracts/index.d.ts.map +1 -1
  27. package/dest/contracts/index.js +3 -2
  28. package/dest/contracts/multicall.d.ts +127 -12
  29. package/dest/contracts/multicall.d.ts.map +1 -1
  30. package/dest/contracts/multicall.js +201 -94
  31. package/dest/contracts/outbox.d.ts +27 -4
  32. package/dest/contracts/outbox.d.ts.map +1 -1
  33. package/dest/contracts/outbox.js +34 -7
  34. package/dest/contracts/registry.d.ts +3 -1
  35. package/dest/contracts/registry.d.ts.map +1 -1
  36. package/dest/contracts/registry.js +30 -1
  37. package/dest/contracts/rollup.d.ts +5437 -34
  38. package/dest/contracts/rollup.d.ts.map +1 -1
  39. package/dest/contracts/rollup.js +448 -104
  40. package/dest/contracts/slashing_proposer.d.ts +171 -0
  41. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  42. package/dest/contracts/slashing_proposer.js +753 -0
  43. package/dest/contracts/watch_event.d.ts +36 -0
  44. package/dest/contracts/watch_event.d.ts.map +1 -0
  45. package/dest/contracts/watch_event.js +66 -0
  46. package/dest/deploy_aztec_l1_contracts.d.ts +5 -9
  47. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  48. package/dest/deploy_aztec_l1_contracts.js +48 -27
  49. package/dest/deploy_l1_contract.js +3 -3
  50. package/dest/foundry_binary.d.ts +13 -0
  51. package/dest/foundry_binary.d.ts.map +1 -0
  52. package/dest/foundry_binary.js +52 -0
  53. package/dest/generated/l1-contracts-defaults.d.ts +3 -3
  54. package/dest/generated/l1-contracts-defaults.js +3 -3
  55. package/dest/l1_artifacts.d.ts +15524 -15847
  56. package/dest/l1_artifacts.d.ts.map +1 -1
  57. package/dest/l1_artifacts.js +9 -24
  58. package/dest/l1_contract_addresses.d.ts +61 -65
  59. package/dest/l1_contract_addresses.d.ts.map +1 -1
  60. package/dest/l1_contract_addresses.js +101 -82
  61. package/dest/l1_reader.d.ts +5 -5
  62. package/dest/l1_reader.d.ts.map +1 -1
  63. package/dest/l1_reader.js +9 -7
  64. package/dest/l1_tx_utils/config.d.ts +7 -1
  65. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  66. package/dest/l1_tx_utils/config.js +17 -4
  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 +156 -79
  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 +57 -23
  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 +34 -8
  91. package/dest/publisher_manager.d.ts.map +1 -1
  92. package/dest/publisher_manager.js +119 -8
  93. package/dest/queries.d.ts +13 -2
  94. package/dest/queries.d.ts.map +1 -1
  95. package/dest/queries.js +62 -3
  96. package/dest/test/blob_kzg_warmup.d.ts +19 -0
  97. package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
  98. package/dest/test/blob_kzg_warmup.js +64 -0
  99. package/dest/test/chain_monitor.d.ts +55 -4
  100. package/dest/test/chain_monitor.d.ts.map +1 -1
  101. package/dest/test/chain_monitor.js +104 -3
  102. package/dest/test/eth_cheat_codes.d.ts +31 -6
  103. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  104. package/dest/test/eth_cheat_codes.js +67 -44
  105. package/dest/test/index.d.ts +2 -3
  106. package/dest/test/index.d.ts.map +1 -1
  107. package/dest/test/index.js +1 -2
  108. package/dest/test/recording_rpc_server.d.ts +18 -0
  109. package/dest/test/recording_rpc_server.d.ts.map +1 -0
  110. package/dest/test/recording_rpc_server.js +68 -0
  111. package/dest/test/rollup_cheat_codes.d.ts +60 -3
  112. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  113. package/dest/test/rollup_cheat_codes.js +86 -7
  114. package/dest/test/start_anvil.d.ts +23 -3
  115. package/dest/test/start_anvil.d.ts.map +1 -1
  116. package/dest/test/start_anvil.js +182 -32
  117. package/dest/test/upgrade_utils.js +2 -2
  118. package/dest/utils.d.ts +9 -2
  119. package/dest/utils.d.ts.map +1 -1
  120. package/dest/utils.js +59 -24
  121. package/package.json +6 -8
  122. package/src/client.ts +72 -2
  123. package/src/config.ts +58 -14
  124. package/src/contracts/chain_state_override.ts +200 -0
  125. package/src/contracts/fee_asset_price_oracle.ts +285 -0
  126. package/src/contracts/governance.ts +313 -13
  127. package/src/contracts/governance_proposer.ts +58 -9
  128. package/src/contracts/gse.ts +7 -2
  129. package/src/contracts/inbox.ts +13 -5
  130. package/src/contracts/index.ts +3 -2
  131. package/src/contracts/multicall.ts +243 -105
  132. package/src/contracts/outbox.ts +42 -8
  133. package/src/contracts/registry.ts +31 -1
  134. package/src/contracts/rollup.ts +507 -109
  135. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +119 -45
  136. package/src/contracts/watch_event.ts +110 -0
  137. package/src/deploy_aztec_l1_contracts.ts +75 -39
  138. package/src/deploy_l1_contract.ts +3 -3
  139. package/src/foundry_binary.ts +57 -0
  140. package/src/generated/l1-contracts-defaults.ts +3 -3
  141. package/src/l1_artifacts.ts +12 -35
  142. package/src/l1_contract_addresses.ts +120 -94
  143. package/src/l1_reader.ts +17 -10
  144. package/src/l1_tx_utils/config.ts +23 -3
  145. package/src/l1_tx_utils/factory.ts +31 -31
  146. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +1 -1
  147. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +1 -1
  148. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
  149. package/src/l1_tx_utils/index-blobs.ts +2 -2
  150. package/src/l1_tx_utils/index.ts +1 -0
  151. package/src/l1_tx_utils/l1_tx_utils.ts +149 -64
  152. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +57 -17
  153. package/src/{test → l1_tx_utils}/tx_delayer.ts +93 -56
  154. package/src/publisher_manager.ts +143 -12
  155. package/src/queries.ts +70 -4
  156. package/src/test/blob_kzg_warmup.ts +65 -0
  157. package/src/test/chain_monitor.ts +148 -3
  158. package/src/test/eth_cheat_codes.ts +63 -47
  159. package/src/test/index.ts +1 -2
  160. package/src/test/recording_rpc_server.ts +69 -0
  161. package/src/test/rollup_cheat_codes.ts +115 -6
  162. package/src/test/start_anvil.ts +212 -32
  163. package/src/test/upgrade_utils.ts +2 -2
  164. package/src/utils.ts +56 -29
  165. package/dest/contracts/empire_slashing_proposer.d.ts +0 -67
  166. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  167. package/dest/contracts/empire_slashing_proposer.js +0 -207
  168. package/dest/contracts/tally_slashing_proposer.d.ts +0 -140
  169. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  170. package/dest/contracts/tally_slashing_proposer.js +0 -320
  171. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  172. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  173. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  174. package/dest/test/delayed_tx_utils.d.ts +0 -13
  175. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  176. package/dest/test/delayed_tx_utils.js +0 -28
  177. package/dest/test/tx_delayer.d.ts +0 -36
  178. package/dest/test/tx_delayer.d.ts.map +0 -1
  179. package/src/contracts/empire_slashing_proposer.ts +0 -259
  180. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  181. package/src/test/delayed_tx_utils.ts +0 -52
@@ -376,25 +376,21 @@ 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 { getErrorCause } from '@aztec/foundation/types';
380
382
  import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
381
383
  import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
382
384
  import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
383
385
  import chunk from 'lodash.chunk';
384
- import { encodeFunctionData, getContract, hexToBigInt, keccak256 } from 'viem';
386
+ import { ContractFunctionRevertedError, RpcRequestError, decodeErrorResult, encodeAbiParameters, encodeFunctionData, getContract, hexToBigInt, keccak256 } from 'viem';
385
387
  import { getPublicClient } from '../client.js';
386
388
  import { formatViemError } from '../utils.js';
387
- import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
388
389
  import { GSEContract } from './gse.js';
389
390
  import { SlasherContract } from './slasher_contract.js';
390
- import { TallySlashingProposerContract } from './tally_slashing_proposer.js';
391
+ import { SlashingProposerContract } from './slashing_proposer.js';
391
392
  import { checkBlockTag } from './utils.js';
392
- export var SlashingProposerType = /*#__PURE__*/ function(SlashingProposerType) {
393
- SlashingProposerType[SlashingProposerType["None"] = 0] = "None";
394
- SlashingProposerType[SlashingProposerType["Tally"] = 1] = "Tally";
395
- SlashingProposerType[SlashingProposerType["Empire"] = 2] = "Empire";
396
- return SlashingProposerType;
397
- }({});
393
+ import { watchContractEvent } from './watch_event.js';
398
394
  /**
399
395
  * Status of a validator/attester in the staking system.
400
396
  * Matches the Status enum in StakingLib.sol
@@ -405,6 +401,38 @@ export var SlashingProposerType = /*#__PURE__*/ function(SlashingProposerType) {
405
401
  AttesterStatus[AttesterStatus["EXITING"] = 3] = "EXITING";
406
402
  return AttesterStatus;
407
403
  }({});
404
+ /** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */ export var TempCheckpointLogField = /*#__PURE__*/ function(TempCheckpointLogField) {
405
+ TempCheckpointLogField[TempCheckpointLogField["HeaderHash"] = 0] = "HeaderHash";
406
+ TempCheckpointLogField[TempCheckpointLogField["BlobCommitmentsHash"] = 1] = "BlobCommitmentsHash";
407
+ TempCheckpointLogField[TempCheckpointLogField["OutHash"] = 2] = "OutHash";
408
+ TempCheckpointLogField[TempCheckpointLogField["AttestationsHash"] = 3] = "AttestationsHash";
409
+ TempCheckpointLogField[TempCheckpointLogField["PayloadDigest"] = 4] = "PayloadDigest";
410
+ TempCheckpointLogField[TempCheckpointLogField["SlotNumber"] = 5] = "SlotNumber";
411
+ TempCheckpointLogField[TempCheckpointLogField["FeeHeader"] = 6] = "FeeHeader";
412
+ return TempCheckpointLogField;
413
+ }({});
414
+ const INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR = 'ValidatorSelection__InsufficientValidatorSetSize';
415
+ /** SlasherUpdated events are rare governance operations, so their watcher polls well below the client's interval. */ const SLASHER_UPDATED_POLLING_INTERVAL_MS = 60_000;
416
+ function isValidatorSelectionError(err, errorName) {
417
+ return getErrorCause(err, ContractFunctionRevertedError)?.data?.errorName === errorName || decodeRpcRequestErrorName(err) === errorName;
418
+ }
419
+ function decodeRpcRequestErrorName(err) {
420
+ const data = getErrorCause(err, RpcRequestError)?.data;
421
+ if (!isHexString(data)) {
422
+ return undefined;
423
+ }
424
+ try {
425
+ return decodeErrorResult({
426
+ abi: RollupAbi,
427
+ data
428
+ }).errorName;
429
+ } catch {
430
+ return undefined;
431
+ }
432
+ }
433
+ function isHexString(value) {
434
+ return typeof value === 'string' && value.startsWith('0x');
435
+ }
408
436
  export class RollupContract {
409
437
  client;
410
438
  static{
@@ -499,6 +527,16 @@ export class RollupContract {
499
527
  2,
500
528
  "getGenesisArchiveTreeRoot"
501
529
  ],
530
+ [
531
+ memoize,
532
+ 2,
533
+ "getVkTreeRoot"
534
+ ],
535
+ [
536
+ memoize,
537
+ 2,
538
+ "getProtocolContractsHash"
539
+ ],
502
540
  [
503
541
  memoize,
504
542
  2,
@@ -507,6 +545,7 @@ export class RollupContract {
507
545
  ], []));
508
546
  }
509
547
  rollup;
548
+ logger;
510
549
  static cachedStfStorageSlot;
511
550
  cachedEscapeHatch;
512
551
  static get checkBlobStorageSlot() {
@@ -525,12 +564,12 @@ export class RollupContract {
525
564
  }
526
565
  static getFromConfig(config) {
527
566
  const client = getPublicClient(config);
528
- const address = config.l1Contracts.rollupAddress.toString();
567
+ const address = config.rollupAddress.toString();
529
568
  return new RollupContract(client, address);
530
569
  }
531
570
  constructor(client, address){
532
571
  this.client = client;
533
- _initProto(this);
572
+ this.logger = (_initProto(this), createLogger('ethereum:rollup'));
534
573
  if (address instanceof EthAddress) {
535
574
  address = address.toString();
536
575
  }
@@ -555,34 +594,10 @@ export class RollupContract {
555
594
  return undefined;
556
595
  }
557
596
  const proposerAddress = await slasher.getProposer();
558
- const proposerAbi = [
559
- {
560
- type: 'function',
561
- name: 'SLASHING_PROPOSER_TYPE',
562
- inputs: [],
563
- outputs: [
564
- {
565
- name: '',
566
- type: 'uint8',
567
- internalType: 'enum SlasherFlavor'
568
- }
569
- ],
570
- stateMutability: 'view'
571
- }
572
- ];
573
- const proposer = getContract({
574
- address: proposerAddress.toString(),
575
- abi: proposerAbi,
576
- client: this.client
577
- });
578
- const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
579
- if (proposerType === 1..valueOf()) {
580
- return new TallySlashingProposerContract(this.client, proposerAddress);
581
- } else if (proposerType === 2..valueOf()) {
582
- return new EmpireSlashingProposerContract(this.client, proposerAddress);
583
- } else {
584
- throw new Error(`Unknown slashing proposer type: ${proposerType}`);
597
+ if (proposerAddress.isZero()) {
598
+ return undefined;
585
599
  }
600
+ return new SlashingProposerContract(this.client, proposerAddress);
586
601
  }
587
602
  getL1StartBlock() {
588
603
  return this.rollup.read.L1_BLOCK_AT_GENESIS();
@@ -640,18 +655,35 @@ export class RollupContract {
640
655
  0n
641
656
  ]));
642
657
  }
658
+ async getVkTreeRoot() {
659
+ const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
660
+ const value = await this.client.getStorageAt({
661
+ address: this.address,
662
+ slot: `0x${slot.toString(16)}`
663
+ });
664
+ return Fr.fromString(value ?? '0x0');
665
+ }
666
+ async getProtocolContractsHash() {
667
+ const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
668
+ const value = await this.client.getStorageAt({
669
+ address: this.address,
670
+ slot: `0x${slot.toString(16)}`
671
+ });
672
+ return Fr.fromString(value ?? '0x0');
673
+ }
643
674
  /**
644
675
  * Returns rollup constants used for epoch queries.
645
676
  * Return type is `L1RollupConstants` which is defined in stdlib,
646
677
  * so we cant reference it until we move this contract to that package.
647
678
  */ async getRollupConstants() {
648
- const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs, targetCommitteeSize] = await Promise.all([
679
+ const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs, targetCommitteeSize, rollupManaLimit] = await Promise.all([
649
680
  this.getL1StartBlock(),
650
681
  this.getL1GenesisTime(),
651
682
  this.getSlotDuration(),
652
683
  this.getEpochDuration(),
653
684
  this.getProofSubmissionEpochs(),
654
- this.getTargetCommitteeSize()
685
+ this.getTargetCommitteeSize(),
686
+ this.getManaLimit()
655
687
  ]);
656
688
  return {
657
689
  l1StartBlock,
@@ -659,7 +691,8 @@ export class RollupContract {
659
691
  slotDuration,
660
692
  epochDuration: Number(epochDuration),
661
693
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
662
- targetCommitteeSize
694
+ targetCommitteeSize,
695
+ rollupManaLimit: Number(rollupManaLimit)
663
696
  };
664
697
  }
665
698
  async getSlasherAddress() {
@@ -705,7 +738,11 @@ export class RollupContract {
705
738
  BigInt(epoch)
706
739
  ]);
707
740
  return isOpen;
708
- } catch {
741
+ } catch (err) {
742
+ this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
743
+ epoch: Number(epoch),
744
+ error: err
745
+ });
709
746
  return false;
710
747
  }
711
748
  }
@@ -721,8 +758,9 @@ export class RollupContract {
721
758
  async getOwner() {
722
759
  return EthAddress.fromString(await this.rollup.read.owner());
723
760
  }
724
- async getActiveAttesterCount() {
725
- return Number(await this.rollup.read.getActiveAttesterCount());
761
+ async getActiveAttesterCount(options) {
762
+ await checkBlockTag(options?.blockNumber, this.client);
763
+ return Number(await this.rollup.read.getActiveAttesterCount(options));
726
764
  }
727
765
  async getSlashingProposerAddress() {
728
766
  const slasher = await this.getSlasherContract();
@@ -734,24 +772,40 @@ export class RollupContract {
734
772
  getCheckpointReward() {
735
773
  return this.rollup.read.getCheckpointReward();
736
774
  }
737
- async getCheckpointNumber() {
738
- return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber());
775
+ async getCheckpointNumber(options) {
776
+ await checkBlockTag(options?.blockNumber, this.client);
777
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
739
778
  }
740
- async getProvenCheckpointNumber() {
741
- return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber());
779
+ async getProvenCheckpointNumber(options) {
780
+ await checkBlockTag(options?.blockNumber, this.client);
781
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
742
782
  }
743
- async getSlotNumber() {
744
- return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot());
783
+ async getSlotNumber(options) {
784
+ await checkBlockTag(options?.blockNumber, this.client);
785
+ return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
745
786
  }
746
- async getL1FeesAt(timestamp) {
787
+ async getL1FeesAt(timestamp, options) {
788
+ await checkBlockTag(options?.blockNumber, this.client);
747
789
  const result = await this.rollup.read.getL1FeesAt([
748
790
  timestamp
749
- ]);
791
+ ], options);
750
792
  return {
751
793
  baseFee: result.baseFee,
752
794
  blobFee: result.blobFee
753
795
  };
754
796
  }
797
+ async getFeeHeader(checkpointNumber) {
798
+ const result = await this.rollup.read.getFeeHeader([
799
+ checkpointNumber
800
+ ]);
801
+ return {
802
+ excessMana: result.excessMana,
803
+ manaUsed: result.manaUsed,
804
+ ethPerFeeAsset: result.ethPerFeeAsset,
805
+ congestionCost: result.congestionCost,
806
+ proverCost: result.proverCost
807
+ };
808
+ }
755
809
  getEthPerFeeAsset() {
756
810
  return this.rollup.read.getEthPerFeeAsset();
757
811
  }
@@ -764,7 +818,7 @@ export class RollupContract {
764
818
  timestamp
765
819
  ]
766
820
  }).catch((e)=>{
767
- if (e instanceof Error && e.message.includes('ValidatorSelection__InsufficientValidatorSetSize')) {
821
+ if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
768
822
  return {
769
823
  result: undefined
770
824
  };
@@ -791,7 +845,7 @@ export class RollupContract {
791
845
  functionName: 'getCurrentEpochCommittee',
792
846
  args: []
793
847
  }).catch((e)=>{
794
- if (e instanceof Error && e.message.includes('ValidatorSelection__InsufficientValidatorSetSize')) {
848
+ if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
795
849
  return {
796
850
  result: undefined
797
851
  };
@@ -820,10 +874,11 @@ export class RollupContract {
820
874
  });
821
875
  return EthAddress.fromString(result);
822
876
  }
823
- async getCheckpoint(checkpointNumber) {
877
+ async getCheckpoint(checkpointNumber, options) {
878
+ await checkBlockTag(options?.blockNumber, this.client);
824
879
  const result = await this.rollup.read.getCheckpoint([
825
880
  BigInt(checkpointNumber)
826
- ]);
881
+ ], options);
827
882
  return {
828
883
  archive: Fr.fromString(result.archive),
829
884
  headerHash: Buffer32.fromString(result.headerHash),
@@ -853,6 +908,28 @@ export class RollupContract {
853
908
  0.5
854
909
  ]));
855
910
  }
911
+ /**
912
+ * Returns the effective pending checkpoint, accounting for potential prunes.
913
+ * When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
914
+ * This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
915
+ * @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
916
+ * @param options - Optional L1 block number to pin the queries to.
917
+ */ getEffectivePendingCheckpoint(atTimestamp, options) {
918
+ return retry(async ()=>{
919
+ const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
920
+ const canPrune = await this.canPruneAtTime(timestamp, options);
921
+ if (canPrune) {
922
+ const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
923
+ return await this.getCheckpoint(provenCheckpointNumber, options);
924
+ }
925
+ const pendingCheckpointNumber = await this.getCheckpointNumber(options);
926
+ return await this.getCheckpoint(pendingCheckpointNumber, options);
927
+ }, 'getting effective pending checkpoint', makeBackoff([
928
+ 0.5,
929
+ 0.5,
930
+ 0.5
931
+ ]));
932
+ }
856
933
  async getTips() {
857
934
  const { pending, proven } = await this.rollup.read.getTips();
858
935
  return {
@@ -937,9 +1014,8 @@ export class RollupContract {
937
1014
  * @return [slot, checkpointNumber, timeOfNextL1Slot] - If you can propose, the L2 slot number, checkpoint number and
938
1015
  * timestamp of the next L1 block
939
1016
  * @throws otherwise
940
- */ async canProposeAtNextEthBlock(archive, account, slotDuration, opts = {}) {
941
- const latestBlock = await this.client.getBlock();
942
- const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
1017
+ */ async canProposeAt(archive, account, timestamp, stateOverride = []) {
1018
+ const timeOfNextL1Slot = timestamp;
943
1019
  const who = typeof account === 'string' ? account : account.address;
944
1020
  try {
945
1021
  const { result: [slot, checkpointNumber] } = await this.client.simulateContract({
@@ -952,7 +1028,7 @@ export class RollupContract {
952
1028
  who
953
1029
  ],
954
1030
  account,
955
- stateOverride: await this.makePendingCheckpointNumberOverride(opts.forcePendingCheckpointNumber)
1031
+ stateOverride
956
1032
  });
957
1033
  return {
958
1034
  slot: SlotNumber.fromBigInt(slot),
@@ -964,11 +1040,15 @@ export class RollupContract {
964
1040
  }
965
1041
  }
966
1042
  /**
967
- * Returns a state override that sets the pending checkpoint number to the specified value. Useful for simulations.
968
- * Requires querying the current state of the contract to get the current proven checkpoint number, as they are both
969
- * stored in the same slot. If the argument is undefined, it returns an empty override.
970
- */ async makePendingCheckpointNumberOverride(forcePendingCheckpointNumber) {
971
- if (forcePendingCheckpointNumber === undefined) {
1043
+ * Returns a state override that sets the pending and/or proven checkpoint numbers. Useful for simulations.
1044
+ * Both values share a single storage slot (pending in the upper 128 bits, proven in the lower 128 bits), so
1045
+ * a single combined override is emitted to avoid the second state-diff clobbering the first. The current live
1046
+ * value is read once to preserve any half not being overridden. Returns an empty override if neither is set.
1047
+ *
1048
+ * Throws if the resulting `proven > pending`, which would crash the simulation: `STFLib.canPruneAtTime` calls
1049
+ * `getEpochForCheckpoint(proven + 1)` whenever `pending != proven`, and that asserts `_n <= tips.pending`.
1050
+ */ async makeChainTipsOverride(override) {
1051
+ if (override.pending === undefined && override.proven === undefined) {
972
1052
  return [];
973
1053
  }
974
1054
  const slot = RollupContract.stfStorageSlot;
@@ -976,8 +1056,15 @@ export class RollupContract {
976
1056
  address: this.address,
977
1057
  slot
978
1058
  });
979
- const currentProvenCheckpointNumber = currentValue ? hexToBigInt(currentValue) & (1n << 128n) - 1n : 0n;
980
- const newValue = BigInt(forcePendingCheckpointNumber) << 128n | currentProvenCheckpointNumber;
1059
+ const currentRaw = currentValue ? hexToBigInt(currentValue) : 0n;
1060
+ const currentPending = currentRaw >> 128n;
1061
+ const currentProven = currentRaw & (1n << 128n) - 1n;
1062
+ const newPending = override.pending !== undefined ? BigInt(override.pending) : currentPending;
1063
+ const newProven = override.proven !== undefined ? BigInt(override.proven) : currentProven;
1064
+ if (newProven > newPending) {
1065
+ throw new Error(`Invalid chain tips override: proven (${newProven}) > pending (${newPending})`);
1066
+ }
1067
+ const newValue = newPending << 128n | newProven;
981
1068
  return [
982
1069
  {
983
1070
  address: this.address,
@@ -990,6 +1077,161 @@ export class RollupContract {
990
1077
  }
991
1078
  ];
992
1079
  }
1080
+ /**
1081
+ * Returns a state override that patches `tempCheckpointLogs[checkpointNumber]` with every field that
1082
+ * the L1 contract reads during `propose()` for the checkpoint that builds on top of it (proposer
1083
+ * pipelining simulation). Mirrors the writes done by `ProposeLib.addTempCheckpointLog`.
1084
+ *
1085
+ * `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path
1086
+ * never asserts against them, so leaving them at storage zero is harmless.
1087
+ */ async makeTempCheckpointLogOverride(checkpointNumber, fields) {
1088
+ const constants = await this.getRollupConstants();
1089
+ const slotAt = (field)=>`0x${this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants).toString(16).padStart(64, '0')}`;
1090
+ const word = (v)=>`0x${v.toString(16).padStart(64, '0')}`;
1091
+ const stateDiff = [];
1092
+ if (fields.headerHash) {
1093
+ stateDiff.push({
1094
+ slot: slotAt(0),
1095
+ value: fields.headerHash.toString()
1096
+ });
1097
+ }
1098
+ if (fields.outHash) {
1099
+ stateDiff.push({
1100
+ slot: slotAt(2),
1101
+ value: fields.outHash.toString()
1102
+ });
1103
+ }
1104
+ if (fields.payloadDigest) {
1105
+ stateDiff.push({
1106
+ slot: slotAt(4),
1107
+ value: fields.payloadDigest.toString()
1108
+ });
1109
+ }
1110
+ if (fields.slotNumber !== undefined) {
1111
+ // CompressedSlot is uint32 on L1 (SafeCast.toUint32 reverts on overflow). Match that behavior here
1112
+ // so a malformed override surfaces immediately rather than silently truncating into a wrong slot.
1113
+ const slotNumber = BigInt(fields.slotNumber);
1114
+ if (slotNumber < 0n || slotNumber > 0xffffffffn) {
1115
+ throw new Error(`slotNumber ${slotNumber} does not fit in uint32`);
1116
+ }
1117
+ stateDiff.push({
1118
+ slot: slotAt(5),
1119
+ value: word(slotNumber)
1120
+ });
1121
+ }
1122
+ if (fields.feeHeader) {
1123
+ stateDiff.push({
1124
+ slot: slotAt(6),
1125
+ value: word(RollupContract.compressFeeHeader(fields.feeHeader))
1126
+ });
1127
+ }
1128
+ if (stateDiff.length === 0) {
1129
+ return [];
1130
+ }
1131
+ return [
1132
+ {
1133
+ address: this.address,
1134
+ stateDiff
1135
+ }
1136
+ ];
1137
+ }
1138
+ /**
1139
+ * Returns a state override that sets archives[checkpointNumber] to the given archive value.
1140
+ * Used when simulating a canProposeAtTime call where the local archive differs from L1
1141
+ * (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
1142
+ */ makeArchiveOverride(checkpointNumber, archive) {
1143
+ const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
1144
+ const archiveSlot = hexToBigInt(keccak256(encodeAbiParameters([
1145
+ {
1146
+ type: 'uint256'
1147
+ },
1148
+ {
1149
+ type: 'uint256'
1150
+ }
1151
+ ], [
1152
+ BigInt(checkpointNumber),
1153
+ archivesMappingBase
1154
+ ])));
1155
+ return [
1156
+ {
1157
+ address: this.address,
1158
+ stateDiff: [
1159
+ {
1160
+ slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
1161
+ value: archive.toString()
1162
+ }
1163
+ ]
1164
+ }
1165
+ ];
1166
+ }
1167
+ /** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */ static mergeStateOverrides(...overrides) {
1168
+ const byAddress = new Map();
1169
+ for (const override of overrides){
1170
+ for (const entry of override){
1171
+ const key = entry.address.toLowerCase();
1172
+ const existing = byAddress.get(key);
1173
+ if (existing) {
1174
+ existing.stateDiff.push(...entry.stateDiff ?? []);
1175
+ if (entry.balance !== undefined) {
1176
+ existing.balance = entry.balance;
1177
+ }
1178
+ } else {
1179
+ byAddress.set(key, {
1180
+ address: entry.address,
1181
+ balance: entry.balance,
1182
+ stateDiff: [
1183
+ ...entry.stateDiff ?? []
1184
+ ]
1185
+ });
1186
+ }
1187
+ }
1188
+ }
1189
+ return [
1190
+ ...byAddress.values()
1191
+ ];
1192
+ }
1193
+ /** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */ static compressFeeHeader(feeHeader) {
1194
+ const MASK_48_BITS = (1n << 48n) - 1n;
1195
+ const MASK_64_BITS = (1n << 64n) - 1n;
1196
+ const MASK_63_BITS = (1n << 63n) - 1n;
1197
+ let value = BigInt(feeHeader.manaUsed) & (1n << 32n) - 1n; // bits [0:31]
1198
+ value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
1199
+ value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
1200
+ value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
1201
+ value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
1202
+ value |= 1n << 255n; // preheat flag
1203
+ return value;
1204
+ }
1205
+ /** Computes the fee header for a child checkpoint given parent fee header and child data.
1206
+ * Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */ static computeChildFeeHeader(parentFeeHeader, childManaUsed, feeAssetPriceModifier, manaTarget) {
1207
+ const MIN_ETH_PER_FEE_ASSET = 100n;
1208
+ const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
1209
+ // excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
1210
+ const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
1211
+ const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
1212
+ // ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
1213
+ const parentPrice = parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
1214
+ let newPrice;
1215
+ if (feeAssetPriceModifier >= 0n) {
1216
+ newPrice = parentPrice * (10_000n + feeAssetPriceModifier) / 10_000n;
1217
+ } else {
1218
+ const absMod = -feeAssetPriceModifier;
1219
+ newPrice = parentPrice * (10_000n - absMod) / 10_000n;
1220
+ }
1221
+ if (newPrice < MIN_ETH_PER_FEE_ASSET) {
1222
+ newPrice = MIN_ETH_PER_FEE_ASSET;
1223
+ }
1224
+ if (newPrice > MAX_ETH_PER_FEE_ASSET) {
1225
+ newPrice = MAX_ETH_PER_FEE_ASSET;
1226
+ }
1227
+ return {
1228
+ excessMana,
1229
+ manaUsed: childManaUsed,
1230
+ ethPerFeeAsset: newPrice,
1231
+ congestionCost: 0n,
1232
+ proverCost: 0n
1233
+ };
1234
+ }
993
1235
  /** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */ buildInvalidateBadAttestationRequest(checkpointNumber, attestationsAndSigners, committee, invalidIndex) {
994
1236
  return {
995
1237
  to: this.address,
@@ -1031,11 +1273,25 @@ export class RollupContract {
1031
1273
  prover
1032
1274
  ]);
1033
1275
  }
1034
- getManaMinFeeAt(timestamp, inFeeAsset) {
1276
+ getManaMinFeeAt(timestamp, inFeeAsset, stateOverride) {
1035
1277
  return this.rollup.read.getManaMinFeeAt([
1036
1278
  timestamp,
1037
1279
  inFeeAsset
1280
+ ], {
1281
+ stateOverride
1282
+ });
1283
+ }
1284
+ async getManaMinFeeComponentsAt(timestamp, inFeeAsset) {
1285
+ const result = await this.rollup.read.getManaMinFeeComponentsAt([
1286
+ timestamp,
1287
+ inFeeAsset
1038
1288
  ]);
1289
+ return {
1290
+ sequencerCost: result.sequencerCost,
1291
+ proverCost: result.proverCost,
1292
+ congestionCost: result.congestionCost,
1293
+ congestionMultiplier: result.congestionMultiplier
1294
+ };
1039
1295
  }
1040
1296
  async getSlotAt(timestamp) {
1041
1297
  return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([
@@ -1086,15 +1342,24 @@ export class RollupContract {
1086
1342
  prover
1087
1343
  ]);
1088
1344
  }
1089
- async getAttesters() {
1090
- const attesterSize = await this.getActiveAttesterCount();
1345
+ async getAttesters(timestamp) {
1346
+ // Pin every read to a single L1 block so the attester count and the chunked index reads
1347
+ // observe a consistent set. Without this, the count and each chunk default to `latest` and
1348
+ // can straddle a block boundary (or reorg), yielding an inconsistent or truncated set.
1349
+ const block = await this.client.getBlock();
1350
+ const blockNumber = block.number ?? undefined;
1351
+ const ts = timestamp ?? block.timestamp;
1352
+ const attesterSize = await this.getActiveAttesterCount({
1353
+ blockNumber
1354
+ });
1091
1355
  const gse = new GSEContract(this.client, await this.getGSE());
1092
- const ts = (await this.client.getBlock()).timestamp;
1093
1356
  const indices = Array.from({
1094
1357
  length: attesterSize
1095
1358
  }, (_, i)=>BigInt(i));
1096
1359
  const chunks = chunk(indices, 1000);
1097
- const results = await Promise.all(chunks.map((chunk)=>gse.getAttestersFromIndicesAtTime(this.address, ts, chunk)));
1360
+ const results = await Promise.all(chunks.map((chunk)=>gse.getAttestersFromIndicesAtTime(this.address, ts, chunk, {
1361
+ blockNumber
1362
+ })));
1098
1363
  return results.flat().map((addr)=>EthAddress.fromString(addr));
1099
1364
  }
1100
1365
  async getAttesterView(address) {
@@ -1176,31 +1441,48 @@ export class RollupContract {
1176
1441
  })
1177
1442
  });
1178
1443
  }
1179
- listenToSlasherChanged(callback) {
1180
- return this.rollup.watchEvent.SlasherUpdated({}, {
1181
- onLogs: (logs)=>{
1182
- for (const log of logs){
1183
- const args = log.args;
1184
- if (args.oldSlasher && args.newSlasher) {
1185
- callback(args);
1186
- }
1444
+ /**
1445
+ * Watches for SlasherUpdated events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit them and
1446
+ * removals are never reported, and events mined within roughly one polling interval of subscribing may be missed.
1447
+ * Slasher rotations are rare governance operations, so by default this polls much slower than the other watchers.
1448
+ */ listenToSlasherChanged(callback, options) {
1449
+ return watchContractEvent(this.client, this.logger, {
1450
+ address: this.address,
1451
+ abi: RollupAbi,
1452
+ eventName: 'SlasherUpdated',
1453
+ onLog: (log)=>{
1454
+ const { oldSlasher, newSlasher } = log.args;
1455
+ if (oldSlasher && newSlasher) {
1456
+ return callback({
1457
+ oldSlasher,
1458
+ newSlasher
1459
+ });
1187
1460
  }
1188
1461
  }
1462
+ }, {
1463
+ pollingIntervalMs: SLASHER_UPDATED_POLLING_INTERVAL_MS,
1464
+ ...options
1189
1465
  });
1190
1466
  }
1191
- listenToCheckpointInvalidated(callback) {
1192
- return this.rollup.watchEvent.CheckpointInvalidated({}, {
1193
- onLogs: (logs)=>{
1194
- for (const log of logs){
1195
- const args = log.args;
1196
- if (args.checkpointNumber !== undefined) {
1197
- callback({
1198
- checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber)
1199
- });
1200
- }
1467
+ /**
1468
+ * Watches for CheckpointInvalidated events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit
1469
+ * them and removals are never reported, and events mined within roughly one polling interval of subscribing may
1470
+ * be missed.
1471
+ */ listenToCheckpointInvalidated(callback, options) {
1472
+ return watchContractEvent(this.client, this.logger, {
1473
+ address: this.address,
1474
+ abi: RollupAbi,
1475
+ eventName: 'CheckpointInvalidated',
1476
+ onLog: (log)=>{
1477
+ const { checkpointNumber } = log.args;
1478
+ if (checkpointNumber !== undefined) {
1479
+ return callback({
1480
+ checkpointNumber: CheckpointNumber.fromBigInt(checkpointNumber),
1481
+ event: log
1482
+ });
1201
1483
  }
1202
1484
  }
1203
- });
1485
+ }, options);
1204
1486
  }
1205
1487
  async getSlashEvents(l1BlockHash) {
1206
1488
  const events = await this.rollup.getEvents.Slashed({}, {
@@ -1212,18 +1494,31 @@ export class RollupContract {
1212
1494
  attester: EthAddress.fromString(event.args.attester)
1213
1495
  }));
1214
1496
  }
1215
- listenToSlash(callback) {
1216
- return this.rollup.watchEvent.Slashed({}, {
1497
+ /**
1498
+ * Watches for Slashed events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit them and
1499
+ * removals are never reported, and events mined within roughly one polling interval of subscribing may be missed.
1500
+ */ listenToSlash(callback, options) {
1501
+ return watchContractEvent(this.client, this.logger, {
1502
+ address: this.address,
1503
+ abi: RollupAbi,
1504
+ eventName: 'Slashed',
1217
1505
  strict: true,
1218
- onLogs: (logs)=>{
1219
- for (const log of logs){
1220
- const args = log.args;
1221
- callback({
1222
- amount: args.amount,
1223
- attester: EthAddress.fromString(args.attester)
1224
- });
1225
- }
1226
- }
1506
+ onLog: (log)=>callback({
1507
+ amount: log.args.amount,
1508
+ attester: EthAddress.fromString(log.args.attester)
1509
+ })
1510
+ }, options);
1511
+ }
1512
+ /**
1513
+ * Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
1514
+ * The Rollup inherits from Ownable and emits this event in its constructor, so the event
1515
+ * is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
1516
+ * probe to detect RPC nodes that prune historical logs.
1517
+ */ async getOwnershipTransferredEventsAtDeploy() {
1518
+ const l1StartBlock = await this.getL1StartBlock();
1519
+ return await this.rollup.getEvents.OwnershipTransferred({}, {
1520
+ fromBlock: l1StartBlock,
1521
+ toBlock: l1StartBlock
1227
1522
  });
1228
1523
  }
1229
1524
  /** Fetches CheckpointProposed events within the given block range. */ async getCheckpointProposedEvents(fromBlock, toBlock) {
@@ -1239,9 +1534,58 @@ export class RollupContract {
1239
1534
  checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber),
1240
1535
  archive: Fr.fromString(log.args.archive),
1241
1536
  versionedBlobHashes: log.args.versionedBlobHashes.map((h)=>Buffer.from(h.slice(2), 'hex')),
1242
- attestationsHash: log.args.attestationsHash ? Buffer32.fromString(log.args.attestationsHash) : undefined,
1243
- payloadDigest: log.args.payloadDigest ? Buffer32.fromString(log.args.payloadDigest) : undefined
1537
+ attestationsHash: (()=>{
1538
+ if (!log.args.attestationsHash) {
1539
+ throw new Error(`CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`);
1540
+ }
1541
+ return Buffer32.fromString(log.args.attestationsHash);
1542
+ })(),
1543
+ payloadDigest: (()=>{
1544
+ if (!log.args.payloadDigest) {
1545
+ throw new Error(`CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`);
1546
+ }
1547
+ return Buffer32.fromString(log.args.payloadDigest);
1548
+ })()
1244
1549
  }
1245
1550
  }));
1246
1551
  }
1552
+ /** Packs pending and proven checkpoint numbers into the chain tips storage format. */ static packChainTips(pendingCheckpointNumber, provenCheckpointNumber) {
1553
+ return pendingCheckpointNumber << 128n | provenCheckpointNumber & (1n << 128n) - 1n;
1554
+ }
1555
+ /** Storage slot for the chain tips (offset 0 within the STF storage struct). */ static get chainTipsStorageSlot() {
1556
+ return BigInt(RollupContract.stfStorageSlot);
1557
+ }
1558
+ /**
1559
+ * Computes the storage slot for a field within a tempCheckpointLog entry.
1560
+ * @param checkpointNumber - The checkpoint number
1561
+ * @param field - The field within the CompressedTempCheckpointLog struct
1562
+ */ async getTempCheckpointLogStorageSlot(checkpointNumber, field) {
1563
+ const [epochDuration, proofSubmissionEpochs] = await Promise.all([
1564
+ this.getEpochDuration(),
1565
+ this.getProofSubmissionEpochs()
1566
+ ]);
1567
+ return this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, {
1568
+ epochDuration,
1569
+ proofSubmissionEpochs
1570
+ });
1571
+ }
1572
+ computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants) {
1573
+ const fieldOffset = BigInt(field);
1574
+ const { epochDuration, proofSubmissionEpochs } = constants;
1575
+ const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
1576
+ const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
1577
+ const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
1578
+ const entryBase = BigInt(keccak256(encodeAbiParameters([
1579
+ {
1580
+ type: 'uint256'
1581
+ },
1582
+ {
1583
+ type: 'uint256'
1584
+ }
1585
+ ], [
1586
+ circularIndex,
1587
+ tempCheckpointLogsBase
1588
+ ])));
1589
+ return entryBase + fieldOffset;
1590
+ }
1247
1591
  }