@aztec/ethereum 0.0.1-commit.03f7ef2 → 0.0.1-commit.04d373f

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 (188) 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 +24 -32
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +63 -62
  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 +6 -5
  11. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  12. package/dest/contracts/fee_asset_handler.js +11 -9
  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 +438 -10
  22. package/dest/contracts/inbox.d.ts +22 -1
  23. package/dest/contracts/inbox.d.ts.map +1 -1
  24. package/dest/contracts/inbox.js +42 -2
  25. package/dest/contracts/index.d.ts +6 -3
  26. package/dest/contracts/index.d.ts.map +1 -1
  27. package/dest/contracts/index.js +5 -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 +58 -0
  35. package/dest/contracts/outbox.d.ts.map +1 -0
  36. package/dest/contracts/outbox.js +109 -0
  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 +5409 -115
  41. package/dest/contracts/rollup.d.ts.map +1 -1
  42. package/dest/contracts/rollup.js +1027 -189
  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 +20 -9
  47. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  48. package/dest/deploy_aztec_l1_contracts.js +113 -37
  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 +18360 -16454
  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/index.d.ts +3 -2
  71. package/dest/l1_tx_utils/fee-strategies/index.d.ts.map +1 -1
  72. package/dest/l1_tx_utils/fee-strategies/index.js +2 -1
  73. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +2 -12
  74. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -1
  75. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +36 -18
  76. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts +2 -11
  77. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts.map +1 -1
  78. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +37 -19
  79. package/dest/l1_tx_utils/fee-strategies/types.d.ts +14 -27
  80. package/dest/l1_tx_utils/fee-strategies/types.d.ts.map +1 -1
  81. package/dest/l1_tx_utils/fee-strategies/types.js +0 -21
  82. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
  83. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
  84. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
  85. package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
  86. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
  87. package/dest/l1_tx_utils/index-blobs.js +2 -2
  88. package/dest/l1_tx_utils/index.d.ts +2 -1
  89. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  90. package/dest/l1_tx_utils/index.js +1 -0
  91. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +2 -2
  92. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -1
  93. package/dest/l1_tx_utils/l1_fee_analyzer.js +3 -3
  94. package/dest/l1_tx_utils/l1_tx_utils.d.ts +26 -7
  95. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  96. package/dest/l1_tx_utils/l1_tx_utils.js +101 -66
  97. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -5
  98. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  99. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +25 -58
  100. package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
  101. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  102. package/dest/{test → l1_tx_utils}/tx_delayer.js +67 -37
  103. package/dest/publisher_manager.d.ts +21 -6
  104. package/dest/publisher_manager.d.ts.map +1 -1
  105. package/dest/publisher_manager.js +81 -7
  106. package/dest/queries.d.ts +14 -3
  107. package/dest/queries.d.ts.map +1 -1
  108. package/dest/queries.js +74 -7
  109. package/dest/test/chain_monitor.d.ts +23 -3
  110. package/dest/test/chain_monitor.d.ts.map +1 -1
  111. package/dest/test/chain_monitor.js +39 -4
  112. package/dest/test/eth_cheat_codes.d.ts +24 -4
  113. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  114. package/dest/test/eth_cheat_codes.js +17 -4
  115. package/dest/test/index.d.ts +1 -3
  116. package/dest/test/index.d.ts.map +1 -1
  117. package/dest/test/index.js +0 -2
  118. package/dest/test/rollup_cheat_codes.d.ts +34 -3
  119. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  120. package/dest/test/rollup_cheat_codes.js +75 -6
  121. package/dest/test/start_anvil.d.ts +23 -3
  122. package/dest/test/start_anvil.d.ts.map +1 -1
  123. package/dest/test/start_anvil.js +143 -29
  124. package/dest/test/upgrade_utils.js +2 -2
  125. package/dest/utils.d.ts +9 -1
  126. package/dest/utils.d.ts.map +1 -1
  127. package/dest/utils.js +81 -12
  128. package/package.json +9 -10
  129. package/src/client.ts +28 -2
  130. package/src/config.ts +81 -63
  131. package/src/contracts/README.md +157 -0
  132. package/src/contracts/chain_state_override.ts +200 -0
  133. package/src/contracts/fee_asset_handler.ts +10 -7
  134. package/src/contracts/fee_asset_price_oracle.ts +285 -0
  135. package/src/contracts/governance.ts +279 -13
  136. package/src/contracts/governance_proposer.ts +58 -2
  137. package/src/contracts/inbox.ts +62 -2
  138. package/src/contracts/index.ts +5 -2
  139. package/src/contracts/log.ts +13 -0
  140. package/src/contracts/multicall.ts +243 -102
  141. package/src/contracts/outbox.ts +128 -0
  142. package/src/contracts/registry.ts +31 -1
  143. package/src/contracts/rollup.ts +728 -148
  144. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +16 -14
  145. package/src/deploy_aztec_l1_contracts.ts +137 -47
  146. package/src/deploy_l1_contract.ts +3 -3
  147. package/src/generated/l1-contracts-defaults.ts +32 -0
  148. package/src/l1_artifacts.ts +12 -35
  149. package/src/l1_contract_addresses.ts +120 -94
  150. package/src/l1_reader.ts +17 -10
  151. package/src/l1_tx_utils/config.ts +20 -0
  152. package/src/l1_tx_utils/constants.ts +2 -2
  153. package/src/l1_tx_utils/factory.ts +31 -31
  154. package/src/l1_tx_utils/fee-strategies/index.ts +1 -1
  155. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +46 -42
  156. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +49 -45
  157. package/src/l1_tx_utils/fee-strategies/types.ts +14 -46
  158. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
  159. package/src/l1_tx_utils/index-blobs.ts +2 -2
  160. package/src/l1_tx_utils/index.ts +1 -0
  161. package/src/l1_tx_utils/l1_fee_analyzer.ts +2 -3
  162. package/src/l1_tx_utils/l1_tx_utils.ts +99 -48
  163. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +31 -66
  164. package/src/{test → l1_tx_utils}/tx_delayer.ts +93 -56
  165. package/src/publisher_manager.ts +107 -10
  166. package/src/queries.ts +81 -7
  167. package/src/test/chain_monitor.ts +66 -4
  168. package/src/test/eth_cheat_codes.ts +16 -4
  169. package/src/test/index.ts +0 -2
  170. package/src/test/rollup_cheat_codes.ts +82 -6
  171. package/src/test/start_anvil.ts +177 -29
  172. package/src/test/upgrade_utils.ts +2 -2
  173. package/src/utils.ts +88 -14
  174. package/dest/contracts/empire_slashing_proposer.d.ts +0 -67
  175. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  176. package/dest/contracts/empire_slashing_proposer.js +0 -209
  177. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  178. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  179. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  180. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  181. package/dest/test/delayed_tx_utils.d.ts +0 -13
  182. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  183. package/dest/test/delayed_tx_utils.js +0 -28
  184. package/dest/test/tx_delayer.d.ts +0 -36
  185. package/dest/test/tx_delayer.d.ts.map +0 -1
  186. package/src/contracts/empire_slashing_proposer.ts +0 -270
  187. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  188. package/src/test/delayed_tx_utils.ts +0 -52
