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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) 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 -3
  4. package/dest/config.d.ts +26 -73
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +64 -387
  7. package/dest/contracts/chain_state_override.d.ts +38 -0
  8. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  9. package/dest/contracts/chain_state_override.js +100 -0
  10. package/dest/contracts/empire_base.d.ts +4 -1
  11. package/dest/contracts/empire_base.d.ts.map +1 -1
  12. package/dest/contracts/fee_asset_handler.d.ts +6 -5
  13. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  14. package/dest/contracts/fee_asset_handler.js +11 -9
  15. package/dest/contracts/fee_asset_price_oracle.d.ts +101 -0
  16. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  17. package/dest/contracts/fee_asset_price_oracle.js +651 -0
  18. package/dest/contracts/governance.d.ts +3 -1
  19. package/dest/contracts/governance.d.ts.map +1 -1
  20. package/dest/contracts/governance.js +14 -4
  21. package/dest/contracts/governance_proposer.d.ts +4 -1
  22. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  23. package/dest/contracts/governance_proposer.js +404 -9
  24. package/dest/contracts/inbox.d.ts +24 -3
  25. package/dest/contracts/inbox.d.ts.map +1 -1
  26. package/dest/contracts/inbox.js +41 -1
  27. package/dest/contracts/index.d.ts +6 -3
  28. package/dest/contracts/index.d.ts.map +1 -1
  29. package/dest/contracts/index.js +5 -2
  30. package/dest/contracts/log.d.ts +13 -0
  31. package/dest/contracts/log.d.ts.map +1 -0
  32. package/dest/contracts/log.js +1 -0
  33. package/dest/contracts/multicall.d.ts +51 -2
  34. package/dest/contracts/multicall.d.ts.map +1 -1
  35. package/dest/contracts/multicall.js +87 -1
  36. package/dest/contracts/outbox.d.ts +41 -0
  37. package/dest/contracts/outbox.d.ts.map +1 -0
  38. package/dest/contracts/outbox.js +86 -0
  39. package/dest/contracts/registry.d.ts +3 -1
  40. package/dest/contracts/registry.d.ts.map +1 -1
  41. package/dest/contracts/registry.js +30 -1
  42. package/dest/contracts/rollup.d.ts +5178 -108
  43. package/dest/contracts/rollup.d.ts.map +1 -1
  44. package/dest/contracts/rollup.js +1004 -188
  45. package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +5 -5
  46. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  47. package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +18 -13
  48. package/dest/deploy_aztec_l1_contracts.d.ts +256 -0
  49. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -0
  50. package/dest/deploy_aztec_l1_contracts.js +411 -0
  51. package/dest/deploy_l1_contract.d.ts +68 -0
  52. package/dest/deploy_l1_contract.d.ts.map +1 -0
  53. package/dest/deploy_l1_contract.js +312 -0
  54. package/dest/forwarder_proxy.d.ts +32 -0
  55. package/dest/forwarder_proxy.d.ts.map +1 -0
  56. package/dest/forwarder_proxy.js +93 -0
  57. package/dest/generated/l1-contracts-defaults.d.ts +30 -0
  58. package/dest/generated/l1-contracts-defaults.d.ts.map +1 -0
  59. package/dest/generated/l1-contracts-defaults.js +30 -0
  60. package/dest/l1_artifacts.d.ts +14161 -17158
  61. package/dest/l1_artifacts.d.ts.map +1 -1
  62. package/dest/l1_artifacts.js +9 -24
  63. package/dest/l1_contract_addresses.d.ts +1 -5
  64. package/dest/l1_contract_addresses.d.ts.map +1 -1
  65. package/dest/l1_contract_addresses.js +3 -9
  66. package/dest/l1_reader.d.ts +5 -1
  67. package/dest/l1_reader.d.ts.map +1 -1
  68. package/dest/l1_reader.js +13 -2
  69. package/dest/l1_tx_utils/config.d.ts +9 -3
  70. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  71. package/dest/l1_tx_utils/config.js +31 -4
  72. package/dest/l1_tx_utils/constants.d.ts +8 -2
  73. package/dest/l1_tx_utils/constants.d.ts.map +1 -1
  74. package/dest/l1_tx_utils/constants.js +27 -2
  75. package/dest/l1_tx_utils/factory.d.ts +18 -10
  76. package/dest/l1_tx_utils/factory.d.ts.map +1 -1
  77. package/dest/l1_tx_utils/factory.js +17 -7
  78. package/dest/l1_tx_utils/fee-strategies/index.d.ts +10 -0
  79. package/dest/l1_tx_utils/fee-strategies/index.d.ts.map +1 -0
  80. package/dest/l1_tx_utils/fee-strategies/index.js +12 -0
  81. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +8 -0
  82. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -0
  83. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +129 -0
  84. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts +23 -0
  85. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts.map +1 -0
  86. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +191 -0
  87. package/dest/l1_tx_utils/fee-strategies/types.d.ts +51 -0
  88. package/dest/l1_tx_utils/fee-strategies/types.d.ts.map +1 -0
  89. package/dest/l1_tx_utils/fee-strategies/types.js +3 -0
  90. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +41 -0
  91. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -0
  92. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +42 -0
  93. package/dest/l1_tx_utils/index-blobs.d.ts +3 -0
  94. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -0
  95. package/dest/l1_tx_utils/index-blobs.js +2 -0
  96. package/dest/l1_tx_utils/index.d.ts +4 -1
  97. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  98. package/dest/l1_tx_utils/index.js +3 -0
  99. package/dest/l1_tx_utils/interfaces.d.ts +2 -2
  100. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -1
  101. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +233 -0
  102. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -0
  103. package/dest/l1_tx_utils/l1_fee_analyzer.js +506 -0
  104. package/dest/l1_tx_utils/l1_tx_utils.d.ts +20 -7
  105. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  106. package/dest/l1_tx_utils/l1_tx_utils.js +98 -61
  107. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +4 -15
  108. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  109. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +61 -164
  110. package/dest/l1_tx_utils/tx_delayer.d.ts +56 -0
  111. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  112. package/dest/{test → l1_tx_utils}/tx_delayer.js +65 -36
  113. package/dest/publisher_manager.d.ts +21 -6
  114. package/dest/publisher_manager.d.ts.map +1 -1
  115. package/dest/publisher_manager.js +81 -7
  116. package/dest/queries.d.ts +14 -3
  117. package/dest/queries.d.ts.map +1 -1
  118. package/dest/queries.js +74 -7
  119. package/dest/test/chain_monitor.d.ts +36 -14
  120. package/dest/test/chain_monitor.d.ts.map +1 -1
  121. package/dest/test/chain_monitor.js +45 -11
  122. package/dest/test/eth_cheat_codes.d.ts +18 -4
  123. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  124. package/dest/test/eth_cheat_codes.js +10 -6
  125. package/dest/test/index.d.ts +1 -3
  126. package/dest/test/index.d.ts.map +1 -1
  127. package/dest/test/index.js +0 -2
  128. package/dest/test/rollup_cheat_codes.d.ts +13 -6
  129. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  130. package/dest/test/rollup_cheat_codes.js +41 -7
  131. package/dest/test/start_anvil.d.ts +24 -2
  132. package/dest/test/start_anvil.d.ts.map +1 -1
  133. package/dest/test/start_anvil.js +143 -29
  134. package/dest/test/upgrade_utils.js +2 -2
  135. package/dest/types.d.ts +57 -2
  136. package/dest/types.d.ts.map +1 -1
  137. package/dest/utils.d.ts +16 -3
  138. package/dest/utils.d.ts.map +1 -1
  139. package/dest/utils.js +80 -12
  140. package/package.json +33 -16
  141. package/src/client.ts +28 -2
  142. package/src/config.ts +82 -468
  143. package/src/contracts/README.md +157 -0
  144. package/src/contracts/chain_state_override.ts +147 -0
  145. package/src/contracts/empire_base.ts +3 -1
  146. package/src/contracts/fee_asset_handler.ts +10 -7
  147. package/src/contracts/fee_asset_price_oracle.ts +280 -0
  148. package/src/contracts/governance.ts +13 -4
  149. package/src/contracts/governance_proposer.ts +16 -2
  150. package/src/contracts/inbox.ts +63 -3
  151. package/src/contracts/index.ts +5 -2
  152. package/src/contracts/log.ts +13 -0
  153. package/src/contracts/multicall.ts +70 -3
  154. package/src/contracts/outbox.ts +98 -0
  155. package/src/contracts/registry.ts +31 -1
  156. package/src/contracts/rollup.ts +701 -149
  157. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +18 -14
  158. package/src/deploy_aztec_l1_contracts.ts +646 -0
  159. package/src/deploy_l1_contract.ts +362 -0
  160. package/src/forwarder_proxy.ts +108 -0
  161. package/src/generated/l1-contracts-defaults.ts +32 -0
  162. package/src/l1_artifacts.ts +12 -35
  163. package/src/l1_contract_addresses.ts +21 -26
  164. package/src/l1_reader.ts +22 -2
  165. package/src/l1_tx_utils/config.ts +44 -6
  166. package/src/l1_tx_utils/constants.ts +13 -2
  167. package/src/l1_tx_utils/factory.ts +31 -31
  168. package/src/l1_tx_utils/fee-strategies/index.ts +22 -0
  169. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +163 -0
  170. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +245 -0
  171. package/src/l1_tx_utils/fee-strategies/types.ts +56 -0
  172. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +108 -0
  173. package/src/l1_tx_utils/index-blobs.ts +2 -0
  174. package/src/l1_tx_utils/index.ts +3 -0
  175. package/src/l1_tx_utils/interfaces.ts +1 -1
  176. package/src/l1_tx_utils/l1_fee_analyzer.ts +803 -0
  177. package/src/l1_tx_utils/l1_tx_utils.ts +98 -40
  178. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +75 -210
  179. package/src/{test → l1_tx_utils}/tx_delayer.ts +82 -52
  180. package/src/publisher_manager.ts +107 -10
  181. package/src/queries.ts +81 -7
  182. package/src/test/chain_monitor.ts +82 -18
  183. package/src/test/eth_cheat_codes.ts +8 -6
  184. package/src/test/index.ts +0 -2
  185. package/src/test/rollup_cheat_codes.ts +43 -10
  186. package/src/test/start_anvil.ts +178 -28
  187. package/src/test/upgrade_utils.ts +2 -2
  188. package/src/types.ts +62 -0
  189. package/src/utils.ts +100 -15
  190. package/dest/contracts/empire_slashing_proposer.d.ts +0 -66
  191. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  192. package/dest/contracts/empire_slashing_proposer.js +0 -200
  193. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  194. package/dest/deploy_l1_contracts.d.ts +0 -673
  195. package/dest/deploy_l1_contracts.d.ts.map +0 -1
  196. package/dest/deploy_l1_contracts.js +0 -1491
  197. package/dest/index.d.ts +0 -18
  198. package/dest/index.d.ts.map +0 -1
  199. package/dest/index.js +0 -17
  200. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  201. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  202. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  203. package/dest/test/delayed_tx_utils.d.ts +0 -13
  204. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  205. package/dest/test/delayed_tx_utils.js +0 -28
  206. package/dest/test/tx_delayer.d.ts +0 -36
  207. package/dest/test/tx_delayer.d.ts.map +0 -1
  208. package/src/contracts/empire_slashing_proposer.ts +0 -265
  209. package/src/deploy_l1_contracts.ts +0 -1869
  210. package/src/index.ts +0 -17
  211. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  212. package/src/test/delayed_tx_utils.ts +0 -52