@@ -1,8 +1,12 @@
1
1
  import { CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
+ import { Buffer32 } from '@aztec/foundation/buffer';
3
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
4
  import { memoize } from '@aztec/foundation/decorators';
3
5
  import { EthAddress } from '@aztec/foundation/eth-address';
4
6
  import type { ViemSignature } from '@aztec/foundation/eth-signature';
7
+ import { createLogger } from '@aztec/foundation/log';
5
8
  import { makeBackoff, retry } from '@aztec/foundation/retry';
9
+ import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
6
10
  import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
7
11
  import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
8
12
 
@@ -11,8 +15,10 @@ import {
11
15
  type Account,
12
16
  type GetContractReturnType,
13
17
  type Hex,
18
+ type Log,
14
19
  type StateOverride,
15
20
  type WatchContractEventReturnType,
21
+ encodeAbiParameters,
16
22
  encodeFunctionData,
17
23
  getContract,
18
24
  hexToBigInt,
@@ -26,10 +32,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
26
32
  import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
27
33
  import type { ViemClient } from '../types.js';
28
34
  import { formatViemError } from '../utils.js';
29
- import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
30
35
  import { GSEContract } from './gse.js';
36
+ import type { L1EventLog } from './log.js';
31
37
  import { SlasherContract } from './slasher_contract.js';
32
- import { TallySlashingProposerContract } from './tally_slashing_proposer.js';
38
+ import { SlashingProposerContract } from './slashing_proposer.js';
33
39
  import { checkBlockTag } from './utils.js';
34
40
 
35
41
  export type ViemCommitteeAttestation = {
@@ -51,20 +57,22 @@ export type L1RollupContractAddresses = Pick<
51
57
  | 'feeJuiceAddress'
52
58
  | 'stakingAssetAddress'
53
59
  | 'rewardDistributorAddress'
54
- | 'slashFactoryAddress'
55
60
  | 'gseAddress'
56
61
  >;
57
62
 
58
63
  export type EpochProofPublicInputArgs = {
59
64
  previousArchive: `0x${string}`;
60
65
  endArchive: `0x${string}`;
66
+ outHash: `0x${string}`;
61
67
  proverId: `0x${string}`;
62
68
  };
63
69
 
64
70
  export type ViemHeader = {
65
71
  lastArchiveRoot: `0x${string}`;
66
72
  blockHeadersHash: `0x${string}`;
67
- contentCommitment: ViemContentCommitment;
73
+ blobsHash: `0x${string}`;
74
+ inHash: `0x${string}`;
75
+ outHash: `0x${string}`;
68
76
  slotNumber: bigint;
69
77
  timestamp: bigint;
70
78
  coinbase: `0x${string}`;
@@ -73,27 +81,164 @@ export type ViemHeader = {
73
81
  totalManaUsed: bigint;
74
82
  };
75
83
 
76
- export type ViemContentCommitment = {
77
- blobsHash: `0x${string}`;
78
- inHash: `0x${string}`;
79
- outHash: `0x${string}`;
80
- };
81
-
82
84
  export type ViemGasFees = {
83
85
  feePerDaGas: bigint;
84
86
  feePerL2Gas: bigint;
85
87
  };
86
88
 
87
- export enum SlashingProposerType {
88
- None = 0,
89
- Tally = 1,
90
- Empire = 2,
89
+ /**
90
+ * Status of a validator/attester in the staking system.
91
+ * Matches the Status enum in StakingLib.sol
92
+ */
93
+ export enum AttesterStatus {
94
+ NONE = 0,
95
+ VALIDATING = 1,
96
+ ZOMBIE = 2,
97
+ EXITING = 3,
91
98
  }
92
99
 
100
+ /**
101
+ * Fee header data for a checkpoint
102
+ */
103
+ export type FeeHeader = {
104
+ excessMana: bigint;
105
+ manaUsed: bigint;
106
+ ethPerFeeAsset: bigint;
107
+ congestionCost: bigint;
108
+ proverCost: bigint;
109
+ };
110
+
111
+ /**
112
+ * Checkpoint log data returned from the rollup contract
113
+ */
114
+ export type CheckpointLog = {
115
+ archive: Fr;
116
+ headerHash: Buffer32;
117
+ blobCommitmentsHash: Buffer32;
118
+ attestationsHash: Buffer32;
119
+ payloadDigest: Buffer32;
120
+ slotNumber: SlotNumber;
121
+ feeHeader: FeeHeader;
122
+ };
123
+
124
+ /**
125
+ * L1 fee data (base fee and blob fee)
126
+ */
127
+ export type L1FeeData = {
128
+ baseFee: bigint;
129
+ blobFee: bigint;
130
+ };
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
+
143
+ /**
144
+ * Field-level override input for `tempCheckpointLogs[checkpointNumber]`. Covers the fields the
145
+ * `propose()` path actually reads back. `payloadDigest` is `Buffer32` because it carries an
146
+ * arbitrary `bytes32` value rather than a BN254 scalar. `slotNumber` carries the uint32 portion
147
+ * of the on-chain `CompressedSlot`.
148
+ */
149
+ export type TempCheckpointLogOverrideFields = {
150
+ headerHash?: Fr;
151
+ outHash?: Fr;
152
+ payloadDigest?: Buffer32;
153
+ slotNumber?: SlotNumber;
154
+ feeHeader?: FeeHeader;
155
+ };
156
+
157
+ /** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
158
+ export type ManaMinFeeComponents = {
159
+ sequencerCost: bigint;
160
+ proverCost: bigint;
161
+ congestionCost: bigint;
162
+ congestionMultiplier: bigint;
163
+ };
164
+
165
+ /**
166
+ * Reward configuration for the rollup
167
+ */
168
+ export type RewardConfig = {
169
+ rewardDistributor: EthAddress;
170
+ sequencerBps: bigint;
171
+ booster: EthAddress;
172
+ checkpointReward: bigint;
173
+ };
174
+
175
+ /**
176
+ * Exit information for a validator
177
+ */
178
+ export type Exit = {
179
+ withdrawalId: bigint;
180
+ amount: bigint;
181
+ exitableAt: bigint;
182
+ recipientOrWithdrawer: EthAddress;
183
+ isRecipient: boolean;
184
+ exists: boolean;
185
+ };
186
+
187
+ /**
188
+ * Attester configuration including public key and withdrawer
189
+ */
190
+ export type AttesterConfig = {
191
+ publicKey: {
192
+ x: bigint;
193
+ y: bigint;
194
+ };
195
+ withdrawer: EthAddress;
196
+ };
197
+
198
+ /**
199
+ * Complete view of an attester's state
200
+ */
201
+ export type AttesterView = {
202
+ status: AttesterStatus;
203
+ effectiveBalance: bigint;
204
+ exit: Exit;
205
+ config: AttesterConfig;
206
+ };
207
+
208
+ /**
209
+ * Return for a status call
210
+ */
211
+ export type RollupStatusResponse = {
212
+ provenCheckpointNumber: CheckpointNumber;
213
+ provenArchive: Fr;
214
+ pendingCheckpointNumber: CheckpointNumber;
215
+ pendingArchive: Fr;
216
+ archiveOfMyCheckpoint: Fr;
217
+ };
218
+
219
+ /** Arguments for the CheckpointProposed event. */
220
+ export type CheckpointProposedArgs = {
221
+ checkpointNumber: CheckpointNumber;
222
+ archive: Fr;
223
+ versionedBlobHashes: Buffer[];
224
+ /** Hash of attestations emitted in the CheckpointProposed event. */
225
+ attestationsHash: Buffer32;
226
+ /** Digest of the payload emitted in the CheckpointProposed event. */
227
+ payloadDigest: Buffer32;
228
+ };
229
+
230
+ /** Log type for CheckpointProposed events. */
231
+ export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
232
+
93
233
  export class RollupContract {
94
234
  private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
235
+ private readonly logger = createLogger('ethereum:rollup');
95
236
 
96
237
  private static cachedStfStorageSlot: Hex | undefined;
238
+ private cachedEscapeHatch?: {
239
+ address: EthAddress;
240
+ contract: GetContractReturnType<typeof EscapeHatchAbi, ViemClient>;
241
+ };
97
242
 
98
243
  static get checkBlobStorageSlot(): bigint {
99
244
  const asString = RollupStorage.find(storage => storage.label === 'checkBlob')?.slot;
@@ -117,7 +262,7 @@ export class RollupContract {
117
262
 
118
263
  static getFromConfig(config: L1ReaderConfig) {
119
264
  const client = getPublicClient(config);
120
- const address = config.l1Contracts.rollupAddress.toString();
265
+ const address = config.rollupAddress.toString();
121
266
  return new RollupContract(client, address);
122
267
  }
123
268
 
@@ -131,8 +276,8 @@ export class RollupContract {
131
276
  this.rollup = getContract({ address, abi: RollupAbi, client });
132
277
  }
133
278
 
134
- getGSE() {
135
- return this.rollup.read.getGSE();
279
+ async getGSE(): Promise<EthAddress> {
280
+ return EthAddress.fromString(await this.rollup.read.getGSE());
136
281
  }
137
282
 
138
283
  public get address() {
@@ -143,54 +288,38 @@ export class RollupContract {
143
288
  return this.rollup;
144
289
  }
145
290
 
146
- public async getSlashingProposer(): Promise<
147
- EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
148
- > {
291
+ public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
149
292
  const slasher = await this.getSlasherContract();
150
293
  if (!slasher) {
151
294
  return undefined;
152
295
  }
153
296
 
154
297
  const proposerAddress = await slasher.getProposer();
155
- const proposerAbi = [
156
- {
157
- type: 'function',
158
- name: 'SLASHING_PROPOSER_TYPE',
159
- inputs: [],
160
- outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
161
- stateMutability: 'view',
162
- },
163
- ] as const;
164
-
165
- const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
166
- const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
167
- if (proposerType === SlashingProposerType.Tally.valueOf()) {
168
- return new TallySlashingProposerContract(this.client, proposerAddress);
169
- } else if (proposerType === SlashingProposerType.Empire.valueOf()) {
170
- return new EmpireSlashingProposerContract(this.client, proposerAddress);
171
- } else {
172
- throw new Error(`Unknown slashing proposer type: ${proposerType}`);
298
+ if (proposerAddress.isZero()) {
299
+ return undefined;
173
300
  }
301
+
302
+ return new SlashingProposerContract(this.client, proposerAddress);
174
303
  }
175
304
 
176
305
  @memoize
177
- getL1StartBlock() {
306
+ getL1StartBlock(): Promise<bigint> {
178
307
  return this.rollup.read.L1_BLOCK_AT_GENESIS();
179
308
  }
180
309
 
181
310
  @memoize
182
- getL1GenesisTime() {
311
+ getL1GenesisTime(): Promise<bigint> {
183
312
  return this.rollup.read.getGenesisTime();
184
313
  }
185
314
 
186
315
  @memoize
187
- getProofSubmissionEpochs() {
188
- return this.rollup.read.getProofSubmissionEpochs();
316
+ async getProofSubmissionEpochs(): Promise<number> {
317
+ return Number(await this.rollup.read.getProofSubmissionEpochs());
189
318
  }
190
319
 
191
320
  @memoize
192
- getEpochDuration() {
193
- return this.rollup.read.getEpochDuration();
321
+ async getEpochDuration(): Promise<number> {
322
+ return Number(await this.rollup.read.getEpochDuration());
194
323
  }
195
324
 
196
325
  @memoize
@@ -199,68 +328,82 @@ export class RollupContract {
199
328
  }
200
329
 
201
330
  @memoize
202
- getTargetCommitteeSize() {
203
- return this.rollup.read.getTargetCommitteeSize();
331
+ async getTargetCommitteeSize(): Promise<number> {
332
+ return Number(await this.rollup.read.getTargetCommitteeSize());
204
333
  }
205
334
 
206
335
  @memoize
207
- getEjectionThreshold() {
336
+ getEjectionThreshold(): Promise<bigint> {
208
337
  return this.rollup.read.getEjectionThreshold();
209
338
  }
210
339
 
211
340
  @memoize
212
- getLocalEjectionThreshold() {
341
+ getLocalEjectionThreshold(): Promise<bigint> {
213
342
  return this.rollup.read.getLocalEjectionThreshold();
214
343
  }
215
344
 
216
345
  @memoize
217
- getLagInEpochsForValidatorSet() {
218
- return this.rollup.read.getLagInEpochsForValidatorSet();
346
+ async getLagInEpochsForValidatorSet(): Promise<number> {
347
+ return Number(await this.rollup.read.getLagInEpochsForValidatorSet());
219
348
  }
220
349
 
221
350
  @memoize
222
- getLagInEpochsForRandao() {
223
- return this.rollup.read.getLagInEpochsForRandao();
351
+ async getLagInEpochsForRandao(): Promise<number> {
352
+ return Number(await this.rollup.read.getLagInEpochsForRandao());
224
353
  }
225
354
 
226
355
  @memoize
227
- getActivationThreshold() {
356
+ getActivationThreshold(): Promise<bigint> {
228
357
  return this.rollup.read.getActivationThreshold();
229
358
  }
230
359
 
231
360
  @memoize
232
- getExitDelay() {
233
- return this.rollup.read.getExitDelay();
361
+ async getExitDelay(): Promise<number> {
362
+ return Number(await this.rollup.read.getExitDelay());
234
363
  }
235
364
 
236
365
  @memoize
237
- getManaTarget() {
366
+ getManaTarget(): Promise<bigint> {
238
367
  return this.rollup.read.getManaTarget();
239
368
  }
240
369
 
241
370
  @memoize
242
- getProvingCostPerMana() {
371
+ getProvingCostPerMana(): Promise<bigint> {
243
372
  return this.rollup.read.getProvingCostPerManaInEth();
244
373
  }
245
374
 
246
375
  @memoize
247
- getProvingCostPerManaInFeeAsset() {
376
+ getProvingCostPerManaInFeeAsset(): Promise<bigint> {
248
377
  return this.rollup.read.getProvingCostPerManaInFeeAsset();
249
378
  }
250
379
 
251
380
  @memoize
252
- getManaLimit() {
381
+ getManaLimit(): Promise<bigint> {
253
382
  return this.rollup.read.getManaLimit();
254
383
  }
255
384
 
256
385
  @memoize
257
- getVersion() {
386
+ getVersion(): Promise<bigint> {
258
387
  return this.rollup.read.getVersion();
259
388
  }
260
389
 
261
390
  @memoize
262
- async getGenesisArchiveTreeRoot(): Promise<`0x${string}`> {
263
- return await this.rollup.read.archiveAt([0n]);
391
+ async getGenesisArchiveTreeRoot(): Promise<Fr> {
392
+ return Fr.fromString(await this.rollup.read.archiveAt([0n]));
393
+ }
394
+
395
+ @memoize
396
+ async getVkTreeRoot(): Promise<Fr> {
397
+ const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
398
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
399
+ return Fr.fromString(value ?? '0x0');
400
+ }
401
+
402
+ @memoize
403
+ async getProtocolContractsHash(): Promise<Fr> {
404
+ const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
405
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
406
+ return Fr.fromString(value ?? '0x0');
264
407
  }
265
408
 
266
409
  /**
@@ -275,13 +418,25 @@ export class RollupContract {
275
418
  slotDuration: number;
276
419
  epochDuration: number;
277
420
  proofSubmissionEpochs: number;
421
+ targetCommitteeSize: number;
422
+ rollupManaLimit: number;
278
423
  }> {
279
- const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs] = await Promise.all([
424
+ const [
425
+ l1StartBlock,
426
+ l1GenesisTime,
427
+ slotDuration,
428
+ epochDuration,
429
+ proofSubmissionEpochs,
430
+ targetCommitteeSize,
431
+ rollupManaLimit,
432
+ ] = await Promise.all([
280
433
  this.getL1StartBlock(),
281
434
  this.getL1GenesisTime(),
282
435
  this.getSlotDuration(),
283
436
  this.getEpochDuration(),
284
437
  this.getProofSubmissionEpochs(),
438
+ this.getTargetCommitteeSize(),
439
+ this.getManaLimit(),
285
440
  ]);
286
441
  return {
287
442
  l1StartBlock,
@@ -289,30 +444,88 @@ export class RollupContract {
289
444
  slotDuration,
290
445
  epochDuration: Number(epochDuration),
291
446
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
447
+ targetCommitteeSize,
448
+ rollupManaLimit: Number(rollupManaLimit),
292
449
  };
293
450
  }
294
451
 
295
- getSlasherAddress() {
296
- return this.rollup.read.getSlasher();
452
+ async getSlasherAddress(): Promise<EthAddress> {
453
+ return EthAddress.fromString(await this.rollup.read.getSlasher());
454
+ }
455
+
456
+ /**
457
+ * Returns the configured escape hatch contract address, or zero if disabled.
458
+ */
459
+ async getEscapeHatchAddress(): Promise<EthAddress> {
460
+ return EthAddress.fromString(await this.rollup.read.getEscapeHatch());
461
+ }
462
+
463
+ private async getEscapeHatchContract(): Promise<
464
+ GetContractReturnType<typeof EscapeHatchAbi, ViemClient> | undefined
465
+ > {
466
+ const escapeHatchAddress = await this.getEscapeHatchAddress();
467
+ if (escapeHatchAddress.isZero()) {
468
+ return undefined;
469
+ }
470
+
471
+ // Cache the viem contract wrapper since it will be used frequently.
472
+ if (!this.cachedEscapeHatch || !this.cachedEscapeHatch.address.equals(escapeHatchAddress)) {
473
+ this.cachedEscapeHatch = {
474
+ address: escapeHatchAddress,
475
+ contract: getContract({
476
+ address: escapeHatchAddress.toString(),
477
+ abi: EscapeHatchAbi,
478
+ client: this.client,
479
+ }),
480
+ };
481
+ }
482
+
483
+ return this.cachedEscapeHatch.contract;
484
+ }
485
+
486
+ /**
487
+ * Returns whether the escape hatch is open for the given epoch.
488
+ * If escape hatch is not configured, returns false.
489
+ *
490
+ * This function is intentionally defensive: any failure to query the escape hatch
491
+ * (RPC issues, transient errors, etc.) is treated as "closed" to avoid callers
492
+ * needing to sprinkle try/catch everywhere.
493
+ */
494
+ async isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean> {
495
+ try {
496
+ const escapeHatch = await this.getEscapeHatchContract();
497
+ if (!escapeHatch) {
498
+ return false;
499
+ }
500
+
501
+ const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
502
+ return isOpen;
503
+ } catch (err) {
504
+ this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
505
+ epoch: Number(epoch),
506
+ error: err,
507
+ });
508
+ return false;
509
+ }
297
510
  }
298
511
 
299
512
  /**
300
513
  * Returns a SlasherContract instance for interacting with the slasher contract.
301
514
  */
302
515
  async getSlasherContract(): Promise<SlasherContract | undefined> {
303
- const slasherAddress = EthAddress.fromString(await this.getSlasherAddress());
516
+ const slasherAddress = await this.getSlasherAddress();
304
517
  if (slasherAddress.isZero()) {
305
518
  return undefined;
306
519
  }
307
520
  return new SlasherContract(this.client, slasherAddress);
308
521
  }
309
522
 
310
- getOwner() {
311
- return this.rollup.read.owner();
523
+ async getOwner(): Promise<EthAddress> {
524
+ return EthAddress.fromString(await this.rollup.read.owner());
312
525
  }
313
526
 
314
- getActiveAttesterCount() {
315
- return this.rollup.read.getActiveAttesterCount();
527
+ async getActiveAttesterCount(): Promise<number> {
528
+ return Number(await this.rollup.read.getActiveAttesterCount());
316
529
  }
317
530
 
318
531
  public async getSlashingProposerAddress() {
@@ -323,31 +536,50 @@ export class RollupContract {
323
536
  return await slasher.getProposer();
324
537
  }
325
538
 
326
- getCheckpointReward() {
539
+ getCheckpointReward(): Promise<bigint> {
327
540
  return this.rollup.read.getCheckpointReward();
328
541
  }
329
542
 
330
- async getCheckpointNumber(): Promise<CheckpointNumber> {
331
- return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber());
543
+ async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
544
+ await checkBlockTag(options?.blockNumber, this.client);
545
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
332
546
  }
333
547
 
334
- async getProvenCheckpointNumber(): Promise<CheckpointNumber> {
335
- return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber());
548
+ async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
549
+ await checkBlockTag(options?.blockNumber, this.client);
550
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
336
551
  }
337
552
 
338
- async getSlotNumber(): Promise<SlotNumber> {
339
- return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot());
553
+ async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
554
+ await checkBlockTag(options?.blockNumber, this.client);
555
+ return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
340
556
  }
341
557
 
342
- getL1FeesAt(timestamp: bigint) {
343
- return this.rollup.read.getL1FeesAt([timestamp]);
558
+ async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
559
+ await checkBlockTag(options?.blockNumber, this.client);
560
+ const result = await this.rollup.read.getL1FeesAt([timestamp], options);
561
+ return {
562
+ baseFee: result.baseFee,
563
+ blobFee: result.blobFee,
564
+ };
565
+ }
566
+
567
+ async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
568
+ const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
569
+ return {
570
+ excessMana: result.excessMana,
571
+ manaUsed: result.manaUsed,
572
+ ethPerFeeAsset: result.ethPerFeeAsset,
573
+ congestionCost: result.congestionCost,
574
+ proverCost: result.proverCost,
575
+ };
344
576
  }
345
577
 
346
- getFeeAssetPerEth() {
347
- return this.rollup.read.getFeeAssetPerEth();
578
+ getEthPerFeeAsset(): Promise<bigint> {
579
+ return this.rollup.read.getEthPerFeeAsset();
348
580
  }
349
581
 
350
- async getCommitteeAt(timestamp: bigint): Promise<readonly `0x${string}`[] | undefined> {
582
+ async getCommitteeAt(timestamp: bigint): Promise<EthAddress[] | undefined> {
351
583
  const { result } = await this.client
352
584
  .simulateContract({
353
585
  address: this.address,
@@ -362,22 +594,22 @@ export class RollupContract {
362
594
  throw e;
363
595
  });
364
596
 
365
- return result;
597
+ return result ? result.map(addr => EthAddress.fromString(addr)) : undefined;
366
598
  }
367
599
 
368
- getSampleSeedAt(timestamp: bigint) {
369
- return this.rollup.read.getSampleSeedAt([timestamp]);
600
+ async getSampleSeedAt(timestamp: bigint): Promise<Buffer32> {
601
+ return Buffer32.fromBigInt(await this.rollup.read.getSampleSeedAt([timestamp]));
370
602
  }
371
603
 
372
- getCurrentSampleSeed() {
373
- return this.rollup.read.getCurrentSampleSeed();
604
+ async getCurrentSampleSeed(): Promise<Buffer32> {
605
+ return Buffer32.fromBigInt(await this.rollup.read.getCurrentSampleSeed());
374
606
  }
375
607
 
376
608
  async getCurrentEpoch(): Promise<EpochNumber> {
377
609
  return EpochNumber.fromBigInt(await this.rollup.read.getCurrentEpoch());
378
610
  }
379
611
 
380
- async getCurrentEpochCommittee(): Promise<readonly `0x${string}`[] | undefined> {
612
+ async getCurrentEpochCommittee(): Promise<EthAddress[] | undefined> {
381
613
  const { result } = await this.client
382
614
  .simulateContract({
383
615
  address: this.address,
@@ -392,10 +624,10 @@ export class RollupContract {
392
624
  throw e;
393
625
  });
394
626
 
395
- return result;
627
+ return result ? result.map(addr => EthAddress.fromString(addr)) : undefined;
396
628
  }
397
629
 
398
- async getCurrentProposer() {
630
+ async getCurrentProposer(): Promise<EthAddress> {
399
631
  const { result } = await this.client.simulateContract({
400
632
  address: this.address,
401
633
  abi: RollupAbi,
@@ -403,10 +635,10 @@ export class RollupContract {
403
635
  args: [],
404
636
  });
405
637
 
406
- return result;
638
+ return EthAddress.fromString(result);
407
639
  }
408
640
 
409
- async getProposerAt(timestamp: bigint) {
641
+ async getProposerAt(timestamp: bigint): Promise<EthAddress> {
410
642
  const { result } = await this.client.simulateContract({
411
643
  address: this.address,
412
644
  abi: RollupAbi,
@@ -414,11 +646,27 @@ export class RollupContract {
414
646
  args: [timestamp],
415
647
  });
416
648
 
417
- return result;
649
+ return EthAddress.fromString(result);
418
650
  }
419
651
 
420
- getCheckpoint(checkpointNumber: CheckpointNumber) {
421
- return this.rollup.read.getCheckpoint([BigInt(checkpointNumber)]);
652
+ async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
653
+ await checkBlockTag(options?.blockNumber, this.client);
654
+ const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
655
+ return {
656
+ archive: Fr.fromString(result.archive),
657
+ headerHash: Buffer32.fromString(result.headerHash),
658
+ blobCommitmentsHash: Buffer32.fromString(result.blobCommitmentsHash),
659
+ attestationsHash: Buffer32.fromString(result.attestationsHash),
660
+ payloadDigest: Buffer32.fromString(result.payloadDigest),
661
+ slotNumber: SlotNumber.fromBigInt(result.slotNumber),
662
+ feeHeader: {
663
+ excessMana: result.feeHeader.excessMana,
664
+ manaUsed: result.feeHeader.manaUsed,
665
+ ethPerFeeAsset: result.feeHeader.ethPerFeeAsset,
666
+ congestionCost: result.feeHeader.congestionCost,
667
+ proverCost: result.feeHeader.proverCost,
668
+ },
669
+ };
422
670
  }
423
671
 
424
672
  /** Returns the pending checkpoint from the rollup contract */
@@ -436,6 +684,30 @@ export class RollupContract {
436
684
  );
437
685
  }
438
686
 
687
+ /**
688
+ * Returns the effective pending checkpoint, accounting for potential prunes.
689
+ * When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
690
+ * This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
691
+ * @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
692
+ * @param options - Optional L1 block number to pin the queries to.
693
+ */
694
+ getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
695
+ return retry(
696
+ async () => {
697
+ const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
698
+ const canPrune = await this.canPruneAtTime(timestamp, options);
699
+ if (canPrune) {
700
+ const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
701
+ return await this.getCheckpoint(provenCheckpointNumber, options);
702
+ }
703
+ const pendingCheckpointNumber = await this.getCheckpointNumber(options);
704
+ return await this.getCheckpoint(pendingCheckpointNumber, options);
705
+ },
706
+ 'getting effective pending checkpoint',
707
+ makeBackoff([0.5, 0.5, 0.5]),
708
+ );
709
+ }
710
+
439
711
  async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
440
712
  const { pending, proven } = await this.rollup.read.getTips();
441
713
  return {
@@ -444,16 +716,16 @@ export class RollupContract {
444
716
  };
445
717
  }
446
718
 
447
- getTimestampForSlot(slot: SlotNumber) {
719
+ getTimestampForSlot(slot: SlotNumber): Promise<bigint> {
448
720
  return this.rollup.read.getTimestampForSlot([BigInt(slot)]);
449
721
  }
450
722
 
451
- getEntryQueueLength() {
452
- return this.rollup.read.getEntryQueueLength();
723
+ async getEntryQueueLength(): Promise<number> {
724
+ return Number(await this.rollup.read.getEntryQueueLength());
453
725
  }
454
726
 
455
- getAvailableValidatorFlushes() {
456
- return this.rollup.read.getAvailableValidatorFlushes();
727
+ async getAvailableValidatorFlushes(): Promise<number> {
728
+ return Number(await this.rollup.read.getAvailableValidatorFlushes());
457
729
  }
458
730
 
459
731
  async getNextFlushableEpoch(): Promise<EpochNumber> {
@@ -509,10 +781,11 @@ export class RollupContract {
509
781
  return EpochNumber.fromBigInt(await this.rollup.read.getEpochAtSlot([BigInt(slotNumber)]));
510
782
  }
511
783
 
512
- getEpochProofPublicInputs(
784
+ async getEpochProofPublicInputs(
513
785
  args: readonly [bigint, bigint, EpochProofPublicInputArgs, readonly `0x${string}`[], `0x${string}`],
514
- ) {
515
- return this.rollup.read.getEpochProofPublicInputs(args);
786
+ ): Promise<Fr[]> {
787
+ const result = await this.rollup.read.getEpochProofPublicInputs(args);
788
+ return result.map(Fr.fromString);
516
789
  }
517
790
 
518
791
  public async validateHeader(
@@ -553,14 +826,13 @@ export class RollupContract {
553
826
  * timestamp of the next L1 block
554
827
  * @throws otherwise
555
828
  */
556
- public async canProposeAtNextEthBlock(
829
+ public async canProposeAt(
557
830
  archive: Buffer,
558
831
  account: `0x${string}` | Account,
559
- slotDuration: number,
560
- opts: { forcePendingCheckpointNumber?: CheckpointNumber } = {},
832
+ timestamp: bigint,
833
+ stateOverride: StateOverride = [],
561
834
  ): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
562
- const latestBlock = await this.client.getBlock();
563
- const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
835
+ const timeOfNextL1Slot = timestamp;
564
836
  const who = typeof account === 'string' ? account : account.address;
565
837
 
566
838
  try {
@@ -572,7 +844,7 @@ export class RollupContract {
572
844
  functionName: 'canProposeAtTime',
573
845
  args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
574
846
  account,
575
- stateOverride: await this.makePendingCheckpointNumberOverride(opts.forcePendingCheckpointNumber),
847
+ stateOverride,
576
848
  });
577
849
 
578
850
  return {
@@ -586,20 +858,32 @@ export class RollupContract {
586
858
  }
587
859
 
588
860
  /**
589
- * Returns a state override that sets the pending checkpoint number to the specified value. Useful for simulations.
590
- * Requires querying the current state of the contract to get the current proven checkpoint number, as they are both
591
- * stored in the same slot. If the argument is undefined, it returns an empty override.
861
+ * Returns a state override that sets the pending and/or proven checkpoint numbers. Useful for simulations.
862
+ * Both values share a single storage slot (pending in the upper 128 bits, proven in the lower 128 bits), so
863
+ * a single combined override is emitted to avoid the second state-diff clobbering the first. The current live
864
+ * value is read once to preserve any half not being overridden. Returns an empty override if neither is set.
865
+ *
866
+ * Throws if the resulting `proven > pending`, which would crash the simulation: `STFLib.canPruneAtTime` calls
867
+ * `getEpochForCheckpoint(proven + 1)` whenever `pending != proven`, and that asserts `_n <= tips.pending`.
592
868
  */
593
- public async makePendingCheckpointNumberOverride(
594
- forcePendingCheckpointNumber: CheckpointNumber | undefined,
595
- ): Promise<StateOverride> {
596
- if (forcePendingCheckpointNumber === undefined) {
869
+ public async makeChainTipsOverride(override: {
870
+ pending?: CheckpointNumber;
871
+ proven?: CheckpointNumber;
872
+ }): Promise<StateOverride> {
873
+ if (override.pending === undefined && override.proven === undefined) {
597
874
  return [];
598
875
  }
599
876
  const slot = RollupContract.stfStorageSlot;
600
877
  const currentValue = await this.client.getStorageAt({ address: this.address, slot });
601
- const currentProvenCheckpointNumber = currentValue ? hexToBigInt(currentValue) & ((1n << 128n) - 1n) : 0n;
602
- const newValue = (BigInt(forcePendingCheckpointNumber) << 128n) | currentProvenCheckpointNumber;
878
+ const currentRaw = currentValue ? hexToBigInt(currentValue) : 0n;
879
+ const currentPending = currentRaw >> 128n;
880
+ const currentProven = currentRaw & ((1n << 128n) - 1n);
881
+ const newPending = override.pending !== undefined ? BigInt(override.pending) : currentPending;
882
+ const newProven = override.proven !== undefined ? BigInt(override.proven) : currentProven;
883
+ if (newProven > newPending) {
884
+ throw new Error(`Invalid chain tips override: proven (${newProven}) > pending (${newPending})`);
885
+ }
886
+ const newValue = (newPending << 128n) | newProven;
603
887
  return [
604
888
  {
605
889
  address: this.address,
@@ -608,6 +892,168 @@ export class RollupContract {
608
892
  ];
609
893
  }
610
894
 
895
+ /**
896
+ * Returns a state override that patches `tempCheckpointLogs[checkpointNumber]` with every field that
897
+ * the L1 contract reads during `propose()` for the checkpoint that builds on top of it (proposer
898
+ * pipelining simulation). Mirrors the writes done by `ProposeLib.addTempCheckpointLog`.
899
+ *
900
+ * `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path
901
+ * never asserts against them, so leaving them at storage zero is harmless.
902
+ */
903
+ public async makeTempCheckpointLogOverride(
904
+ checkpointNumber: CheckpointNumber,
905
+ fields: TempCheckpointLogOverrideFields,
906
+ ): Promise<StateOverride> {
907
+ const constants = await this.getRollupConstants();
908
+ const slotAt = (field: TempCheckpointLogField) =>
909
+ `0x${this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants).toString(16).padStart(64, '0')}` as const;
910
+ const word = (v: bigint) => `0x${v.toString(16).padStart(64, '0')}` as const;
911
+
912
+ const stateDiff: { slot: `0x${string}`; value: `0x${string}` }[] = [];
913
+
914
+ if (fields.headerHash) {
915
+ stateDiff.push({ slot: slotAt(TempCheckpointLogField.HeaderHash), value: fields.headerHash.toString() });
916
+ }
917
+ if (fields.outHash) {
918
+ stateDiff.push({ slot: slotAt(TempCheckpointLogField.OutHash), value: fields.outHash.toString() });
919
+ }
920
+ if (fields.payloadDigest) {
921
+ stateDiff.push({
922
+ slot: slotAt(TempCheckpointLogField.PayloadDigest),
923
+ value: fields.payloadDigest.toString() as `0x${string}`,
924
+ });
925
+ }
926
+ if (fields.slotNumber !== undefined) {
927
+ // CompressedSlot is uint32 on L1 (SafeCast.toUint32 reverts on overflow). Match that behavior here
928
+ // so a malformed override surfaces immediately rather than silently truncating into a wrong slot.
929
+ const slotNumber = BigInt(fields.slotNumber);
930
+ if (slotNumber < 0n || slotNumber > 0xffffffffn) {
931
+ throw new Error(`slotNumber ${slotNumber} does not fit in uint32`);
932
+ }
933
+ stateDiff.push({ slot: slotAt(TempCheckpointLogField.SlotNumber), value: word(slotNumber) });
934
+ }
935
+ if (fields.feeHeader) {
936
+ stateDiff.push({
937
+ slot: slotAt(TempCheckpointLogField.FeeHeader),
938
+ value: word(RollupContract.compressFeeHeader(fields.feeHeader)),
939
+ });
940
+ }
941
+
942
+ if (stateDiff.length === 0) {
943
+ return [];
944
+ }
945
+ return [{ address: this.address, stateDiff }];
946
+ }
947
+
948
+ /**
949
+ * Returns a state override that sets archives[checkpointNumber] to the given archive value.
950
+ * Used when simulating a canProposeAtTime call where the local archive differs from L1
951
+ * (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
952
+ */
953
+ public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
954
+ const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
955
+ const archiveSlot = hexToBigInt(
956
+ keccak256(
957
+ encodeAbiParameters(
958
+ [{ type: 'uint256' }, { type: 'uint256' }],
959
+ [BigInt(checkpointNumber), archivesMappingBase],
960
+ ),
961
+ ),
962
+ );
963
+ return [
964
+ {
965
+ address: this.address,
966
+ stateDiff: [
967
+ {
968
+ slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
969
+ value: archive.toString(),
970
+ },
971
+ ],
972
+ },
973
+ ];
974
+ }
975
+
976
+ /** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
977
+ public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
978
+ type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
979
+ const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
980
+ for (const override of overrides) {
981
+ for (const entry of override) {
982
+ const key = entry.address.toLowerCase();
983
+ const existing = byAddress.get(key);
984
+ if (existing) {
985
+ existing.stateDiff.push(...(entry.stateDiff ?? []));
986
+ if (entry.balance !== undefined) {
987
+ existing.balance = entry.balance;
988
+ }
989
+ } else {
990
+ byAddress.set(key, {
991
+ address: entry.address,
992
+ balance: entry.balance,
993
+ stateDiff: [...(entry.stateDiff ?? [])],
994
+ });
995
+ }
996
+ }
997
+ }
998
+ return [...byAddress.values()];
999
+ }
1000
+
1001
+ /** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
1002
+ public static compressFeeHeader(feeHeader: FeeHeader): bigint {
1003
+ const MASK_48_BITS = (1n << 48n) - 1n;
1004
+ const MASK_64_BITS = (1n << 64n) - 1n;
1005
+ const MASK_63_BITS = (1n << 63n) - 1n;
1006
+
1007
+ let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
1008
+ value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
1009
+ value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
1010
+ value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
1011
+ value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
1012
+ value |= 1n << 255n; // preheat flag
1013
+ return value;
1014
+ }
1015
+
1016
+ /** Computes the fee header for a child checkpoint given parent fee header and child data.
1017
+ * Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
1018
+ public static computeChildFeeHeader(
1019
+ parentFeeHeader: FeeHeader,
1020
+ childManaUsed: bigint,
1021
+ feeAssetPriceModifier: bigint,
1022
+ manaTarget: bigint,
1023
+ ): FeeHeader {
1024
+ const MIN_ETH_PER_FEE_ASSET = 100n;
1025
+ const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
1026
+
1027
+ // excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
1028
+ const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
1029
+ const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
1030
+
1031
+ // ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
1032
+ const parentPrice =
1033
+ parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
1034
+ let newPrice: bigint;
1035
+ if (feeAssetPriceModifier >= 0n) {
1036
+ newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
1037
+ } else {
1038
+ const absMod = -feeAssetPriceModifier;
1039
+ newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
1040
+ }
1041
+ if (newPrice < MIN_ETH_PER_FEE_ASSET) {
1042
+ newPrice = MIN_ETH_PER_FEE_ASSET;
1043
+ }
1044
+ if (newPrice > MAX_ETH_PER_FEE_ASSET) {
1045
+ newPrice = MAX_ETH_PER_FEE_ASSET;
1046
+ }
1047
+
1048
+ return {
1049
+ excessMana,
1050
+ manaUsed: childManaUsed,
1051
+ ethPerFeeAsset: newPrice,
1052
+ congestionCost: 0n,
1053
+ proverCost: 0n,
1054
+ };
1055
+ }
1056
+
611
1057
  /** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
612
1058
  public buildInvalidateBadAttestationRequest(
613
1059
  checkpointNumber: CheckpointNumber,
@@ -617,6 +1063,7 @@ export class RollupContract {
617
1063
  ): L1TxRequest {
618
1064
  return {
619
1065
  to: this.address,
1066
+ abi: RollupAbi,
620
1067
  data: encodeFunctionData({
621
1068
  abi: RollupAbi,
622
1069
  functionName: 'invalidateBadAttestation',
@@ -638,6 +1085,7 @@ export class RollupContract {
638
1085
  ): L1TxRequest {
639
1086
  return {
640
1087
  to: this.address,
1088
+ abi: RollupAbi,
641
1089
  data: encodeFunctionData({
642
1090
  abi: RollupAbi,
643
1091
  functionName: 'invalidateInsufficientAttestations',
@@ -654,90 +1102,133 @@ export class RollupContract {
654
1102
  return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
655
1103
  }
656
1104
 
657
- getManaBaseFeeAt(timestamp: bigint, inFeeAsset: boolean) {
658
- return this.rollup.read.getManaBaseFeeAt([timestamp, inFeeAsset]);
1105
+ getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
1106
+ return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
1107
+ }
1108
+
1109
+ async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
1110
+ const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
1111
+ return {
1112
+ sequencerCost: result.sequencerCost,
1113
+ proverCost: result.proverCost,
1114
+ congestionCost: result.congestionCost,
1115
+ congestionMultiplier: result.congestionMultiplier,
1116
+ };
659
1117
  }
660
1118
 
661
1119
  async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
662
1120
  return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([timestamp]));
663
1121
  }
664
1122
 
665
- async status(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }) {
1123
+ async status(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<RollupStatusResponse> {
666
1124
  await checkBlockTag(options?.blockNumber, this.client);
667
- return this.rollup.read.status([BigInt(checkpointNumber)], options);
1125
+ const result = await this.rollup.read.status([BigInt(checkpointNumber)], options);
1126
+ return {
1127
+ provenCheckpointNumber: CheckpointNumber.fromBigInt(result[0]),
1128
+ provenArchive: Fr.fromString(result[1]),
1129
+ pendingCheckpointNumber: CheckpointNumber.fromBigInt(result[2]),
1130
+ pendingArchive: Fr.fromString(result[3]),
1131
+ archiveOfMyCheckpoint: Fr.fromString(result[4]),
1132
+ };
668
1133
  }
669
1134
 
670
- async canPruneAtTime(timestamp: bigint, options?: { blockNumber?: bigint }) {
1135
+ async canPruneAtTime(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<boolean> {
671
1136
  await checkBlockTag(options?.blockNumber, this.client);
672
1137
  return this.rollup.read.canPruneAtTime([timestamp], options);
673
1138
  }
674
1139
 
675
- archive() {
676
- return this.rollup.read.archive();
1140
+ async archive(): Promise<Fr> {
1141
+ return Fr.fromString(await this.rollup.read.archive());
677
1142
  }
678
1143
 
679
- archiveAt(checkpointNumber: CheckpointNumber) {
680
- return this.rollup.read.archiveAt([BigInt(checkpointNumber)]);
1144
+ async archiveAt(checkpointNumber: CheckpointNumber): Promise<Fr> {
1145
+ return Fr.fromString(await this.rollup.read.archiveAt([BigInt(checkpointNumber)]));
681
1146
  }
682
1147
 
683
- getSequencerRewards(address: Hex | EthAddress) {
1148
+ getSequencerRewards(address: Hex | EthAddress): Promise<bigint> {
684
1149
  if (address instanceof EthAddress) {
685
1150
  address = address.toString();
686
1151
  }
687
1152
  return this.rollup.read.getSequencerRewards([address]);
688
1153
  }
689
1154
 
690
- getSpecificProverRewardsForEpoch(epoch: bigint, prover: Hex | EthAddress) {
1155
+ getSpecificProverRewardsForEpoch(epoch: bigint, prover: Hex | EthAddress): Promise<bigint> {
691
1156
  if (prover instanceof EthAddress) {
692
1157
  prover = prover.toString();
693
1158
  }
694
1159
  return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
695
1160
  }
696
1161
 
697
- async getAttesters() {
1162
+ async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
698
1163
  const attesterSize = await this.getActiveAttesterCount();
699
1164
  const gse = new GSEContract(this.client, await this.getGSE());
700
- const ts = (await this.client.getBlock()).timestamp;
701
-
702
- const indices = Array.from({ length: Number(attesterSize) }, (_, i) => BigInt(i));
1165
+ const ts = timestamp ?? (await this.client.getBlock()).timestamp;
1166
+ const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
703
1167
  const chunks = chunk(indices, 1000);
704
1168
 
705
- return (await Promise.all(chunks.map(chunk => gse.getAttestersFromIndicesAtTime(this.address, ts, chunk)))).flat();
1169
+ const results = await Promise.all(chunks.map(chunk => gse.getAttestersFromIndicesAtTime(this.address, ts, chunk)));
1170
+ return results.flat().map(addr => EthAddress.fromString(addr));
706
1171
  }
707
1172
 
708
- getAttesterView(address: Hex | EthAddress) {
1173
+ async getAttesterView(address: Hex | EthAddress): Promise<AttesterView> {
709
1174
  if (address instanceof EthAddress) {
710
1175
  address = address.toString();
711
1176
  }
712
- return this.rollup.read.getAttesterView([address]);
1177
+ const result = await this.rollup.read.getAttesterView([address]);
1178
+ return {
1179
+ status: result.status as AttesterStatus,
1180
+ effectiveBalance: result.effectiveBalance,
1181
+ exit: {
1182
+ withdrawalId: result.exit.withdrawalId,
1183
+ amount: result.exit.amount,
1184
+ exitableAt: result.exit.exitableAt,
1185
+ recipientOrWithdrawer: EthAddress.fromString(result.exit.recipientOrWithdrawer),
1186
+ isRecipient: result.exit.isRecipient,
1187
+ exists: result.exit.exists,
1188
+ },
1189
+ config: {
1190
+ publicKey: {
1191
+ x: result.config.publicKey.x,
1192
+ y: result.config.publicKey.y,
1193
+ },
1194
+ withdrawer: EthAddress.fromString(result.config.withdrawer),
1195
+ },
1196
+ };
713
1197
  }
714
1198
 
715
- getStatus(address: Hex | EthAddress) {
1199
+ async getStatus(address: Hex | EthAddress): Promise<AttesterStatus> {
716
1200
  if (address instanceof EthAddress) {
717
1201
  address = address.toString();
718
1202
  }
719
- return this.rollup.read.getStatus([address]);
1203
+ return (await this.rollup.read.getStatus([address])) as AttesterStatus;
720
1204
  }
721
1205
 
722
- getBlobCommitmentsHash(checkpointNumber: CheckpointNumber) {
723
- return this.rollup.read.getBlobCommitmentsHash([BigInt(checkpointNumber)]);
1206
+ async getBlobCommitmentsHash(checkpointNumber: CheckpointNumber): Promise<Buffer32> {
1207
+ return Buffer32.fromString(await this.rollup.read.getBlobCommitmentsHash([BigInt(checkpointNumber)]));
724
1208
  }
725
1209
 
726
- getCurrentBlobCommitmentsHash() {
727
- return this.rollup.read.getCurrentBlobCommitmentsHash();
1210
+ async getCurrentBlobCommitmentsHash(): Promise<Buffer32> {
1211
+ return Buffer32.fromString(await this.rollup.read.getCurrentBlobCommitmentsHash());
728
1212
  }
729
1213
 
730
- getStakingAsset() {
731
- return this.rollup.read.getStakingAsset();
1214
+ async getStakingAsset(): Promise<EthAddress> {
1215
+ return EthAddress.fromString(await this.rollup.read.getStakingAsset());
732
1216
  }
733
1217
 
734
- getRewardConfig() {
735
- return this.rollup.read.getRewardConfig();
1218
+ async getRewardConfig(): Promise<RewardConfig> {
1219
+ const result = await this.rollup.read.getRewardConfig();
1220
+ return {
1221
+ rewardDistributor: EthAddress.fromString(result.rewardDistributor),
1222
+ sequencerBps: BigInt(result.sequencerBps),
1223
+ booster: EthAddress.fromString(result.booster),
1224
+ checkpointReward: result.checkpointReward,
1225
+ };
736
1226
  }
737
1227
 
738
1228
  setupEpoch(l1TxUtils: L1TxUtils) {
739
1229
  return l1TxUtils.sendAndMonitorTransaction({
740
1230
  to: this.address,
1231
+ abi: RollupAbi,
741
1232
  data: encodeFunctionData({
742
1233
  abi: RollupAbi,
743
1234
  functionName: 'setupEpoch',
@@ -749,6 +1240,7 @@ export class RollupContract {
749
1240
  vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
750
1241
  return l1TxUtils.sendAndMonitorTransaction({
751
1242
  to: this.address,
1243
+ abi: RollupAbi,
752
1244
  data: encodeFunctionData({
753
1245
  abi: RollupAbi,
754
1246
  functionName: 'vote',
@@ -776,7 +1268,7 @@ export class RollupContract {
776
1268
  }
777
1269
 
778
1270
  public listenToCheckpointInvalidated(
779
- callback: (args: { checkpointNumber: CheckpointNumber }) => unknown,
1271
+ callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
780
1272
  ): WatchContractEventReturnType {
781
1273
  return this.rollup.watchEvent.CheckpointInvalidated(
782
1274
  {},
@@ -785,7 +1277,7 @@ export class RollupContract {
785
1277
  for (const log of logs) {
786
1278
  const args = log.args;
787
1279
  if (args.checkpointNumber !== undefined) {
788
- callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber) });
1280
+ callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber), event: log });
789
1281
  }
790
1282
  }
791
1283
  },
@@ -817,4 +1309,92 @@ export class RollupContract {
817
1309
  },
818
1310
  );
819
1311
  }
1312
+
1313
+ /**
1314
+ * Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
1315
+ * The Rollup inherits from Ownable and emits this event in its constructor, so the event
1316
+ * is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
1317
+ * probe to detect RPC nodes that prune historical logs.
1318
+ */
1319
+ async getOwnershipTransferredEventsAtDeploy() {
1320
+ const l1StartBlock = await this.getL1StartBlock();
1321
+ return await this.rollup.getEvents.OwnershipTransferred({}, { fromBlock: l1StartBlock, toBlock: l1StartBlock });
1322
+ }
1323
+
1324
+ /** Fetches CheckpointProposed events within the given block range. */
1325
+ async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
1326
+ const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
1327
+ return logs
1328
+ .filter(log => log.blockNumber! >= fromBlock && log.blockNumber! <= toBlock)
1329
+ .map(log => ({
1330
+ l1BlockNumber: log.blockNumber!,
1331
+ l1BlockHash: Buffer32.fromString(log.blockHash!),
1332
+ l1TransactionHash: log.transactionHash!,
1333
+ args: {
1334
+ checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
1335
+ archive: Fr.fromString(log.args.archive!),
1336
+ versionedBlobHashes: log.args.versionedBlobHashes!.map(h => Buffer.from(h.slice(2), 'hex')),
1337
+ attestationsHash: (() => {
1338
+ if (!log.args.attestationsHash) {
1339
+ throw new Error(
1340
+ `CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`,
1341
+ );
1342
+ }
1343
+ return Buffer32.fromString(log.args.attestationsHash);
1344
+ })(),
1345
+ payloadDigest: (() => {
1346
+ if (!log.args.payloadDigest) {
1347
+ throw new Error(
1348
+ `CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`,
1349
+ );
1350
+ }
1351
+ return Buffer32.fromString(log.args.payloadDigest);
1352
+ })(),
1353
+ },
1354
+ }));
1355
+ }
1356
+
1357
+ /** Packs pending and proven checkpoint numbers into the chain tips storage format. */
1358
+ static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
1359
+ return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
1360
+ }
1361
+
1362
+ /** Storage slot for the chain tips (offset 0 within the STF storage struct). */
1363
+ static get chainTipsStorageSlot(): bigint {
1364
+ return BigInt(RollupContract.stfStorageSlot);
1365
+ }
1366
+
1367
+ /**
1368
+ * Computes the storage slot for a field within a tempCheckpointLog entry.
1369
+ * @param checkpointNumber - The checkpoint number
1370
+ * @param field - The field within the CompressedTempCheckpointLog struct
1371
+ */
1372
+ async getTempCheckpointLogStorageSlot(
1373
+ checkpointNumber: CheckpointNumber,
1374
+ field: TempCheckpointLogField,
1375
+ ): Promise<bigint> {
1376
+ const [epochDuration, proofSubmissionEpochs] = await Promise.all([
1377
+ this.getEpochDuration(),
1378
+ this.getProofSubmissionEpochs(),
1379
+ ]);
1380
+ return this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, { epochDuration, proofSubmissionEpochs });
1381
+ }
1382
+
1383
+ private computeTempCheckpointLogStorageSlot(
1384
+ checkpointNumber: CheckpointNumber,
1385
+ field: TempCheckpointLogField,
1386
+ constants: { epochDuration: number; proofSubmissionEpochs: number },
1387
+ ): bigint {
1388
+ const fieldOffset = BigInt(field);
1389
+ const { epochDuration, proofSubmissionEpochs } = constants;
1390
+ const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
1391
+ const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
1392
+ const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
1393
+ const entryBase = BigInt(
1394
+ keccak256(
1395
+ encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
1396
+ ),
1397
+ );
1398
+ return entryBase + fieldOffset;
1399
+ }
820
1400
  }