@@ -1,7 +1,12 @@
1
- import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
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';
8
+ import { makeBackoff, retry } from '@aztec/foundation/retry';
9
+ import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
5
10
  import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
6
11
  import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
7
12
 
@@ -10,8 +15,10 @@ import {
10
15
  type Account,
11
16
  type GetContractReturnType,
12
17
  type Hex,
18
+ type Log,
13
19
  type StateOverride,
14
20
  type WatchContractEventReturnType,
21
+ encodeAbiParameters,
15
22
  encodeFunctionData,
16
23
  getContract,
17
24
  hexToBigInt,
@@ -19,16 +26,16 @@ import {
19
26
  } from 'viem';
20
27
 
21
28
  import { getPublicClient } from '../client.js';
22
- import type { DeployL1ContractsReturnType } from '../deploy_l1_contracts.js';
29
+ import type { DeployAztecL1ContractsReturnType } from '../deploy_aztec_l1_contracts.js';
23
30
  import type { L1ContractAddresses } from '../l1_contract_addresses.js';
24
31
  import type { L1ReaderConfig } from '../l1_reader.js';
25
32
  import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
26
33
  import type { ViemClient } from '../types.js';
27
34
  import { formatViemError } from '../utils.js';
28
- import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
29
35
  import { GSEContract } from './gse.js';
36
+ import type { L1EventLog } from './log.js';
30
37
  import { SlasherContract } from './slasher_contract.js';
31
- import { TallySlashingProposerContract } from './tally_slashing_proposer.js';
38
+ import { SlashingProposerContract } from './slashing_proposer.js';
32
39
  import { checkBlockTag } from './utils.js';
33
40
 
34
41
  export type ViemCommitteeAttestation = {
@@ -50,20 +57,22 @@ export type L1RollupContractAddresses = Pick<
50
57
  | 'feeJuiceAddress'
51
58
  | 'stakingAssetAddress'
52
59
  | 'rewardDistributorAddress'
53
- | 'slashFactoryAddress'
54
60
  | 'gseAddress'
55
61
  >;
56
62
 
57
63
  export type EpochProofPublicInputArgs = {
58
64
  previousArchive: `0x${string}`;
59
65
  endArchive: `0x${string}`;
66
+ outHash: `0x${string}`;
60
67
  proverId: `0x${string}`;
61
68
  };
62
69
 
63
70
  export type ViemHeader = {
64
71
  lastArchiveRoot: `0x${string}`;
65
72
  blockHeadersHash: `0x${string}`;
66
- contentCommitment: ViemContentCommitment;
73
+ blobsHash: `0x${string}`;
74
+ inHash: `0x${string}`;
75
+ outHash: `0x${string}`;
67
76
  slotNumber: bigint;
68
77
  timestamp: bigint;
69
78
  coinbase: `0x${string}`;
@@ -72,27 +81,150 @@ export type ViemHeader = {
72
81
  totalManaUsed: bigint;
73
82
  };
74
83
 
75
- export type ViemContentCommitment = {
76
- blobsHash: `0x${string}`;
77
- inHash: `0x${string}`;
78
- outHash: `0x${string}`;
79
- };
80
-
81
84
  export type ViemGasFees = {
82
85
  feePerDaGas: bigint;
83
86
  feePerL2Gas: bigint;
84
87
  };
85
88
 
86
- export enum SlashingProposerType {
87
- None = 0,
88
- Tally = 1,
89
- 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,
98
+ }
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,
90
141
  }
91
142
 
143
+ /** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
144
+ export type ManaMinFeeComponents = {
145
+ sequencerCost: bigint;
146
+ proverCost: bigint;
147
+ congestionCost: bigint;
148
+ congestionMultiplier: bigint;
149
+ };
150
+
151
+ /**
152
+ * Reward configuration for the rollup
153
+ */
154
+ export type RewardConfig = {
155
+ rewardDistributor: EthAddress;
156
+ sequencerBps: bigint;
157
+ booster: EthAddress;
158
+ checkpointReward: bigint;
159
+ };
160
+
161
+ /**
162
+ * Exit information for a validator
163
+ */
164
+ export type Exit = {
165
+ withdrawalId: bigint;
166
+ amount: bigint;
167
+ exitableAt: bigint;
168
+ recipientOrWithdrawer: EthAddress;
169
+ isRecipient: boolean;
170
+ exists: boolean;
171
+ };
172
+
173
+ /**
174
+ * Attester configuration including public key and withdrawer
175
+ */
176
+ export type AttesterConfig = {
177
+ publicKey: {
178
+ x: bigint;
179
+ y: bigint;
180
+ };
181
+ withdrawer: EthAddress;
182
+ };
183
+
184
+ /**
185
+ * Complete view of an attester's state
186
+ */
187
+ export type AttesterView = {
188
+ status: AttesterStatus;
189
+ effectiveBalance: bigint;
190
+ exit: Exit;
191
+ config: AttesterConfig;
192
+ };
193
+
194
+ /**
195
+ * Return for a status call
196
+ */
197
+ export type RollupStatusResponse = {
198
+ provenCheckpointNumber: CheckpointNumber;
199
+ provenArchive: Fr;
200
+ pendingCheckpointNumber: CheckpointNumber;
201
+ pendingArchive: Fr;
202
+ archiveOfMyCheckpoint: Fr;
203
+ };
204
+
205
+ /** Arguments for the CheckpointProposed event. */
206
+ export type CheckpointProposedArgs = {
207
+ checkpointNumber: CheckpointNumber;
208
+ archive: Fr;
209
+ versionedBlobHashes: Buffer[];
210
+ /** Hash of attestations emitted in the CheckpointProposed event. */
211
+ attestationsHash: Buffer32;
212
+ /** Digest of the payload emitted in the CheckpointProposed event. */
213
+ payloadDigest: Buffer32;
214
+ };
215
+
216
+ /** Log type for CheckpointProposed events. */
217
+ export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
218
+
92
219
  export class RollupContract {
93
220
  private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
221
+ private readonly logger = createLogger('ethereum:rollup');
94
222
 
95
223
  private static cachedStfStorageSlot: Hex | undefined;
224
+ private cachedEscapeHatch?: {
225
+ address: EthAddress;
226
+ contract: GetContractReturnType<typeof EscapeHatchAbi, ViemClient>;
227
+ };
96
228
 
97
229
  static get checkBlobStorageSlot(): bigint {
98
230
  const asString = RollupStorage.find(storage => storage.label === 'checkBlob')?.slot;
@@ -106,7 +238,7 @@ export class RollupContract {
106
238
  return (RollupContract.cachedStfStorageSlot ??= keccak256(Buffer.from('aztec.stf.storage', 'utf-8')));
107
239
  }
108
240
 
109
- static getFromL1ContractsValues(deployL1ContractsValues: DeployL1ContractsReturnType) {
241
+ static getFromL1ContractsValues(deployL1ContractsValues: DeployAztecL1ContractsReturnType) {
110
242
  const {
111
243
  l1Client,
112
244
  l1ContractAddresses: { rollupAddress },
@@ -130,8 +262,8 @@ export class RollupContract {
130
262
  this.rollup = getContract({ address, abi: RollupAbi, client });
131
263
  }
132
264
 
133
- getGSE() {
134
- return this.rollup.read.getGSE();
265
+ async getGSE(): Promise<EthAddress> {
266
+ return EthAddress.fromString(await this.rollup.read.getGSE());
135
267
  }
136
268
 
137
269
  public get address() {
@@ -142,54 +274,38 @@ export class RollupContract {
142
274
  return this.rollup;
143
275
  }
144
276
 
145
- public async getSlashingProposer(): Promise<
146
- EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
147
- > {
277
+ public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
148
278
  const slasher = await this.getSlasherContract();
149
279
  if (!slasher) {
150
280
  return undefined;
151
281
  }
152
282
 
153
283
  const proposerAddress = await slasher.getProposer();
154
- const proposerAbi = [
155
- {
156
- type: 'function',
157
- name: 'SLASHING_PROPOSER_TYPE',
158
- inputs: [],
159
- outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
160
- stateMutability: 'view',
161
- },
162
- ] as const;
163
-
164
- const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
165
- const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
166
- if (proposerType === SlashingProposerType.Tally.valueOf()) {
167
- return new TallySlashingProposerContract(this.client, proposerAddress);
168
- } else if (proposerType === SlashingProposerType.Empire.valueOf()) {
169
- return new EmpireSlashingProposerContract(this.client, proposerAddress);
170
- } else {
171
- throw new Error(`Unknown slashing proposer type: ${proposerType}`);
284
+ if (proposerAddress.isZero()) {
285
+ return undefined;
172
286
  }
287
+
288
+ return new SlashingProposerContract(this.client, proposerAddress);
173
289
  }
174
290
 
175
291
  @memoize
176
- getL1StartBlock() {
292
+ getL1StartBlock(): Promise<bigint> {
177
293
  return this.rollup.read.L1_BLOCK_AT_GENESIS();
178
294
  }
179
295
 
180
296
  @memoize
181
- getL1GenesisTime() {
297
+ getL1GenesisTime(): Promise<bigint> {
182
298
  return this.rollup.read.getGenesisTime();
183
299
  }
184
300
 
185
301
  @memoize
186
- getProofSubmissionEpochs() {
187
- return this.rollup.read.getProofSubmissionEpochs();
302
+ async getProofSubmissionEpochs(): Promise<number> {
303
+ return Number(await this.rollup.read.getProofSubmissionEpochs());
188
304
  }
189
305
 
190
306
  @memoize
191
- getEpochDuration() {
192
- return this.rollup.read.getEpochDuration();
307
+ async getEpochDuration(): Promise<number> {
308
+ return Number(await this.rollup.read.getEpochDuration());
193
309
  }
194
310
 
195
311
  @memoize
@@ -198,68 +314,82 @@ export class RollupContract {
198
314
  }
199
315
 
200
316
  @memoize
201
- getTargetCommitteeSize() {
202
- return this.rollup.read.getTargetCommitteeSize();
317
+ async getTargetCommitteeSize(): Promise<number> {
318
+ return Number(await this.rollup.read.getTargetCommitteeSize());
203
319
  }
204
320
 
205
321
  @memoize
206
- getEjectionThreshold() {
322
+ getEjectionThreshold(): Promise<bigint> {
207
323
  return this.rollup.read.getEjectionThreshold();
208
324
  }
209
325
 
210
326
  @memoize
211
- getLocalEjectionThreshold() {
327
+ getLocalEjectionThreshold(): Promise<bigint> {
212
328
  return this.rollup.read.getLocalEjectionThreshold();
213
329
  }
214
330
 
215
331
  @memoize
216
- getLagInEpochsForValidatorSet() {
217
- return this.rollup.read.getLagInEpochsForValidatorSet();
332
+ async getLagInEpochsForValidatorSet(): Promise<number> {
333
+ return Number(await this.rollup.read.getLagInEpochsForValidatorSet());
218
334
  }
219
335
 
220
336
  @memoize
221
- getLagInEpochsForRandao() {
222
- return this.rollup.read.getLagInEpochsForRandao();
337
+ async getLagInEpochsForRandao(): Promise<number> {
338
+ return Number(await this.rollup.read.getLagInEpochsForRandao());
223
339
  }
224
340
 
225
341
  @memoize
226
- getActivationThreshold() {
342
+ getActivationThreshold(): Promise<bigint> {
227
343
  return this.rollup.read.getActivationThreshold();
228
344
  }
229
345
 
230
346
  @memoize
231
- getExitDelay() {
232
- return this.rollup.read.getExitDelay();
347
+ async getExitDelay(): Promise<number> {
348
+ return Number(await this.rollup.read.getExitDelay());
233
349
  }
234
350
 
235
351
  @memoize
236
- getManaTarget() {
352
+ getManaTarget(): Promise<bigint> {
237
353
  return this.rollup.read.getManaTarget();
238
354
  }
239
355
 
240
356
  @memoize
241
- getProvingCostPerMana() {
357
+ getProvingCostPerMana(): Promise<bigint> {
242
358
  return this.rollup.read.getProvingCostPerManaInEth();
243
359
  }
244
360
 
245
361
  @memoize
246
- getProvingCostPerManaInFeeAsset() {
362
+ getProvingCostPerManaInFeeAsset(): Promise<bigint> {
247
363
  return this.rollup.read.getProvingCostPerManaInFeeAsset();
248
364
  }
249
365
 
250
366
  @memoize
251
- getManaLimit() {
367
+ getManaLimit(): Promise<bigint> {
252
368
  return this.rollup.read.getManaLimit();
253
369
  }
254
370
 
255
371
  @memoize
256
- getVersion() {
372
+ getVersion(): Promise<bigint> {
257
373
  return this.rollup.read.getVersion();
258
374
  }
259
375
 
260
376
  @memoize
261
- async getGenesisArchiveTreeRoot(): Promise<`0x${string}`> {
262
- return await this.rollup.read.archiveAt([0n]);
377
+ async getGenesisArchiveTreeRoot(): Promise<Fr> {
378
+ return Fr.fromString(await this.rollup.read.archiveAt([0n]));
379
+ }
380
+
381
+ @memoize
382
+ async getVkTreeRoot(): Promise<Fr> {
383
+ const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
384
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
385
+ return Fr.fromString(value ?? '0x0');
386
+ }
387
+
388
+ @memoize
389
+ async getProtocolContractsHash(): Promise<Fr> {
390
+ const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
391
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
392
+ return Fr.fromString(value ?? '0x0');
263
393
  }
264
394
 
265
395
  /**
@@ -274,13 +404,25 @@ export class RollupContract {
274
404
  slotDuration: number;
275
405
  epochDuration: number;
276
406
  proofSubmissionEpochs: number;
407
+ targetCommitteeSize: number;
408
+ rollupManaLimit: number;
277
409
  }> {
278
- const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs] = await Promise.all([
410
+ const [
411
+ l1StartBlock,
412
+ l1GenesisTime,
413
+ slotDuration,
414
+ epochDuration,
415
+ proofSubmissionEpochs,
416
+ targetCommitteeSize,
417
+ rollupManaLimit,
418
+ ] = await Promise.all([
279
419
  this.getL1StartBlock(),
280
420
  this.getL1GenesisTime(),
281
421
  this.getSlotDuration(),
282
422
  this.getEpochDuration(),
283
423
  this.getProofSubmissionEpochs(),
424
+ this.getTargetCommitteeSize(),
425
+ this.getManaLimit(),
284
426
  ]);
285
427
  return {
286
428
  l1StartBlock,
@@ -288,30 +430,88 @@ export class RollupContract {
288
430
  slotDuration,
289
431
  epochDuration: Number(epochDuration),
290
432
  proofSubmissionEpochs: Number(proofSubmissionEpochs),
433
+ targetCommitteeSize,
434
+ rollupManaLimit: Number(rollupManaLimit),
291
435
  };
292
436
  }
293
437
 
294
- getSlasherAddress() {
295
- return this.rollup.read.getSlasher();
438
+ async getSlasherAddress(): Promise<EthAddress> {
439
+ return EthAddress.fromString(await this.rollup.read.getSlasher());
440
+ }
441
+
442
+ /**
443
+ * Returns the configured escape hatch contract address, or zero if disabled.
444
+ */
445
+ async getEscapeHatchAddress(): Promise<EthAddress> {
446
+ return EthAddress.fromString(await this.rollup.read.getEscapeHatch());
447
+ }
448
+
449
+ private async getEscapeHatchContract(): Promise<
450
+ GetContractReturnType<typeof EscapeHatchAbi, ViemClient> | undefined
451
+ > {
452
+ const escapeHatchAddress = await this.getEscapeHatchAddress();
453
+ if (escapeHatchAddress.isZero()) {
454
+ return undefined;
455
+ }
456
+
457
+ // Cache the viem contract wrapper since it will be used frequently.
458
+ if (!this.cachedEscapeHatch || !this.cachedEscapeHatch.address.equals(escapeHatchAddress)) {
459
+ this.cachedEscapeHatch = {
460
+ address: escapeHatchAddress,
461
+ contract: getContract({
462
+ address: escapeHatchAddress.toString(),
463
+ abi: EscapeHatchAbi,
464
+ client: this.client,
465
+ }),
466
+ };
467
+ }
468
+
469
+ return this.cachedEscapeHatch.contract;
470
+ }
471
+
472
+ /**
473
+ * Returns whether the escape hatch is open for the given epoch.
474
+ * If escape hatch is not configured, returns false.
475
+ *
476
+ * This function is intentionally defensive: any failure to query the escape hatch
477
+ * (RPC issues, transient errors, etc.) is treated as "closed" to avoid callers
478
+ * needing to sprinkle try/catch everywhere.
479
+ */
480
+ async isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean> {
481
+ try {
482
+ const escapeHatch = await this.getEscapeHatchContract();
483
+ if (!escapeHatch) {
484
+ return false;
485
+ }
486
+
487
+ const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
488
+ return isOpen;
489
+ } catch (err) {
490
+ this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
491
+ epoch: Number(epoch),
492
+ error: err,
493
+ });
494
+ return false;
495
+ }
296
496
  }
297
497
 
298
498
  /**
299
499
  * Returns a SlasherContract instance for interacting with the slasher contract.
300
500
  */
301
501
  async getSlasherContract(): Promise<SlasherContract | undefined> {
302
- const slasherAddress = EthAddress.fromString(await this.getSlasherAddress());
502
+ const slasherAddress = await this.getSlasherAddress();
303
503
  if (slasherAddress.isZero()) {
304
504
  return undefined;
305
505
  }
306
506
  return new SlasherContract(this.client, slasherAddress);
307
507
  }
308
508
 
309
- getOwner() {
310
- return this.rollup.read.owner();
509
+ async getOwner(): Promise<EthAddress> {
510
+ return EthAddress.fromString(await this.rollup.read.owner());
311
511
  }
312
512
 
313
- getActiveAttesterCount() {
314
- return this.rollup.read.getActiveAttesterCount();
513
+ async getActiveAttesterCount(): Promise<number> {
514
+ return Number(await this.rollup.read.getActiveAttesterCount());
315
515
  }
316
516
 
317
517
  public async getSlashingProposerAddress() {
@@ -322,31 +522,50 @@ export class RollupContract {
322
522
  return await slasher.getProposer();
323
523
  }
324
524
 
325
- getCheckpointReward() {
525
+ getCheckpointReward(): Promise<bigint> {
326
526
  return this.rollup.read.getCheckpointReward();
327
527
  }
328
528
 
329
- getCheckpointNumber() {
330
- return this.rollup.read.getPendingCheckpointNumber();
529
+ async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
530
+ await checkBlockTag(options?.blockNumber, this.client);
531
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
331
532
  }
332
533
 
333
- getProvenCheckpointNumber() {
334
- return this.rollup.read.getProvenCheckpointNumber();
534
+ async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
535
+ await checkBlockTag(options?.blockNumber, this.client);
536
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
335
537
  }
336
538
 
337
- async getSlotNumber(): Promise<SlotNumber> {
338
- return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot());
539
+ async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
540
+ await checkBlockTag(options?.blockNumber, this.client);
541
+ return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
339
542
  }
340
543
 
341
- getL1FeesAt(timestamp: bigint) {
342
- return this.rollup.read.getL1FeesAt([timestamp]);
544
+ async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
545
+ await checkBlockTag(options?.blockNumber, this.client);
546
+ const result = await this.rollup.read.getL1FeesAt([timestamp], options);
547
+ return {
548
+ baseFee: result.baseFee,
549
+ blobFee: result.blobFee,
550
+ };
343
551
  }
344
552
 
345
- getFeeAssetPerEth() {
346
- return this.rollup.read.getFeeAssetPerEth();
553
+ async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
554
+ const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
555
+ return {
556
+ excessMana: result.excessMana,
557
+ manaUsed: result.manaUsed,
558
+ ethPerFeeAsset: result.ethPerFeeAsset,
559
+ congestionCost: result.congestionCost,
560
+ proverCost: result.proverCost,
561
+ };
562
+ }
563
+
564
+ getEthPerFeeAsset(): Promise<bigint> {
565
+ return this.rollup.read.getEthPerFeeAsset();
347
566
  }
348
567
 
349
- async getCommitteeAt(timestamp: bigint): Promise<readonly `0x${string}`[] | undefined> {
568
+ async getCommitteeAt(timestamp: bigint): Promise<EthAddress[] | undefined> {
350
569
  const { result } = await this.client
351
570
  .simulateContract({
352
571
  address: this.address,
@@ -361,22 +580,22 @@ export class RollupContract {
361
580
  throw e;
362
581
  });
363
582
 
364
- return result;
583
+ return result ? result.map(addr => EthAddress.fromString(addr)) : undefined;
365
584
  }
366
585
 
367
- getSampleSeedAt(timestamp: bigint) {
368
- return this.rollup.read.getSampleSeedAt([timestamp]);
586
+ async getSampleSeedAt(timestamp: bigint): Promise<Buffer32> {
587
+ return Buffer32.fromBigInt(await this.rollup.read.getSampleSeedAt([timestamp]));
369
588
  }
370
589
 
371
- getCurrentSampleSeed() {
372
- return this.rollup.read.getCurrentSampleSeed();
590
+ async getCurrentSampleSeed(): Promise<Buffer32> {
591
+ return Buffer32.fromBigInt(await this.rollup.read.getCurrentSampleSeed());
373
592
  }
374
593
 
375
594
  async getCurrentEpoch(): Promise<EpochNumber> {
376
595
  return EpochNumber.fromBigInt(await this.rollup.read.getCurrentEpoch());
377
596
  }
378
597
 
379
- async getCurrentEpochCommittee(): Promise<readonly `0x${string}`[] | undefined> {
598
+ async getCurrentEpochCommittee(): Promise<EthAddress[] | undefined> {
380
599
  const { result } = await this.client
381
600
  .simulateContract({
382
601
  address: this.address,
@@ -391,10 +610,10 @@ export class RollupContract {
391
610
  throw e;
392
611
  });
393
612
 
394
- return result;
613
+ return result ? result.map(addr => EthAddress.fromString(addr)) : undefined;
395
614
  }
396
615
 
397
- async getCurrentProposer() {
616
+ async getCurrentProposer(): Promise<EthAddress> {
398
617
  const { result } = await this.client.simulateContract({
399
618
  address: this.address,
400
619
  abi: RollupAbi,
@@ -402,10 +621,10 @@ export class RollupContract {
402
621
  args: [],
403
622
  });
404
623
 
405
- return result;
624
+ return EthAddress.fromString(result);
406
625
  }
407
626
 
408
- async getProposerAt(timestamp: bigint) {
627
+ async getProposerAt(timestamp: bigint): Promise<EthAddress> {
409
628
  const { result } = await this.client.simulateContract({
410
629
  address: this.address,
411
630
  abi: RollupAbi,
@@ -413,27 +632,86 @@ export class RollupContract {
413
632
  args: [timestamp],
414
633
  });
415
634
 
416
- return result;
635
+ return EthAddress.fromString(result);
417
636
  }
418
637
 
419
- getCheckpoint(checkpointNumber: bigint | number) {
420
- return this.rollup.read.getCheckpoint([BigInt(checkpointNumber)]);
638
+ async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
639
+ await checkBlockTag(options?.blockNumber, this.client);
640
+ const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
641
+ return {
642
+ archive: Fr.fromString(result.archive),
643
+ headerHash: Buffer32.fromString(result.headerHash),
644
+ blobCommitmentsHash: Buffer32.fromString(result.blobCommitmentsHash),
645
+ attestationsHash: Buffer32.fromString(result.attestationsHash),
646
+ payloadDigest: Buffer32.fromString(result.payloadDigest),
647
+ slotNumber: SlotNumber.fromBigInt(result.slotNumber),
648
+ feeHeader: {
649
+ excessMana: result.feeHeader.excessMana,
650
+ manaUsed: result.feeHeader.manaUsed,
651
+ ethPerFeeAsset: result.feeHeader.ethPerFeeAsset,
652
+ congestionCost: result.feeHeader.congestionCost,
653
+ proverCost: result.feeHeader.proverCost,
654
+ },
655
+ };
421
656
  }
422
657
 
423
- getTips() {
424
- return this.rollup.read.getTips();
658
+ /** Returns the pending checkpoint from the rollup contract */
659
+ getPendingCheckpoint() {
660
+ // We retry because of race conditions during prunes: we may get a pending checkpoint number which is immediately
661
+ // reorged out due to a prune happening, causing the subsequent getCheckpoint call to fail. So we try again in that case.
662
+ return retry(
663
+ async () => {
664
+ const pendingCheckpointNumber = await this.getCheckpointNumber();
665
+ const pendingCheckpoint = await this.getCheckpoint(pendingCheckpointNumber);
666
+ return pendingCheckpoint;
667
+ },
668
+ 'getting pending checkpoint',
669
+ makeBackoff([0.5, 0.5, 0.5]),
670
+ );
425
671
  }
426
672
 
427
- getTimestampForSlot(slot: SlotNumber) {
673
+ /**
674
+ * Returns the effective pending checkpoint, accounting for potential prunes.
675
+ * When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
676
+ * This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
677
+ * @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
678
+ * @param options - Optional L1 block number to pin the queries to.
679
+ */
680
+ getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
681
+ return retry(
682
+ async () => {
683
+ const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
684
+ const canPrune = await this.canPruneAtTime(timestamp, options);
685
+ if (canPrune) {
686
+ const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
687
+ return await this.getCheckpoint(provenCheckpointNumber, options);
688
+ }
689
+ const pendingCheckpointNumber = await this.getCheckpointNumber(options);
690
+ return await this.getCheckpoint(pendingCheckpointNumber, options);
691
+ },
692
+ 'getting effective pending checkpoint',
693
+ makeBackoff([0.5, 0.5, 0.5]),
694
+ );
695
+ }
696
+
697
+ async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
698
+ const { pending, proven } = await this.rollup.read.getTips();
699
+ return {
700
+ pending: CheckpointNumber.fromBigInt(pending),
701
+ proven: CheckpointNumber.fromBigInt(proven),
702
+ };
703
+ }
704
+
705
+ getTimestampForSlot(slot: SlotNumber): Promise<bigint> {
428
706
  return this.rollup.read.getTimestampForSlot([BigInt(slot)]);
429
707
  }
430
708
 
431
- getEntryQueueLength() {
432
- return this.rollup.read.getEntryQueueLength();
709
+ async getEntryQueueLength(): Promise<number> {
710
+ return Number(await this.rollup.read.getEntryQueueLength());
433
711
  }
434
712
 
435
- getAvailableValidatorFlushes() {
436
- return this.rollup.read.getAvailableValidatorFlushes();
713
+ async getAvailableValidatorFlushes(): Promise<number> {
714
+ return Number(await this.rollup.read.getAvailableValidatorFlushes());
437
715
  }
438
716
 
439
717
  async getNextFlushableEpoch(): Promise<EpochNumber> {
@@ -444,7 +722,7 @@ export class RollupContract {
444
722
  return EpochNumber.fromBigInt(await this.rollup.read.getCurrentEpoch());
445
723
  }
446
724
 
447
- async getEpochNumberForCheckpoint(checkpointNumber: bigint): Promise<EpochNumber> {
725
+ async getEpochNumberForCheckpoint(checkpointNumber: CheckpointNumber): Promise<EpochNumber> {
448
726
  return EpochNumber.fromBigInt(await this.rollup.read.getEpochForCheckpoint([BigInt(checkpointNumber)]));
449
727
  }
450
728
 
@@ -489,10 +767,11 @@ export class RollupContract {
489
767
  return EpochNumber.fromBigInt(await this.rollup.read.getEpochAtSlot([BigInt(slotNumber)]));
490
768
  }
491
769
 
492
- getEpochProofPublicInputs(
770
+ async getEpochProofPublicInputs(
493
771
  args: readonly [bigint, bigint, EpochProofPublicInputArgs, readonly `0x${string}`[], `0x${string}`],
494
- ) {
495
- return this.rollup.read.getEpochProofPublicInputs(args);
772
+ ): Promise<Fr[]> {
773
+ const result = await this.rollup.read.getEpochProofPublicInputs(args);
774
+ return result.map(Fr.fromString);
496
775
  }
497
776
 
498
777
  public async validateHeader(
@@ -533,14 +812,13 @@ export class RollupContract {
533
812
  * timestamp of the next L1 block
534
813
  * @throws otherwise
535
814
  */
536
- public async canProposeAtNextEthBlock(
815
+ public async canProposeAt(
537
816
  archive: Buffer,
538
817
  account: `0x${string}` | Account,
539
- slotDuration: number,
540
- opts: { forcePendingCheckpointNumber?: number } = {},
541
- ): Promise<{ slot: SlotNumber; checkpointNumber: bigint; timeOfNextL1Slot: bigint }> {
542
- const latestBlock = await this.client.getBlock();
543
- const timeOfNextL1Slot = latestBlock.timestamp + BigInt(slotDuration);
818
+ timestamp: bigint,
819
+ stateOverride: StateOverride = [],
820
+ ): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
821
+ const timeOfNextL1Slot = timestamp;
544
822
  const who = typeof account === 'string' ? account : account.address;
545
823
 
546
824
  try {
@@ -552,10 +830,14 @@ export class RollupContract {
552
830
  functionName: 'canProposeAtTime',
553
831
  args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
554
832
  account,
555
- stateOverride: await this.makePendingCheckpointNumberOverride(opts.forcePendingCheckpointNumber),
833
+ stateOverride,
556
834
  });
557
835
 
558
- return { slot: SlotNumber.fromBigInt(slot), checkpointNumber, timeOfNextL1Slot };
836
+ return {
837
+ slot: SlotNumber.fromBigInt(slot),
838
+ checkpointNumber: CheckpointNumber.fromBigInt(checkpointNumber),
839
+ timeOfNextL1Slot,
840
+ };
559
841
  } catch (err: unknown) {
560
842
  throw formatViemError(err);
561
843
  }
@@ -567,7 +849,7 @@ export class RollupContract {
567
849
  * stored in the same slot. If the argument is undefined, it returns an empty override.
568
850
  */
569
851
  public async makePendingCheckpointNumberOverride(
570
- forcePendingCheckpointNumber: number | undefined,
852
+ forcePendingCheckpointNumber: CheckpointNumber | undefined,
571
853
  ): Promise<StateOverride> {
572
854
  if (forcePendingCheckpointNumber === undefined) {
573
855
  return [];
@@ -584,15 +866,161 @@ export class RollupContract {
584
866
  ];
585
867
  }
586
868
 
869
+ /**
870
+ * Returns a state override that sets tempCheckpointLogs[checkpointNumber].feeHeader to the compressed fee header.
871
+ * Used when simulating a propose call where the parent checkpoint hasn't landed on L1 yet (pipelining).
872
+ */
873
+ public async makeFeeHeaderOverride(checkpointNumber: CheckpointNumber, feeHeader: FeeHeader): Promise<StateOverride> {
874
+ const { epochDuration, proofSubmissionEpochs } = await this.getRollupConstants();
875
+ const roundaboutSize = BigInt(epochDuration * (proofSubmissionEpochs + 1) + 1);
876
+ const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
877
+
878
+ // tempCheckpointLogs is at offset 2 in RollupStore
879
+ const tempCheckpointLogsMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 2n;
880
+
881
+ // Solidity mapping slot: keccak256(abi.encode(key, baseSlot))
882
+ const structBaseSlot = hexToBigInt(
883
+ keccak256(
884
+ encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsMappingBase]),
885
+ ),
886
+ );
887
+
888
+ // feeHeader is the 7th field (offset 6) in CompressedTempCheckpointLog
889
+ const feeHeaderSlot = structBaseSlot + 6n;
890
+ const compressed = RollupContract.compressFeeHeader(feeHeader);
891
+
892
+ return [
893
+ {
894
+ address: this.address,
895
+ stateDiff: [
896
+ {
897
+ slot: `0x${feeHeaderSlot.toString(16).padStart(64, '0')}`,
898
+ value: `0x${compressed.toString(16).padStart(64, '0')}`,
899
+ },
900
+ ],
901
+ },
902
+ ];
903
+ }
904
+
905
+ /**
906
+ * Returns a state override that sets archives[checkpointNumber] to the given archive value.
907
+ * Used when simulating a canProposeAtTime call where the local archive differs from L1
908
+ * (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
909
+ */
910
+ public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
911
+ const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
912
+ const archiveSlot = hexToBigInt(
913
+ keccak256(
914
+ encodeAbiParameters(
915
+ [{ type: 'uint256' }, { type: 'uint256' }],
916
+ [BigInt(checkpointNumber), archivesMappingBase],
917
+ ),
918
+ ),
919
+ );
920
+ return [
921
+ {
922
+ address: this.address,
923
+ stateDiff: [
924
+ {
925
+ slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
926
+ value: archive.toString(),
927
+ },
928
+ ],
929
+ },
930
+ ];
931
+ }
932
+
933
+ /** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
934
+ public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
935
+ type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
936
+ const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
937
+ for (const override of overrides) {
938
+ for (const entry of override) {
939
+ const key = entry.address.toLowerCase();
940
+ const existing = byAddress.get(key);
941
+ if (existing) {
942
+ existing.stateDiff.push(...(entry.stateDiff ?? []));
943
+ if (entry.balance !== undefined) {
944
+ existing.balance = entry.balance;
945
+ }
946
+ } else {
947
+ byAddress.set(key, {
948
+ address: entry.address,
949
+ balance: entry.balance,
950
+ stateDiff: [...(entry.stateDiff ?? [])],
951
+ });
952
+ }
953
+ }
954
+ }
955
+ return [...byAddress.values()];
956
+ }
957
+
958
+ /** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
959
+ public static compressFeeHeader(feeHeader: FeeHeader): bigint {
960
+ const MASK_48_BITS = (1n << 48n) - 1n;
961
+ const MASK_64_BITS = (1n << 64n) - 1n;
962
+ const MASK_63_BITS = (1n << 63n) - 1n;
963
+
964
+ let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
965
+ value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
966
+ value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
967
+ value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
968
+ value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
969
+ value |= 1n << 255n; // preheat flag
970
+ return value;
971
+ }
972
+
973
+ /** Computes the fee header for a child checkpoint given parent fee header and child data.
974
+ * Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
975
+ public static computeChildFeeHeader(
976
+ parentFeeHeader: FeeHeader,
977
+ childManaUsed: bigint,
978
+ feeAssetPriceModifier: bigint,
979
+ manaTarget: bigint,
980
+ ): FeeHeader {
981
+ const MIN_ETH_PER_FEE_ASSET = 100n;
982
+ const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
983
+
984
+ // excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
985
+ const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
986
+ const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
987
+
988
+ // ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
989
+ const parentPrice =
990
+ parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
991
+ let newPrice: bigint;
992
+ if (feeAssetPriceModifier >= 0n) {
993
+ newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
994
+ } else {
995
+ const absMod = -feeAssetPriceModifier;
996
+ newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
997
+ }
998
+ if (newPrice < MIN_ETH_PER_FEE_ASSET) {
999
+ newPrice = MIN_ETH_PER_FEE_ASSET;
1000
+ }
1001
+ if (newPrice > MAX_ETH_PER_FEE_ASSET) {
1002
+ newPrice = MAX_ETH_PER_FEE_ASSET;
1003
+ }
1004
+
1005
+ return {
1006
+ excessMana,
1007
+ manaUsed: childManaUsed,
1008
+ ethPerFeeAsset: newPrice,
1009
+ congestionCost: 0n,
1010
+ proverCost: 0n,
1011
+ };
1012
+ }
1013
+
587
1014
  /** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
588
1015
  public buildInvalidateBadAttestationRequest(
589
- checkpointNumber: number,
1016
+ checkpointNumber: CheckpointNumber,
590
1017
  attestationsAndSigners: ViemCommitteeAttestations,
591
1018
  committee: EthAddress[],
592
1019
  invalidIndex: number,
593
1020
  ): L1TxRequest {
594
1021
  return {
595
1022
  to: this.address,
1023
+ abi: RollupAbi,
596
1024
  data: encodeFunctionData({
597
1025
  abi: RollupAbi,
598
1026
  functionName: 'invalidateBadAttestation',
@@ -608,12 +1036,13 @@ export class RollupContract {
608
1036
 
609
1037
  /** Creates a request to Rollup#invalidateInsufficientAttestations to be simulated or sent */
610
1038
  public buildInvalidateInsufficientAttestationsRequest(
611
- checkpointNumber: number,
1039
+ checkpointNumber: CheckpointNumber,
612
1040
  attestationsAndSigners: ViemCommitteeAttestations,
613
1041
  committee: EthAddress[],
614
1042
  ): L1TxRequest {
615
1043
  return {
616
1044
  to: this.address,
1045
+ abi: RollupAbi,
617
1046
  data: encodeFunctionData({
618
1047
  abi: RollupAbi,
619
1048
  functionName: 'invalidateInsufficientAttestations',
@@ -630,90 +1059,133 @@ export class RollupContract {
630
1059
  return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
631
1060
  }
632
1061
 
633
- getManaBaseFeeAt(timestamp: bigint, inFeeAsset: boolean) {
634
- return this.rollup.read.getManaBaseFeeAt([timestamp, inFeeAsset]);
1062
+ getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
1063
+ return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
1064
+ }
1065
+
1066
+ async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
1067
+ const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
1068
+ return {
1069
+ sequencerCost: result.sequencerCost,
1070
+ proverCost: result.proverCost,
1071
+ congestionCost: result.congestionCost,
1072
+ congestionMultiplier: result.congestionMultiplier,
1073
+ };
635
1074
  }
636
1075
 
637
1076
  async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
638
1077
  return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([timestamp]));
639
1078
  }
640
1079
 
641
- async status(checkpointNumber: bigint, options?: { blockNumber?: bigint }) {
1080
+ async status(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<RollupStatusResponse> {
642
1081
  await checkBlockTag(options?.blockNumber, this.client);
643
- return this.rollup.read.status([checkpointNumber], options);
1082
+ const result = await this.rollup.read.status([BigInt(checkpointNumber)], options);
1083
+ return {
1084
+ provenCheckpointNumber: CheckpointNumber.fromBigInt(result[0]),
1085
+ provenArchive: Fr.fromString(result[1]),
1086
+ pendingCheckpointNumber: CheckpointNumber.fromBigInt(result[2]),
1087
+ pendingArchive: Fr.fromString(result[3]),
1088
+ archiveOfMyCheckpoint: Fr.fromString(result[4]),
1089
+ };
644
1090
  }
645
1091
 
646
- async canPruneAtTime(timestamp: bigint, options?: { blockNumber?: bigint }) {
1092
+ async canPruneAtTime(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<boolean> {
647
1093
  await checkBlockTag(options?.blockNumber, this.client);
648
1094
  return this.rollup.read.canPruneAtTime([timestamp], options);
649
1095
  }
650
1096
 
651
- archive() {
652
- return this.rollup.read.archive();
1097
+ async archive(): Promise<Fr> {
1098
+ return Fr.fromString(await this.rollup.read.archive());
653
1099
  }
654
1100
 
655
- archiveAt(checkpointNumber: bigint) {
656
- return this.rollup.read.archiveAt([checkpointNumber]);
1101
+ async archiveAt(checkpointNumber: CheckpointNumber): Promise<Fr> {
1102
+ return Fr.fromString(await this.rollup.read.archiveAt([BigInt(checkpointNumber)]));
657
1103
  }
658
1104
 
659
- getSequencerRewards(address: Hex | EthAddress) {
1105
+ getSequencerRewards(address: Hex | EthAddress): Promise<bigint> {
660
1106
  if (address instanceof EthAddress) {
661
1107
  address = address.toString();
662
1108
  }
663
1109
  return this.rollup.read.getSequencerRewards([address]);
664
1110
  }
665
1111
 
666
- getSpecificProverRewardsForEpoch(epoch: bigint, prover: Hex | EthAddress) {
1112
+ getSpecificProverRewardsForEpoch(epoch: bigint, prover: Hex | EthAddress): Promise<bigint> {
667
1113
  if (prover instanceof EthAddress) {
668
1114
  prover = prover.toString();
669
1115
  }
670
1116
  return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
671
1117
  }
672
1118
 
673
- async getAttesters() {
1119
+ async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
674
1120
  const attesterSize = await this.getActiveAttesterCount();
675
1121
  const gse = new GSEContract(this.client, await this.getGSE());
676
- const ts = (await this.client.getBlock()).timestamp;
677
-
678
- const indices = Array.from({ length: Number(attesterSize) }, (_, i) => BigInt(i));
1122
+ const ts = timestamp ?? (await this.client.getBlock()).timestamp;
1123
+ const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
679
1124
  const chunks = chunk(indices, 1000);
680
1125
 
681
- return (await Promise.all(chunks.map(chunk => gse.getAttestersFromIndicesAtTime(this.address, ts, chunk)))).flat();
1126
+ const results = await Promise.all(chunks.map(chunk => gse.getAttestersFromIndicesAtTime(this.address, ts, chunk)));
1127
+ return results.flat().map(addr => EthAddress.fromString(addr));
682
1128
  }
683
1129
 
684
- getAttesterView(address: Hex | EthAddress) {
1130
+ async getAttesterView(address: Hex | EthAddress): Promise<AttesterView> {
685
1131
  if (address instanceof EthAddress) {
686
1132
  address = address.toString();
687
1133
  }
688
- return this.rollup.read.getAttesterView([address]);
1134
+ const result = await this.rollup.read.getAttesterView([address]);
1135
+ return {
1136
+ status: result.status as AttesterStatus,
1137
+ effectiveBalance: result.effectiveBalance,
1138
+ exit: {
1139
+ withdrawalId: result.exit.withdrawalId,
1140
+ amount: result.exit.amount,
1141
+ exitableAt: result.exit.exitableAt,
1142
+ recipientOrWithdrawer: EthAddress.fromString(result.exit.recipientOrWithdrawer),
1143
+ isRecipient: result.exit.isRecipient,
1144
+ exists: result.exit.exists,
1145
+ },
1146
+ config: {
1147
+ publicKey: {
1148
+ x: result.config.publicKey.x,
1149
+ y: result.config.publicKey.y,
1150
+ },
1151
+ withdrawer: EthAddress.fromString(result.config.withdrawer),
1152
+ },
1153
+ };
689
1154
  }
690
1155
 
691
- getStatus(address: Hex | EthAddress) {
1156
+ async getStatus(address: Hex | EthAddress): Promise<AttesterStatus> {
692
1157
  if (address instanceof EthAddress) {
693
1158
  address = address.toString();
694
1159
  }
695
- return this.rollup.read.getStatus([address]);
1160
+ return (await this.rollup.read.getStatus([address])) as AttesterStatus;
696
1161
  }
697
1162
 
698
- getBlobCommitmentsHash(checkpointNumber: bigint) {
699
- return this.rollup.read.getBlobCommitmentsHash([checkpointNumber]);
1163
+ async getBlobCommitmentsHash(checkpointNumber: CheckpointNumber): Promise<Buffer32> {
1164
+ return Buffer32.fromString(await this.rollup.read.getBlobCommitmentsHash([BigInt(checkpointNumber)]));
700
1165
  }
701
1166
 
702
- getCurrentBlobCommitmentsHash() {
703
- return this.rollup.read.getCurrentBlobCommitmentsHash();
1167
+ async getCurrentBlobCommitmentsHash(): Promise<Buffer32> {
1168
+ return Buffer32.fromString(await this.rollup.read.getCurrentBlobCommitmentsHash());
704
1169
  }
705
1170
 
706
- getStakingAsset() {
707
- return this.rollup.read.getStakingAsset();
1171
+ async getStakingAsset(): Promise<EthAddress> {
1172
+ return EthAddress.fromString(await this.rollup.read.getStakingAsset());
708
1173
  }
709
1174
 
710
- getRewardConfig() {
711
- return this.rollup.read.getRewardConfig();
1175
+ async getRewardConfig(): Promise<RewardConfig> {
1176
+ const result = await this.rollup.read.getRewardConfig();
1177
+ return {
1178
+ rewardDistributor: EthAddress.fromString(result.rewardDistributor),
1179
+ sequencerBps: BigInt(result.sequencerBps),
1180
+ booster: EthAddress.fromString(result.booster),
1181
+ checkpointReward: result.checkpointReward,
1182
+ };
712
1183
  }
713
1184
 
714
1185
  setupEpoch(l1TxUtils: L1TxUtils) {
715
1186
  return l1TxUtils.sendAndMonitorTransaction({
716
1187
  to: this.address,
1188
+ abi: RollupAbi,
717
1189
  data: encodeFunctionData({
718
1190
  abi: RollupAbi,
719
1191
  functionName: 'setupEpoch',
@@ -725,6 +1197,7 @@ export class RollupContract {
725
1197
  vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
726
1198
  return l1TxUtils.sendAndMonitorTransaction({
727
1199
  to: this.address,
1200
+ abi: RollupAbi,
728
1201
  data: encodeFunctionData({
729
1202
  abi: RollupAbi,
730
1203
  functionName: 'vote',
@@ -752,7 +1225,7 @@ export class RollupContract {
752
1225
  }
753
1226
 
754
1227
  public listenToCheckpointInvalidated(
755
- callback: (args: { checkpointNumber: bigint }) => unknown,
1228
+ callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
756
1229
  ): WatchContractEventReturnType {
757
1230
  return this.rollup.watchEvent.CheckpointInvalidated(
758
1231
  {},
@@ -761,7 +1234,7 @@ export class RollupContract {
761
1234
  for (const log of logs) {
762
1235
  const args = log.args;
763
1236
  if (args.checkpointNumber !== undefined) {
764
- callback({ checkpointNumber: args.checkpointNumber });
1237
+ callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber), event: log });
765
1238
  }
766
1239
  }
767
1240
  },
@@ -793,4 +1266,83 @@ export class RollupContract {
793
1266
  },
794
1267
  );
795
1268
  }
1269
+
1270
+ /**
1271
+ * Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
1272
+ * The Rollup inherits from Ownable and emits this event in its constructor, so the event
1273
+ * is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
1274
+ * probe to detect RPC nodes that prune historical logs.
1275
+ */
1276
+ async getOwnershipTransferredEventsAtDeploy() {
1277
+ const l1StartBlock = await this.getL1StartBlock();
1278
+ return await this.rollup.getEvents.OwnershipTransferred({}, { fromBlock: l1StartBlock, toBlock: l1StartBlock });
1279
+ }
1280
+
1281
+ /** Fetches CheckpointProposed events within the given block range. */
1282
+ async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
1283
+ const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
1284
+ return logs
1285
+ .filter(log => log.blockNumber! >= fromBlock && log.blockNumber! <= toBlock)
1286
+ .map(log => ({
1287
+ l1BlockNumber: log.blockNumber!,
1288
+ l1BlockHash: Buffer32.fromString(log.blockHash!),
1289
+ l1TransactionHash: log.transactionHash!,
1290
+ args: {
1291
+ checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
1292
+ archive: Fr.fromString(log.args.archive!),
1293
+ versionedBlobHashes: log.args.versionedBlobHashes!.map(h => Buffer.from(h.slice(2), 'hex')),
1294
+ attestationsHash: (() => {
1295
+ if (!log.args.attestationsHash) {
1296
+ throw new Error(
1297
+ `CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`,
1298
+ );
1299
+ }
1300
+ return Buffer32.fromString(log.args.attestationsHash);
1301
+ })(),
1302
+ payloadDigest: (() => {
1303
+ if (!log.args.payloadDigest) {
1304
+ throw new Error(
1305
+ `CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`,
1306
+ );
1307
+ }
1308
+ return Buffer32.fromString(log.args.payloadDigest);
1309
+ })(),
1310
+ },
1311
+ }));
1312
+ }
1313
+
1314
+ /** Packs pending and proven checkpoint numbers into the chain tips storage format. */
1315
+ static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
1316
+ return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
1317
+ }
1318
+
1319
+ /** Storage slot for the chain tips (offset 0 within the STF storage struct). */
1320
+ static get chainTipsStorageSlot(): bigint {
1321
+ return BigInt(RollupContract.stfStorageSlot);
1322
+ }
1323
+
1324
+ /**
1325
+ * Computes the storage slot for a field within a tempCheckpointLog entry.
1326
+ * @param checkpointNumber - The checkpoint number
1327
+ * @param field - The field within the CompressedTempCheckpointLog struct
1328
+ */
1329
+ async getTempCheckpointLogStorageSlot(
1330
+ checkpointNumber: CheckpointNumber,
1331
+ field: TempCheckpointLogField,
1332
+ ): Promise<bigint> {
1333
+ const fieldOffset = BigInt(field);
1334
+ const [epochDuration, proofSubmissionEpochs] = await Promise.all([
1335
+ this.getEpochDuration(),
1336
+ this.getProofSubmissionEpochs(),
1337
+ ]);
1338
+ const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
1339
+ const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
1340
+ const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
1341
+ const entryBase = BigInt(
1342
+ keccak256(
1343
+ encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
1344
+ ),
1345
+ );
1346
+ return entryBase + fieldOffset;
1347
+ }
796
1348
  }