@aztec-labs/ethereum 6.0.0-nightly.20260829

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 (272) hide show
  1. package/README.md +3 -0
  2. package/dest/account.d.ts +2 -0
  3. package/dest/account.d.ts.map +1 -0
  4. package/dest/account.js +4 -0
  5. package/dest/chain.d.ts +25 -0
  6. package/dest/chain.d.ts.map +1 -0
  7. package/dest/chain.js +53 -0
  8. package/dest/client.d.ts +42 -0
  9. package/dest/client.d.ts.map +1 -0
  10. package/dest/client.js +104 -0
  11. package/dest/config.d.ts +103 -0
  12. package/dest/config.d.ts.map +1 -0
  13. package/dest/config.js +233 -0
  14. package/dest/constants.d.ts +4 -0
  15. package/dest/constants.d.ts.map +1 -0
  16. package/dest/constants.js +2 -0
  17. package/dest/contracts/chain_state_override.d.ts +81 -0
  18. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  19. package/dest/contracts/chain_state_override.js +139 -0
  20. package/dest/contracts/empire_base.d.ts +30 -0
  21. package/dest/contracts/empire_base.d.ts.map +1 -0
  22. package/dest/contracts/empire_base.js +84 -0
  23. package/dest/contracts/errors.d.ts +7 -0
  24. package/dest/contracts/errors.d.ts.map +1 -0
  25. package/dest/contracts/errors.js +12 -0
  26. package/dest/contracts/fee_asset_handler.d.ts +20 -0
  27. package/dest/contracts/fee_asset_handler.d.ts.map +1 -0
  28. package/dest/contracts/fee_asset_handler.js +59 -0
  29. package/dest/contracts/fee_asset_price_oracle.d.ts +105 -0
  30. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  31. package/dest/contracts/fee_asset_price_oracle.js +656 -0
  32. package/dest/contracts/fee_juice.d.ts +14 -0
  33. package/dest/contracts/fee_juice.d.ts.map +1 -0
  34. package/dest/contracts/fee_juice.js +59 -0
  35. package/dest/contracts/governance.d.ts +192 -0
  36. package/dest/contracts/governance.d.ts.map +1 -0
  37. package/dest/contracts/governance.js +471 -0
  38. package/dest/contracts/governance_proposer.d.ts +63 -0
  39. package/dest/contracts/governance_proposer.d.ts.map +1 -0
  40. package/dest/contracts/governance_proposer.js +536 -0
  41. package/dest/contracts/gse.d.ts +38 -0
  42. package/dest/contracts/gse.d.ts.map +1 -0
  43. package/dest/contracts/gse.js +81 -0
  44. package/dest/contracts/inbox.d.ts +94 -0
  45. package/dest/contracts/inbox.d.ts.map +1 -0
  46. package/dest/contracts/inbox.js +141 -0
  47. package/dest/contracts/index.d.ts +18 -0
  48. package/dest/contracts/index.d.ts.map +1 -0
  49. package/dest/contracts/index.js +17 -0
  50. package/dest/contracts/log.d.ts +13 -0
  51. package/dest/contracts/log.d.ts.map +1 -0
  52. package/dest/contracts/log.js +1 -0
  53. package/dest/contracts/multicall.d.ts +138 -0
  54. package/dest/contracts/multicall.d.ts.map +1 -0
  55. package/dest/contracts/multicall.js +266 -0
  56. package/dest/contracts/outbox.d.ts +64 -0
  57. package/dest/contracts/outbox.d.ts.map +1 -0
  58. package/dest/contracts/outbox.js +113 -0
  59. package/dest/contracts/registry.d.ts +31 -0
  60. package/dest/contracts/registry.d.ts.map +1 -0
  61. package/dest/contracts/registry.js +142 -0
  62. package/dest/contracts/rollup.d.ts +5836 -0
  63. package/dest/contracts/rollup.d.ts.map +1 -0
  64. package/dest/contracts/rollup.js +1612 -0
  65. package/dest/contracts/slasher_contract.d.ts +44 -0
  66. package/dest/contracts/slasher_contract.d.ts.map +1 -0
  67. package/dest/contracts/slasher_contract.js +75 -0
  68. package/dest/contracts/slashing_proposer.d.ts +174 -0
  69. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  70. package/dest/contracts/slashing_proposer.js +755 -0
  71. package/dest/contracts/utils.d.ts +3 -0
  72. package/dest/contracts/utils.d.ts.map +1 -0
  73. package/dest/contracts/utils.js +11 -0
  74. package/dest/contracts/watch_event.d.ts +36 -0
  75. package/dest/contracts/watch_event.d.ts.map +1 -0
  76. package/dest/contracts/watch_event.js +66 -0
  77. package/dest/deploy_aztec_l1_contracts.d.ts +267 -0
  78. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -0
  79. package/dest/deploy_aztec_l1_contracts.js +424 -0
  80. package/dest/deploy_l1_contract.d.ts +68 -0
  81. package/dest/deploy_l1_contract.d.ts.map +1 -0
  82. package/dest/deploy_l1_contract.js +312 -0
  83. package/dest/eth-signer/eth-signer.d.ts +21 -0
  84. package/dest/eth-signer/eth-signer.d.ts.map +1 -0
  85. package/dest/eth-signer/eth-signer.js +5 -0
  86. package/dest/eth-signer/index.d.ts +2 -0
  87. package/dest/eth-signer/index.d.ts.map +1 -0
  88. package/dest/eth-signer/index.js +1 -0
  89. package/dest/forwarder_proxy.d.ts +32 -0
  90. package/dest/forwarder_proxy.d.ts.map +1 -0
  91. package/dest/forwarder_proxy.js +93 -0
  92. package/dest/foundry_binary.d.ts +13 -0
  93. package/dest/foundry_binary.d.ts.map +1 -0
  94. package/dest/foundry_binary.js +52 -0
  95. package/dest/l1_artifacts.d.ts +81896 -0
  96. package/dest/l1_artifacts.d.ts.map +1 -0
  97. package/dest/l1_artifacts.js +151 -0
  98. package/dest/l1_contract_addresses.d.ts +73 -0
  99. package/dest/l1_contract_addresses.d.ts.map +1 -0
  100. package/dest/l1_contract_addresses.js +120 -0
  101. package/dest/l1_reader.d.ts +18 -0
  102. package/dest/l1_reader.d.ts.map +1 -0
  103. package/dest/l1_reader.js +35 -0
  104. package/dest/l1_tx_utils/config.d.ts +65 -0
  105. package/dest/l1_tx_utils/config.d.ts.map +1 -0
  106. package/dest/l1_tx_utils/config.js +109 -0
  107. package/dest/l1_tx_utils/constants.d.ts +12 -0
  108. package/dest/l1_tx_utils/constants.d.ts.map +1 -0
  109. package/dest/l1_tx_utils/constants.js +39 -0
  110. package/dest/l1_tx_utils/factory.d.ts +32 -0
  111. package/dest/l1_tx_utils/factory.d.ts.map +1 -0
  112. package/dest/l1_tx_utils/factory.js +22 -0
  113. package/dest/l1_tx_utils/fee-strategies/index.d.ts +10 -0
  114. package/dest/l1_tx_utils/fee-strategies/index.d.ts.map +1 -0
  115. package/dest/l1_tx_utils/fee-strategies/index.js +12 -0
  116. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +8 -0
  117. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -0
  118. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +129 -0
  119. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts +23 -0
  120. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts.map +1 -0
  121. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +191 -0
  122. package/dest/l1_tx_utils/fee-strategies/types.d.ts +51 -0
  123. package/dest/l1_tx_utils/fee-strategies/types.d.ts.map +1 -0
  124. package/dest/l1_tx_utils/fee-strategies/types.js +3 -0
  125. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +41 -0
  126. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -0
  127. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +42 -0
  128. package/dest/l1_tx_utils/index-blobs.d.ts +3 -0
  129. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -0
  130. package/dest/l1_tx_utils/index-blobs.js +2 -0
  131. package/dest/l1_tx_utils/index.d.ts +13 -0
  132. package/dest/l1_tx_utils/index.d.ts.map +1 -0
  133. package/dest/l1_tx_utils/index.js +13 -0
  134. package/dest/l1_tx_utils/interfaces.d.ts +76 -0
  135. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -0
  136. package/dest/l1_tx_utils/interfaces.js +4 -0
  137. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +252 -0
  138. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -0
  139. package/dest/l1_tx_utils/l1_fee_analyzer.js +568 -0
  140. package/dest/l1_tx_utils/l1_tx_utils.d.ts +113 -0
  141. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -0
  142. package/dest/l1_tx_utils/l1_tx_utils.js +720 -0
  143. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +83 -0
  144. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -0
  145. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +357 -0
  146. package/dest/l1_tx_utils/signer.d.ts +4 -0
  147. package/dest/l1_tx_utils/signer.d.ts.map +1 -0
  148. package/dest/l1_tx_utils/signer.js +16 -0
  149. package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
  150. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  151. package/dest/l1_tx_utils/tx_delayer.js +223 -0
  152. package/dest/l1_tx_utils/types.d.ts +93 -0
  153. package/dest/l1_tx_utils/types.d.ts.map +1 -0
  154. package/dest/l1_tx_utils/types.js +36 -0
  155. package/dest/l1_tx_utils/utils.d.ts +4 -0
  156. package/dest/l1_tx_utils/utils.d.ts.map +1 -0
  157. package/dest/l1_tx_utils/utils.js +14 -0
  158. package/dest/l1_types.d.ts +6 -0
  159. package/dest/l1_types.d.ts.map +1 -0
  160. package/dest/l1_types.js +1 -0
  161. package/dest/publisher_manager.d.ts +42 -0
  162. package/dest/publisher_manager.d.ts.map +1 -0
  163. package/dest/publisher_manager.js +199 -0
  164. package/dest/queries.d.ts +25 -0
  165. package/dest/queries.d.ts.map +1 -0
  166. package/dest/queries.js +144 -0
  167. package/dest/test/blob_kzg_warmup.d.ts +19 -0
  168. package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
  169. package/dest/test/blob_kzg_warmup.js +64 -0
  170. package/dest/test/chain_monitor.d.ts +126 -0
  171. package/dest/test/chain_monitor.d.ts.map +1 -0
  172. package/dest/test/chain_monitor.js +314 -0
  173. package/dest/test/eth_cheat_codes.d.ts +260 -0
  174. package/dest/test/eth_cheat_codes.d.ts.map +1 -0
  175. package/dest/test/eth_cheat_codes.js +608 -0
  176. package/dest/test/eth_cheat_codes_with_state.d.ts +18 -0
  177. package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -0
  178. package/dest/test/eth_cheat_codes_with_state.js +34 -0
  179. package/dest/test/index.d.ts +8 -0
  180. package/dest/test/index.d.ts.map +1 -0
  181. package/dest/test/index.js +7 -0
  182. package/dest/test/recording_rpc_server.d.ts +18 -0
  183. package/dest/test/recording_rpc_server.d.ts.map +1 -0
  184. package/dest/test/recording_rpc_server.js +68 -0
  185. package/dest/test/rollup_cheat_codes.d.ts +142 -0
  186. package/dest/test/rollup_cheat_codes.d.ts.map +1 -0
  187. package/dest/test/rollup_cheat_codes.js +338 -0
  188. package/dest/test/start_anvil.d.ts +40 -0
  189. package/dest/test/start_anvil.d.ts.map +1 -0
  190. package/dest/test/start_anvil.js +210 -0
  191. package/dest/test/upgrade_utils.d.ts +12 -0
  192. package/dest/test/upgrade_utils.d.ts.map +1 -0
  193. package/dest/test/upgrade_utils.js +111 -0
  194. package/dest/types.d.ts +68 -0
  195. package/dest/types.d.ts.map +1 -0
  196. package/dest/types.js +3 -0
  197. package/dest/utils.d.ts +45 -0
  198. package/dest/utils.d.ts.map +1 -0
  199. package/dest/utils.js +260 -0
  200. package/dest/zkPassportVerifierAddress.d.ts +15 -0
  201. package/dest/zkPassportVerifierAddress.d.ts.map +1 -0
  202. package/dest/zkPassportVerifierAddress.js +11 -0
  203. package/package.json +122 -0
  204. package/src/account.ts +5 -0
  205. package/src/chain.ts +71 -0
  206. package/src/client.ts +165 -0
  207. package/src/config.ts +342 -0
  208. package/src/constants.ts +4 -0
  209. package/src/contracts/README.md +157 -0
  210. package/src/contracts/chain_state_override.ts +203 -0
  211. package/src/contracts/empire_base.ts +89 -0
  212. package/src/contracts/errors.ts +13 -0
  213. package/src/contracts/fee_asset_handler.ts +66 -0
  214. package/src/contracts/fee_asset_price_oracle.ts +284 -0
  215. package/src/contracts/fee_juice.ts +57 -0
  216. package/src/contracts/governance.ts +597 -0
  217. package/src/contracts/governance_proposer.ts +181 -0
  218. package/src/contracts/gse.ts +105 -0
  219. package/src/contracts/inbox.ts +223 -0
  220. package/src/contracts/index.ts +17 -0
  221. package/src/contracts/log.ts +13 -0
  222. package/src/contracts/multicall.ts +298 -0
  223. package/src/contracts/outbox.ts +132 -0
  224. package/src/contracts/registry.ts +161 -0
  225. package/src/contracts/rollup.ts +1502 -0
  226. package/src/contracts/slasher_contract.ts +89 -0
  227. package/src/contracts/slashing_proposer.ts +397 -0
  228. package/src/contracts/utils.ts +14 -0
  229. package/src/contracts/watch_event.ts +109 -0
  230. package/src/deploy_aztec_l1_contracts.ts +663 -0
  231. package/src/deploy_l1_contract.ts +361 -0
  232. package/src/eth-signer/eth-signer.ts +24 -0
  233. package/src/eth-signer/index.ts +1 -0
  234. package/src/forwarder_proxy.ts +107 -0
  235. package/src/foundry_binary.ts +57 -0
  236. package/src/l1_artifacts.ts +231 -0
  237. package/src/l1_contract_addresses.ts +149 -0
  238. package/src/l1_reader.ts +57 -0
  239. package/src/l1_tx_utils/README.md +177 -0
  240. package/src/l1_tx_utils/config.ts +181 -0
  241. package/src/l1_tx_utils/constants.ts +29 -0
  242. package/src/l1_tx_utils/factory.ts +64 -0
  243. package/src/l1_tx_utils/fee-strategies/index.ts +22 -0
  244. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +162 -0
  245. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +244 -0
  246. package/src/l1_tx_utils/fee-strategies/types.ts +55 -0
  247. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +107 -0
  248. package/src/l1_tx_utils/index-blobs.ts +2 -0
  249. package/src/l1_tx_utils/index.ts +15 -0
  250. package/src/l1_tx_utils/interfaces.ts +86 -0
  251. package/src/l1_tx_utils/l1_fee_analyzer.ts +874 -0
  252. package/src/l1_tx_utils/l1_tx_utils.ts +840 -0
  253. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +459 -0
  254. package/src/l1_tx_utils/signer.ts +27 -0
  255. package/src/l1_tx_utils/tx_delayer.ts +302 -0
  256. package/src/l1_tx_utils/types.ts +116 -0
  257. package/src/l1_tx_utils/utils.ts +15 -0
  258. package/src/l1_types.ts +6 -0
  259. package/src/publisher_manager.ts +239 -0
  260. package/src/queries.ts +189 -0
  261. package/src/test/blob_kzg_warmup.ts +65 -0
  262. package/src/test/chain_monitor.ts +389 -0
  263. package/src/test/eth_cheat_codes.ts +624 -0
  264. package/src/test/eth_cheat_codes_with_state.ts +38 -0
  265. package/src/test/index.ts +7 -0
  266. package/src/test/recording_rpc_server.ts +69 -0
  267. package/src/test/rollup_cheat_codes.ts +398 -0
  268. package/src/test/start_anvil.ts +255 -0
  269. package/src/test/upgrade_utils.ts +109 -0
  270. package/src/types.ts +97 -0
  271. package/src/utils.ts +344 -0
  272. package/src/zkPassportVerifierAddress.ts +15 -0
@@ -0,0 +1,1502 @@
1
+ import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
2
+ import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
3
+ import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
4
+
5
+ import { CheckpointNumber, EpochNumber, SlotNumber } from '@aztec-labs/foundation/branded-types';
6
+ import { Buffer32 } from '@aztec-labs/foundation/buffer';
7
+ import { Fr } from '@aztec-labs/foundation/curves/bn254';
8
+ import { memoize } from '@aztec-labs/foundation/decorators';
9
+ import { EthAddress } from '@aztec-labs/foundation/eth-address';
10
+ import type { ViemSignature } from '@aztec-labs/foundation/eth-signature';
11
+ import { createLogger } from '@aztec-labs/foundation/log';
12
+ import { makeBackoff, retry } from '@aztec-labs/foundation/retry';
13
+ import { getErrorCause } from '@aztec-labs/foundation/types';
14
+ import chunk from 'lodash.chunk';
15
+ import {
16
+ type Account,
17
+ ContractFunctionRevertedError,
18
+ type GetContractReturnType,
19
+ type Hex,
20
+ type Log,
21
+ RpcRequestError,
22
+ type StateOverride,
23
+ type WatchContractEventReturnType,
24
+ decodeErrorResult,
25
+ encodeAbiParameters,
26
+ encodeFunctionData,
27
+ getContract,
28
+ hexToBigInt,
29
+ keccak256,
30
+ } from 'viem';
31
+
32
+ import { getPublicClient } from '../client.js';
33
+ import type { DeployAztecL1ContractsReturnType } from '../deploy_aztec_l1_contracts.js';
34
+ import type { L1ContractAddresses } from '../l1_contract_addresses.js';
35
+ import type { L1ReaderConfig } from '../l1_reader.js';
36
+ import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
37
+ import type { ViemClient } from '../types.js';
38
+ import { formatViemError } from '../utils.js';
39
+ import { GSEContract } from './gse.js';
40
+ import type { L1EventLog } from './log.js';
41
+ import { SlasherContract } from './slasher_contract.js';
42
+ import { SlashingProposerContract } from './slashing_proposer.js';
43
+ import { checkBlockTag } from './utils.js';
44
+ import { type WatchContractEventOptions, watchContractEvent } from './watch_event.js';
45
+
46
+ export type ViemCommitteeAttestation = {
47
+ addr: `0x${string}`;
48
+ signature: ViemSignature;
49
+ };
50
+
51
+ export type ViemCommitteeAttestations = {
52
+ signatureIndices: `0x${string}`;
53
+ signaturesOrAddresses: `0x${string}`;
54
+ };
55
+
56
+ export type L1RollupContractAddresses = Pick<
57
+ L1ContractAddresses,
58
+ | 'rollupAddress'
59
+ | 'inboxAddress'
60
+ | 'outboxAddress'
61
+ | 'feeJuicePortalAddress'
62
+ | 'feeJuiceAddress'
63
+ | 'stakingAssetAddress'
64
+ | 'rewardDistributorAddress'
65
+ | 'gseAddress'
66
+ >;
67
+
68
+ export type EpochProofPublicInputArgs = {
69
+ previousArchive: `0x${string}`;
70
+ endArchive: `0x${string}`;
71
+ outHash: `0x${string}`;
72
+ /** Inbox rolling hash before the epoch's first checkpoint's messages. */
73
+ previousInboxRollingHash: `0x${string}`;
74
+ /** Inbox rolling hash after the epoch's last checkpoint's messages. */
75
+ endInboxRollingHash: `0x${string}`;
76
+ proverId: `0x${string}`;
77
+ };
78
+
79
+ export type ViemHeader = {
80
+ lastArchiveRoot: `0x${string}`;
81
+ blockHeadersHash: `0x${string}`;
82
+ blobsHash: `0x${string}`;
83
+ inboxRollingHash: `0x${string}`;
84
+ outHash: `0x${string}`;
85
+ slotNumber: bigint;
86
+ timestamp: bigint;
87
+ coinbase: `0x${string}`;
88
+ feeRecipient: `0x${string}`;
89
+ gasFees: ViemGasFees;
90
+ totalManaUsed: bigint;
91
+ accumulatedFees: bigint;
92
+ };
93
+
94
+ export type ViemGasFees = {
95
+ feePerDaGas: bigint;
96
+ feePerL2Gas: bigint;
97
+ };
98
+
99
+ /**
100
+ * Status of a validator/attester in the staking system.
101
+ * Matches the Status enum in StakingLib.sol
102
+ */
103
+ export enum AttesterStatus {
104
+ NONE = 0,
105
+ VALIDATING = 1,
106
+ ZOMBIE = 2,
107
+ EXITING = 3,
108
+ }
109
+
110
+ /**
111
+ * Fee header data for a checkpoint
112
+ */
113
+ export type FeeHeader = {
114
+ excessMana: bigint;
115
+ manaUsed: bigint;
116
+ ethPerFeeAsset: bigint;
117
+ congestionCost: bigint;
118
+ proverCost: bigint;
119
+ };
120
+
121
+ /**
122
+ * Checkpoint log data returned from the rollup contract
123
+ */
124
+ export type CheckpointLog = {
125
+ archive: Fr;
126
+ headerHash: Buffer32;
127
+ blobCommitmentsHash: Buffer32;
128
+ attestationsHash: Buffer32;
129
+ payloadDigest: Buffer32;
130
+ slotNumber: SlotNumber;
131
+ feeHeader: FeeHeader;
132
+ };
133
+
134
+ /**
135
+ * L1 fee data (base fee and blob fee)
136
+ */
137
+ export type L1FeeData = {
138
+ baseFee: bigint;
139
+ blobFee: bigint;
140
+ };
141
+
142
+ /**
143
+ * Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. The `SlotNumber`
144
+ * word also packs the inbox consumption counts, which Solidity places in the same slot.
145
+ */
146
+ export enum TempCheckpointLogField {
147
+ HeaderHash = 0,
148
+ BlobCommitmentsHash = 1,
149
+ OutHash = 2,
150
+ AttestationsHash = 3,
151
+ PayloadDigest = 4,
152
+ SlotNumber = 5,
153
+ FeeHeader = 6,
154
+ InboxRollingHash = 7,
155
+ }
156
+
157
+ /**
158
+ * Field-level override input for `tempCheckpointLogs[checkpointNumber]`. Covers the fields the
159
+ * `propose()` path actually reads back. `payloadDigest` is `Buffer32` because it carries an
160
+ * arbitrary `bytes32` value rather than a BN254 scalar. `slotNumber` carries the uint32 portion
161
+ * of the on-chain `CompressedSlot`.
162
+ *
163
+ * `slotNumber` and `inboxMsgTotal` share a single storage word, so supplying either rewrites both;
164
+ * the one left out lands as zero.
165
+ */
166
+ export type TempCheckpointLogOverrideFields = {
167
+ headerHash?: Fr;
168
+ outHash?: Fr;
169
+ payloadDigest?: Buffer32;
170
+ slotNumber?: SlotNumber;
171
+ /** Cumulative Inbox message count consumed as of this checkpoint. */
172
+ inboxMsgTotal?: bigint;
173
+ feeHeader?: FeeHeader;
174
+ };
175
+
176
+ /** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
177
+ export type ManaMinFeeComponents = {
178
+ sequencerCost: bigint;
179
+ proverCost: bigint;
180
+ congestionCost: bigint;
181
+ congestionMultiplier: bigint;
182
+ };
183
+
184
+ /**
185
+ * Reward configuration for the rollup
186
+ */
187
+ export type RewardConfig = {
188
+ rewardDistributor: EthAddress;
189
+ sequencerBps: bigint;
190
+ booster: EthAddress;
191
+ checkpointReward: bigint;
192
+ };
193
+
194
+ /**
195
+ * Exit information for a validator
196
+ */
197
+ export type Exit = {
198
+ withdrawalId: bigint;
199
+ amount: bigint;
200
+ exitableAt: bigint;
201
+ recipientOrWithdrawer: EthAddress;
202
+ isRecipient: boolean;
203
+ exists: boolean;
204
+ };
205
+
206
+ /**
207
+ * Attester configuration including public key and withdrawer
208
+ */
209
+ export type AttesterConfig = {
210
+ publicKey: {
211
+ x: bigint;
212
+ y: bigint;
213
+ };
214
+ withdrawer: EthAddress;
215
+ };
216
+
217
+ /**
218
+ * Complete view of an attester's state
219
+ */
220
+ export type AttesterView = {
221
+ status: AttesterStatus;
222
+ effectiveBalance: bigint;
223
+ exit: Exit;
224
+ config: AttesterConfig;
225
+ };
226
+
227
+ /**
228
+ * Return for a status call
229
+ */
230
+ export type RollupStatusResponse = {
231
+ provenCheckpointNumber: CheckpointNumber;
232
+ provenArchive: Fr;
233
+ pendingCheckpointNumber: CheckpointNumber;
234
+ pendingArchive: Fr;
235
+ archiveOfMyCheckpoint: Fr;
236
+ };
237
+
238
+ /** Arguments for the CheckpointProposed event. */
239
+ export type CheckpointProposedArgs = {
240
+ checkpointNumber: CheckpointNumber;
241
+ archive: Fr;
242
+ versionedBlobHashes: Buffer[];
243
+ /** Hash of attestations emitted in the CheckpointProposed event. */
244
+ attestationsHash: Buffer32;
245
+ /** Digest of the payload emitted in the CheckpointProposed event. */
246
+ payloadDigest: Buffer32;
247
+ };
248
+
249
+ /** Log type for CheckpointProposed events. */
250
+ export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
251
+
252
+ const INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR = 'ValidatorSelection__InsufficientValidatorSetSize';
253
+
254
+ /** SlasherUpdated events are rare governance operations, so their watcher polls well below the client's interval. */
255
+ const SLASHER_UPDATED_POLLING_INTERVAL_MS = 60_000;
256
+
257
+ function isValidatorSelectionError(err: unknown, errorName: string): boolean {
258
+ return (
259
+ getErrorCause(err, ContractFunctionRevertedError)?.data?.errorName === errorName ||
260
+ decodeRpcRequestErrorName(err) === errorName
261
+ );
262
+ }
263
+
264
+ function decodeRpcRequestErrorName(err: unknown): string | undefined {
265
+ const data = getErrorCause(err, RpcRequestError)?.data;
266
+ if (!isHexString(data)) {
267
+ return undefined;
268
+ }
269
+
270
+ try {
271
+ return decodeErrorResult({ abi: RollupAbi, data }).errorName;
272
+ } catch {
273
+ return undefined;
274
+ }
275
+ }
276
+
277
+ function isHexString(value: unknown): value is Hex {
278
+ return typeof value === 'string' && value.startsWith('0x');
279
+ }
280
+
281
+ function requireUintFits(value: bigint, bits: number, name: string): bigint {
282
+ if (value < 0n || value >= 1n << BigInt(bits)) {
283
+ throw new Error(`${name} ${value} does not fit in uint${bits}`);
284
+ }
285
+ return value;
286
+ }
287
+
288
+ export class RollupContract {
289
+ private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
290
+ private readonly logger = createLogger('ethereum:rollup');
291
+
292
+ private static cachedStfStorageSlot: Hex | undefined;
293
+ private cachedEscapeHatch?: {
294
+ address: EthAddress;
295
+ contract: GetContractReturnType<typeof EscapeHatchAbi, ViemClient>;
296
+ };
297
+
298
+ static get checkBlobStorageSlot(): bigint {
299
+ const asString = RollupStorage.find(storage => storage.label === 'checkBlob')?.slot;
300
+ if (asString === undefined) {
301
+ throw new Error('checkBlobStorageSlot not found');
302
+ }
303
+ return BigInt(asString);
304
+ }
305
+
306
+ static get stfStorageSlot(): Hex {
307
+ return (RollupContract.cachedStfStorageSlot ??= keccak256(Buffer.from('aztec.stf.storage', 'utf-8')));
308
+ }
309
+
310
+ static getFromL1ContractsValues(deployL1ContractsValues: DeployAztecL1ContractsReturnType) {
311
+ const {
312
+ l1Client,
313
+ l1ContractAddresses: { rollupAddress },
314
+ } = deployL1ContractsValues;
315
+ return new RollupContract(l1Client, rollupAddress.toString());
316
+ }
317
+
318
+ static getFromConfig(config: L1ReaderConfig) {
319
+ const client = getPublicClient(config);
320
+ const address = config.rollupAddress.toString();
321
+ return new RollupContract(client, address);
322
+ }
323
+
324
+ constructor(
325
+ public readonly client: ViemClient,
326
+ address: Hex | EthAddress,
327
+ ) {
328
+ if (address instanceof EthAddress) {
329
+ address = address.toString();
330
+ }
331
+ this.rollup = getContract({ address, abi: RollupAbi, client });
332
+ }
333
+
334
+ async getGSE(): Promise<EthAddress> {
335
+ return EthAddress.fromString(await this.rollup.read.getGSE());
336
+ }
337
+
338
+ public get address() {
339
+ return this.rollup.address;
340
+ }
341
+
342
+ getContract(): GetContractReturnType<typeof RollupAbi, ViemClient> {
343
+ return this.rollup;
344
+ }
345
+
346
+ public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
347
+ const slasher = await this.getSlasherContract();
348
+ if (!slasher) {
349
+ return undefined;
350
+ }
351
+
352
+ const proposerAddress = await slasher.getProposer();
353
+ if (proposerAddress.isZero()) {
354
+ return undefined;
355
+ }
356
+
357
+ return new SlashingProposerContract(this.client, proposerAddress);
358
+ }
359
+
360
+ @memoize
361
+ getL1StartBlock(): Promise<bigint> {
362
+ return this.rollup.read.L1_BLOCK_AT_GENESIS();
363
+ }
364
+
365
+ @memoize
366
+ getL1GenesisTime(): Promise<bigint> {
367
+ return this.rollup.read.getGenesisTime();
368
+ }
369
+
370
+ @memoize
371
+ async getProofSubmissionEpochs(): Promise<number> {
372
+ return Number(await this.rollup.read.getProofSubmissionEpochs());
373
+ }
374
+
375
+ @memoize
376
+ async getEpochDuration(): Promise<number> {
377
+ return Number(await this.rollup.read.getEpochDuration());
378
+ }
379
+
380
+ @memoize
381
+ async getSlotDuration(): Promise<number> {
382
+ return Number(await this.rollup.read.getSlotDuration());
383
+ }
384
+
385
+ @memoize
386
+ async getTargetCommitteeSize(): Promise<number> {
387
+ return Number(await this.rollup.read.getTargetCommitteeSize());
388
+ }
389
+
390
+ @memoize
391
+ getEjectionThreshold(): Promise<bigint> {
392
+ return this.rollup.read.getEjectionThreshold();
393
+ }
394
+
395
+ @memoize
396
+ getLocalEjectionThreshold(): Promise<bigint> {
397
+ return this.rollup.read.getLocalEjectionThreshold();
398
+ }
399
+
400
+ @memoize
401
+ async getLagInEpochsForValidatorSet(): Promise<number> {
402
+ return Number(await this.rollup.read.getLagInEpochsForValidatorSet());
403
+ }
404
+
405
+ @memoize
406
+ async getLagInEpochsForRandao(): Promise<number> {
407
+ return Number(await this.rollup.read.getLagInEpochsForRandao());
408
+ }
409
+
410
+ @memoize
411
+ getActivationThreshold(): Promise<bigint> {
412
+ return this.rollup.read.getActivationThreshold();
413
+ }
414
+
415
+ @memoize
416
+ async getExitDelay(): Promise<number> {
417
+ return Number(await this.rollup.read.getExitDelay());
418
+ }
419
+
420
+ @memoize
421
+ getManaTarget(): Promise<bigint> {
422
+ return this.rollup.read.getManaTarget();
423
+ }
424
+
425
+ @memoize
426
+ getProvingCostPerMana(): Promise<bigint> {
427
+ return this.rollup.read.getProvingCostPerManaInEth();
428
+ }
429
+
430
+ @memoize
431
+ getProvingCostPerManaInFeeAsset(): Promise<bigint> {
432
+ return this.rollup.read.getProvingCostPerManaInFeeAsset();
433
+ }
434
+
435
+ @memoize
436
+ getManaLimit(): Promise<bigint> {
437
+ return this.rollup.read.getManaLimit();
438
+ }
439
+
440
+ @memoize
441
+ getVersion(): Promise<bigint> {
442
+ return this.rollup.read.getVersion();
443
+ }
444
+
445
+ @memoize
446
+ async getGenesisArchiveTreeRoot(): Promise<Fr> {
447
+ return Fr.fromString(await this.rollup.read.archiveAt([0n]));
448
+ }
449
+
450
+ @memoize
451
+ async getVkTreeRoot(): Promise<Fr> {
452
+ const slot = BigInt(RollupContract.stfStorageSlot) + 3n;
453
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
454
+ return Fr.fromString(value ?? '0x0');
455
+ }
456
+
457
+ @memoize
458
+ async getProtocolContractsHash(): Promise<Fr> {
459
+ const slot = BigInt(RollupContract.stfStorageSlot) + 4n;
460
+ const value = await this.client.getStorageAt({ address: this.address, slot: `0x${slot.toString(16)}` });
461
+ return Fr.fromString(value ?? '0x0');
462
+ }
463
+
464
+ /**
465
+ * Returns rollup constants used for epoch queries.
466
+ * Return type is `L1RollupConstants` which is defined in stdlib,
467
+ * so we cant reference it until we move this contract to that package.
468
+ */
469
+ @memoize
470
+ public async getRollupConstants(): Promise<{
471
+ l1StartBlock: bigint;
472
+ l1GenesisTime: bigint;
473
+ slotDuration: number;
474
+ epochDuration: number;
475
+ proofSubmissionEpochs: number;
476
+ targetCommitteeSize: number;
477
+ rollupManaLimit: number;
478
+ }> {
479
+ const [
480
+ l1StartBlock,
481
+ l1GenesisTime,
482
+ slotDuration,
483
+ epochDuration,
484
+ proofSubmissionEpochs,
485
+ targetCommitteeSize,
486
+ rollupManaLimit,
487
+ ] = await Promise.all([
488
+ this.getL1StartBlock(),
489
+ this.getL1GenesisTime(),
490
+ this.getSlotDuration(),
491
+ this.getEpochDuration(),
492
+ this.getProofSubmissionEpochs(),
493
+ this.getTargetCommitteeSize(),
494
+ this.getManaLimit(),
495
+ ]);
496
+ return {
497
+ l1StartBlock,
498
+ l1GenesisTime,
499
+ slotDuration,
500
+ epochDuration: Number(epochDuration),
501
+ proofSubmissionEpochs: Number(proofSubmissionEpochs),
502
+ targetCommitteeSize,
503
+ rollupManaLimit: Number(rollupManaLimit),
504
+ };
505
+ }
506
+
507
+ async getSlasherAddress(): Promise<EthAddress> {
508
+ return EthAddress.fromString(await this.rollup.read.getSlasher());
509
+ }
510
+
511
+ /**
512
+ * Returns the configured escape hatch contract address, or zero if disabled.
513
+ */
514
+ async getEscapeHatchAddress(): Promise<EthAddress> {
515
+ return EthAddress.fromString(await this.rollup.read.getEscapeHatch());
516
+ }
517
+
518
+ private async getEscapeHatchContract(): Promise<
519
+ GetContractReturnType<typeof EscapeHatchAbi, ViemClient> | undefined
520
+ > {
521
+ const escapeHatchAddress = await this.getEscapeHatchAddress();
522
+ if (escapeHatchAddress.isZero()) {
523
+ return undefined;
524
+ }
525
+
526
+ // Cache the viem contract wrapper since it will be used frequently.
527
+ if (!this.cachedEscapeHatch || !this.cachedEscapeHatch.address.equals(escapeHatchAddress)) {
528
+ this.cachedEscapeHatch = {
529
+ address: escapeHatchAddress,
530
+ contract: getContract({
531
+ address: escapeHatchAddress.toString(),
532
+ abi: EscapeHatchAbi,
533
+ client: this.client,
534
+ }),
535
+ };
536
+ }
537
+
538
+ return this.cachedEscapeHatch.contract;
539
+ }
540
+
541
+ /**
542
+ * Returns whether the escape hatch is open for the given epoch.
543
+ * If escape hatch is not configured, returns false.
544
+ *
545
+ * This function is intentionally defensive: any failure to query the escape hatch
546
+ * (RPC issues, transient errors, etc.) is treated as "closed" to avoid callers
547
+ * needing to sprinkle try/catch everywhere.
548
+ */
549
+ async isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean> {
550
+ try {
551
+ const escapeHatch = await this.getEscapeHatchContract();
552
+ if (!escapeHatch) {
553
+ return false;
554
+ }
555
+
556
+ const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
557
+ return isOpen;
558
+ } catch (err) {
559
+ this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
560
+ epoch: Number(epoch),
561
+ error: err,
562
+ });
563
+ return false;
564
+ }
565
+ }
566
+
567
+ /**
568
+ * Returns a SlasherContract instance for interacting with the slasher contract.
569
+ */
570
+ async getSlasherContract(): Promise<SlasherContract | undefined> {
571
+ const slasherAddress = await this.getSlasherAddress();
572
+ if (slasherAddress.isZero()) {
573
+ return undefined;
574
+ }
575
+ return new SlasherContract(this.client, slasherAddress);
576
+ }
577
+
578
+ async getOwner(): Promise<EthAddress> {
579
+ return EthAddress.fromString(await this.rollup.read.owner());
580
+ }
581
+
582
+ async getActiveAttesterCount(options?: { blockNumber?: bigint }): Promise<number> {
583
+ await checkBlockTag(options?.blockNumber, this.client);
584
+ return Number(await this.rollup.read.getActiveAttesterCount(options));
585
+ }
586
+
587
+ /**
588
+ * Number of attesters that were staked at the given (historical) L1 timestamp. This is the count that
589
+ * validator-set sampling uses to decide whether an epoch gets a committee, and the historical counterpart
590
+ * of {@link getActiveAttesterCount} (which reads at the latest L1 block).
591
+ */
592
+ async getAttesterCountAtTime(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<number> {
593
+ const gse = new GSEContract(this.client, await this.getGSE());
594
+ return gse.getAttesterCountAtTime(this.address, timestamp, options);
595
+ }
596
+
597
+ public async getSlashingProposerAddress() {
598
+ const slasher = await this.getSlasherContract();
599
+ if (!slasher) {
600
+ return EthAddress.ZERO;
601
+ }
602
+ return await slasher.getProposer();
603
+ }
604
+
605
+ getCheckpointReward(): Promise<bigint> {
606
+ return this.rollup.read.getCheckpointReward();
607
+ }
608
+
609
+ async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
610
+ await checkBlockTag(options?.blockNumber, this.client);
611
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
612
+ }
613
+
614
+ async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
615
+ await checkBlockTag(options?.blockNumber, this.client);
616
+ return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
617
+ }
618
+
619
+ async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
620
+ await checkBlockTag(options?.blockNumber, this.client);
621
+ return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
622
+ }
623
+
624
+ async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
625
+ await checkBlockTag(options?.blockNumber, this.client);
626
+ const result = await this.rollup.read.getL1FeesAt([timestamp], options);
627
+ return {
628
+ baseFee: result.baseFee,
629
+ blobFee: result.blobFee,
630
+ };
631
+ }
632
+
633
+ async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
634
+ const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
635
+ return {
636
+ excessMana: result.excessMana,
637
+ manaUsed: result.manaUsed,
638
+ ethPerFeeAsset: result.ethPerFeeAsset,
639
+ congestionCost: result.congestionCost,
640
+ proverCost: result.proverCost,
641
+ };
642
+ }
643
+
644
+ getEthPerFeeAsset(): Promise<bigint> {
645
+ return this.rollup.read.getEthPerFeeAsset();
646
+ }
647
+
648
+ async getCommitteeAt(timestamp: bigint): Promise<EthAddress[] | undefined> {
649
+ const { result } = await this.client
650
+ .simulateContract({
651
+ address: this.address,
652
+ abi: RollupAbi,
653
+ functionName: 'getCommitteeAt',
654
+ args: [timestamp],
655
+ })
656
+ .catch(e => {
657
+ if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
658
+ return { result: undefined };
659
+ }
660
+ throw e;
661
+ });
662
+
663
+ return result ? result.map(addr => EthAddress.fromString(addr)) : undefined;
664
+ }
665
+
666
+ async getSampleSeedAt(timestamp: bigint): Promise<Buffer32> {
667
+ return Buffer32.fromBigInt(await this.rollup.read.getSampleSeedAt([timestamp]));
668
+ }
669
+
670
+ async getCurrentSampleSeed(): Promise<Buffer32> {
671
+ return Buffer32.fromBigInt(await this.rollup.read.getCurrentSampleSeed());
672
+ }
673
+
674
+ async getCurrentEpoch(): Promise<EpochNumber> {
675
+ return EpochNumber.fromBigInt(await this.rollup.read.getCurrentEpoch());
676
+ }
677
+
678
+ async getCurrentEpochCommittee(): Promise<EthAddress[] | undefined> {
679
+ const { result } = await this.client
680
+ .simulateContract({
681
+ address: this.address,
682
+ abi: RollupAbi,
683
+ functionName: 'getCurrentEpochCommittee',
684
+ args: [],
685
+ })
686
+ .catch(e => {
687
+ if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
688
+ return { result: undefined };
689
+ }
690
+ throw e;
691
+ });
692
+
693
+ return result ? result.map(addr => EthAddress.fromString(addr)) : undefined;
694
+ }
695
+
696
+ async getCurrentProposer(): Promise<EthAddress> {
697
+ const { result } = await this.client.simulateContract({
698
+ address: this.address,
699
+ abi: RollupAbi,
700
+ functionName: 'getCurrentProposer',
701
+ args: [],
702
+ });
703
+
704
+ return EthAddress.fromString(result);
705
+ }
706
+
707
+ async getProposerAt(timestamp: bigint): Promise<EthAddress> {
708
+ const { result } = await this.client.simulateContract({
709
+ address: this.address,
710
+ abi: RollupAbi,
711
+ functionName: 'getProposerAt',
712
+ args: [timestamp],
713
+ });
714
+
715
+ return EthAddress.fromString(result);
716
+ }
717
+
718
+ async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
719
+ await checkBlockTag(options?.blockNumber, this.client);
720
+ const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
721
+ return {
722
+ archive: Fr.fromString(result.archive),
723
+ headerHash: Buffer32.fromString(result.headerHash),
724
+ blobCommitmentsHash: Buffer32.fromString(result.blobCommitmentsHash),
725
+ attestationsHash: Buffer32.fromString(result.attestationsHash),
726
+ payloadDigest: Buffer32.fromString(result.payloadDigest),
727
+ slotNumber: SlotNumber.fromBigInt(result.slotNumber),
728
+ feeHeader: {
729
+ excessMana: result.feeHeader.excessMana,
730
+ manaUsed: result.feeHeader.manaUsed,
731
+ ethPerFeeAsset: result.feeHeader.ethPerFeeAsset,
732
+ congestionCost: result.feeHeader.congestionCost,
733
+ proverCost: result.feeHeader.proverCost,
734
+ },
735
+ };
736
+ }
737
+
738
+ /** Returns the pending checkpoint from the rollup contract */
739
+ getPendingCheckpoint() {
740
+ // We retry because of race conditions during prunes: we may get a pending checkpoint number which is immediately
741
+ // reorged out due to a prune happening, causing the subsequent getCheckpoint call to fail. So we try again in that case.
742
+ return retry(
743
+ async () => {
744
+ const pendingCheckpointNumber = await this.getCheckpointNumber();
745
+ const pendingCheckpoint = await this.getCheckpoint(pendingCheckpointNumber);
746
+ return pendingCheckpoint;
747
+ },
748
+ 'getting pending checkpoint',
749
+ makeBackoff([0.5, 0.5, 0.5]),
750
+ );
751
+ }
752
+
753
+ /**
754
+ * Returns the effective pending checkpoint, accounting for potential prunes.
755
+ * When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
756
+ * This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
757
+ * @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
758
+ * @param options - Optional L1 block number to pin the queries to.
759
+ */
760
+ getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
761
+ return retry(
762
+ async () => {
763
+ const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
764
+ const canPrune = await this.canPruneAtTime(timestamp, options);
765
+ if (canPrune) {
766
+ const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
767
+ return await this.getCheckpoint(provenCheckpointNumber, options);
768
+ }
769
+ const pendingCheckpointNumber = await this.getCheckpointNumber(options);
770
+ return await this.getCheckpoint(pendingCheckpointNumber, options);
771
+ },
772
+ 'getting effective pending checkpoint',
773
+ makeBackoff([0.5, 0.5, 0.5]),
774
+ );
775
+ }
776
+
777
+ async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
778
+ const { pending, proven } = await this.rollup.read.getTips();
779
+ return {
780
+ pending: CheckpointNumber.fromBigInt(pending),
781
+ proven: CheckpointNumber.fromBigInt(proven),
782
+ };
783
+ }
784
+
785
+ getTimestampForSlot(slot: SlotNumber): Promise<bigint> {
786
+ return this.rollup.read.getTimestampForSlot([BigInt(slot)]);
787
+ }
788
+
789
+ async getEntryQueueLength(): Promise<number> {
790
+ return Number(await this.rollup.read.getEntryQueueLength());
791
+ }
792
+
793
+ async getAvailableValidatorFlushes(): Promise<number> {
794
+ return Number(await this.rollup.read.getAvailableValidatorFlushes());
795
+ }
796
+
797
+ async getNextFlushableEpoch(): Promise<EpochNumber> {
798
+ return EpochNumber.fromBigInt(await this.rollup.read.getNextFlushableEpoch());
799
+ }
800
+
801
+ async getCurrentEpochNumber(): Promise<EpochNumber> {
802
+ return EpochNumber.fromBigInt(await this.rollup.read.getCurrentEpoch());
803
+ }
804
+
805
+ async getEpochNumberForCheckpoint(checkpointNumber: CheckpointNumber): Promise<EpochNumber> {
806
+ return EpochNumber.fromBigInt(await this.rollup.read.getEpochForCheckpoint([BigInt(checkpointNumber)]));
807
+ }
808
+
809
+ async getRollupAddresses(): Promise<L1RollupContractAddresses> {
810
+ const [
811
+ inboxAddress,
812
+ outboxAddress,
813
+ feeJuicePortalAddress,
814
+ rewardDistributorAddress,
815
+ feeJuiceAddress,
816
+ stakingAssetAddress,
817
+ gseAddress,
818
+ ] = (
819
+ await Promise.all([
820
+ this.rollup.read.getInbox(),
821
+ this.rollup.read.getOutbox(),
822
+ this.rollup.read.getFeeAssetPortal(),
823
+ this.rollup.read.getRewardDistributor(),
824
+ this.rollup.read.getFeeAsset(),
825
+ this.rollup.read.getStakingAsset(),
826
+ this.rollup.read.getGSE(),
827
+ ] as const)
828
+ ).map(EthAddress.fromString);
829
+
830
+ return {
831
+ rollupAddress: EthAddress.fromString(this.address),
832
+ inboxAddress,
833
+ outboxAddress,
834
+ feeJuicePortalAddress,
835
+ feeJuiceAddress,
836
+ stakingAssetAddress,
837
+ rewardDistributorAddress,
838
+ gseAddress,
839
+ };
840
+ }
841
+
842
+ public async getFeeJuicePortal() {
843
+ return EthAddress.fromString(await this.rollup.read.getFeeAssetPortal());
844
+ }
845
+
846
+ public async getEpochNumberForSlotNumber(slotNumber: SlotNumber): Promise<EpochNumber> {
847
+ return EpochNumber.fromBigInt(await this.rollup.read.getEpochAtSlot([BigInt(slotNumber)]));
848
+ }
849
+
850
+ async getEpochProofPublicInputs(
851
+ args: readonly [bigint, bigint, EpochProofPublicInputArgs, readonly ViemHeader[], `0x${string}`],
852
+ ): Promise<Fr[]> {
853
+ const result = await this.rollup.read.getEpochProofPublicInputs(args);
854
+ return result.map(Fr.fromString);
855
+ }
856
+
857
+ public async validateHeader(
858
+ args: readonly [
859
+ ViemHeader,
860
+ ViemCommitteeAttestations,
861
+ `0x${string}`[],
862
+ ViemSignature,
863
+ `0x${string}`,
864
+ `0x${string}`,
865
+ {
866
+ ignoreDA: boolean;
867
+ ignoreSignatures: boolean;
868
+ },
869
+ ],
870
+ account: `0x${string}` | Account,
871
+ ): Promise<void> {
872
+ try {
873
+ await this.client.simulateContract({
874
+ address: this.address,
875
+ abi: RollupAbi,
876
+ functionName: 'validateHeaderWithAttestations',
877
+ args,
878
+ account,
879
+ });
880
+ } catch (error: unknown) {
881
+ throw formatViemError(error);
882
+ }
883
+ }
884
+
885
+ /**
886
+ * @notice Calls `canProposeAtTime` with the time of the next Ethereum block and the sender address
887
+ *
888
+ * @dev Throws if unable to propose
889
+ *
890
+ * @param archive - The archive that we expect to be current state
891
+ * @return [slot, checkpointNumber, timeOfNextL1Slot] - If you can propose, the L2 slot number, checkpoint number and
892
+ * timestamp of the next L1 block
893
+ * @throws otherwise
894
+ */
895
+ public async canProposeAt(
896
+ archive: Buffer,
897
+ account: `0x${string}` | Account,
898
+ timestamp: bigint,
899
+ stateOverride: StateOverride = [],
900
+ ): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
901
+ const timeOfNextL1Slot = timestamp;
902
+ const who = typeof account === 'string' ? account : account.address;
903
+
904
+ try {
905
+ const {
906
+ result: [slot, checkpointNumber],
907
+ } = await this.client.simulateContract({
908
+ address: this.address,
909
+ abi: RollupAbi,
910
+ functionName: 'canProposeAtTime',
911
+ args: [timeOfNextL1Slot, `0x${archive.toString('hex')}`, who],
912
+ account,
913
+ stateOverride,
914
+ });
915
+
916
+ return {
917
+ slot: SlotNumber.fromBigInt(slot),
918
+ checkpointNumber: CheckpointNumber.fromBigInt(checkpointNumber),
919
+ timeOfNextL1Slot,
920
+ };
921
+ } catch (err: unknown) {
922
+ throw formatViemError(err);
923
+ }
924
+ }
925
+
926
+ /**
927
+ * Returns a state override that sets the pending and/or proven checkpoint numbers. Useful for simulations.
928
+ * Both values share a single storage slot (pending in the upper 128 bits, proven in the lower 128 bits), so
929
+ * a single combined override is emitted to avoid the second state-diff clobbering the first. The current live
930
+ * value is read once to preserve any half not being overridden. Returns an empty override if neither is set.
931
+ *
932
+ * Throws if the resulting `proven > pending`, which would crash the simulation: `STFLib.canPruneAtTime` calls
933
+ * `getEpochForCheckpoint(proven + 1)` whenever `pending != proven`, and that asserts `_n <= tips.pending`.
934
+ */
935
+ public async makeChainTipsOverride(override: {
936
+ pending?: CheckpointNumber;
937
+ proven?: CheckpointNumber;
938
+ }): Promise<StateOverride> {
939
+ if (override.pending === undefined && override.proven === undefined) {
940
+ return [];
941
+ }
942
+ const slot = RollupContract.stfStorageSlot;
943
+ const currentValue = await this.client.getStorageAt({ address: this.address, slot });
944
+ const currentRaw = currentValue ? hexToBigInt(currentValue) : 0n;
945
+ const currentPending = currentRaw >> 128n;
946
+ const currentProven = currentRaw & ((1n << 128n) - 1n);
947
+ const newPending = override.pending !== undefined ? BigInt(override.pending) : currentPending;
948
+ const newProven = override.proven !== undefined ? BigInt(override.proven) : currentProven;
949
+ if (newProven > newPending) {
950
+ throw new Error(`Invalid chain tips override: proven (${newProven}) > pending (${newPending})`);
951
+ }
952
+ const newValue = (newPending << 128n) | newProven;
953
+ return [
954
+ {
955
+ address: this.address,
956
+ stateDiff: [{ slot, value: `0x${newValue.toString(16).padStart(64, '0')}` }],
957
+ },
958
+ ];
959
+ }
960
+
961
+ /**
962
+ * Returns a state override that patches `tempCheckpointLogs[checkpointNumber]` with every field that
963
+ * the L1 contract reads during `propose()` for the checkpoint that builds on top of it (proposer
964
+ * pipelining simulation). Mirrors the writes done by `ProposeLib.addTempCheckpointLog`.
965
+ *
966
+ * `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path
967
+ * never asserts against them, so leaving them at storage zero is harmless.
968
+ *
969
+ * One diff entry is emitted per storage word touched, so words left out keep their on-chain values.
970
+ * `slotNumber` and the two inbox consumption counts share a word: any of them rewrites all three.
971
+ */
972
+ public async makeTempCheckpointLogOverride(
973
+ checkpointNumber: CheckpointNumber,
974
+ fields: TempCheckpointLogOverrideFields,
975
+ ): Promise<StateOverride> {
976
+ const constants = await this.getRollupConstants();
977
+ const slotAt = (field: TempCheckpointLogField) =>
978
+ `0x${this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants).toString(16).padStart(64, '0')}` as const;
979
+ const word = (v: bigint) => `0x${v.toString(16).padStart(64, '0')}` as const;
980
+
981
+ const stateDiff: { slot: `0x${string}`; value: `0x${string}` }[] = [];
982
+
983
+ if (fields.headerHash) {
984
+ stateDiff.push({ slot: slotAt(TempCheckpointLogField.HeaderHash), value: fields.headerHash.toString() });
985
+ }
986
+ if (fields.outHash) {
987
+ stateDiff.push({ slot: slotAt(TempCheckpointLogField.OutHash), value: fields.outHash.toString() });
988
+ }
989
+ if (fields.payloadDigest) {
990
+ stateDiff.push({
991
+ slot: slotAt(TempCheckpointLogField.PayloadDigest),
992
+ value: fields.payloadDigest.toString() as `0x${string}`,
993
+ });
994
+ }
995
+ if (fields.slotNumber !== undefined || fields.inboxMsgTotal !== undefined) {
996
+ // The L1 struct packs the slot number and the inbox consumption count into one word, so this
997
+ // diff always writes both. Widths are enforced here because the L1 writers cast through
998
+ // SafeCast and revert on overflow; a malformed override must surface rather than silently truncate
999
+ // into a neighbouring field.
1000
+ const slotNumber = requireUintFits(BigInt(fields.slotNumber ?? 0), 32, 'slotNumber');
1001
+ const inboxMsgTotal = requireUintFits(fields.inboxMsgTotal ?? 0n, 64, 'inboxMsgTotal');
1002
+ stateDiff.push({
1003
+ slot: slotAt(TempCheckpointLogField.SlotNumber),
1004
+ value: word(slotNumber | (inboxMsgTotal << 32n)),
1005
+ });
1006
+ }
1007
+ if (fields.feeHeader) {
1008
+ stateDiff.push({
1009
+ slot: slotAt(TempCheckpointLogField.FeeHeader),
1010
+ value: word(RollupContract.compressFeeHeader(fields.feeHeader)),
1011
+ });
1012
+ }
1013
+
1014
+ if (stateDiff.length === 0) {
1015
+ return [];
1016
+ }
1017
+ return [{ address: this.address, stateDiff }];
1018
+ }
1019
+
1020
+ /**
1021
+ * Returns a state override that sets archives[checkpointNumber] to the given archive value.
1022
+ * Used when simulating a canProposeAtTime call where the local archive differs from L1
1023
+ * (e.g. pipelining where the parent checkpoint hasn't landed on L1 yet).
1024
+ */
1025
+ public makeArchiveOverride(checkpointNumber: CheckpointNumber, archive: Fr): StateOverride {
1026
+ const archivesMappingBase = hexToBigInt(RollupContract.stfStorageSlot) + 1n;
1027
+ const archiveSlot = hexToBigInt(
1028
+ keccak256(
1029
+ encodeAbiParameters(
1030
+ [{ type: 'uint256' }, { type: 'uint256' }],
1031
+ [BigInt(checkpointNumber), archivesMappingBase],
1032
+ ),
1033
+ ),
1034
+ );
1035
+ return [
1036
+ {
1037
+ address: this.address,
1038
+ stateDiff: [
1039
+ {
1040
+ slot: `0x${archiveSlot.toString(16).padStart(64, '0')}`,
1041
+ value: archive.toString(),
1042
+ },
1043
+ ],
1044
+ },
1045
+ ];
1046
+ }
1047
+
1048
+ /** Merges multiple StateOverride arrays, combining stateDiff entries for the same address. */
1049
+ public static mergeStateOverrides(...overrides: StateOverride[]): StateOverride {
1050
+ type StateDiffEntry = { slot: `0x${string}`; value: `0x${string}` };
1051
+ const byAddress = new Map<string, { address: `0x${string}`; balance?: bigint; stateDiff: StateDiffEntry[] }>();
1052
+ for (const override of overrides) {
1053
+ for (const entry of override) {
1054
+ const key = entry.address.toLowerCase();
1055
+ const existing = byAddress.get(key);
1056
+ if (existing) {
1057
+ existing.stateDiff.push(...(entry.stateDiff ?? []));
1058
+ if (entry.balance !== undefined) {
1059
+ existing.balance = entry.balance;
1060
+ }
1061
+ } else {
1062
+ byAddress.set(key, {
1063
+ address: entry.address,
1064
+ balance: entry.balance,
1065
+ stateDiff: [...(entry.stateDiff ?? [])],
1066
+ });
1067
+ }
1068
+ }
1069
+ }
1070
+ return [...byAddress.values()];
1071
+ }
1072
+
1073
+ /** Compresses a FeeHeader into a uint256 matching FeeHeaderLib.compress() in FeeStructs.sol. */
1074
+ public static compressFeeHeader(feeHeader: FeeHeader): bigint {
1075
+ const MASK_48_BITS = (1n << 48n) - 1n;
1076
+ const MASK_64_BITS = (1n << 64n) - 1n;
1077
+ const MASK_63_BITS = (1n << 63n) - 1n;
1078
+
1079
+ let value = BigInt(feeHeader.manaUsed) & ((1n << 32n) - 1n); // bits [0:31]
1080
+ value |= (feeHeader.excessMana < MASK_48_BITS ? feeHeader.excessMana : MASK_48_BITS) << 32n; // bits [32:79]
1081
+ value |= (BigInt(feeHeader.ethPerFeeAsset) & MASK_48_BITS) << 80n; // bits [80:127]
1082
+ value |= (feeHeader.congestionCost < MASK_64_BITS ? feeHeader.congestionCost : MASK_64_BITS) << 128n; // bits [128:191]
1083
+ value |= (feeHeader.proverCost < MASK_63_BITS ? feeHeader.proverCost : MASK_63_BITS) << 192n; // bits [192:254]
1084
+ value |= 1n << 255n; // preheat flag
1085
+ return value;
1086
+ }
1087
+
1088
+ /** Computes the fee header for a child checkpoint given parent fee header and child data.
1089
+ * Must stay in sync with Solidity FeeLib.sol (computeNewEthPerFeeAsset, clampedAdd). */
1090
+ public static computeChildFeeHeader(
1091
+ parentFeeHeader: FeeHeader,
1092
+ childManaUsed: bigint,
1093
+ feeAssetPriceModifier: bigint,
1094
+ manaTarget: bigint,
1095
+ ): FeeHeader {
1096
+ const MIN_ETH_PER_FEE_ASSET = 100n;
1097
+ const MAX_ETH_PER_FEE_ASSET = 100_000_000_000_000n; // 1e14, matches FeeLib.sol
1098
+
1099
+ // excessMana = clampedAdd(parent.excessMana + parent.manaUsed, -manaTarget)
1100
+ const sum = parentFeeHeader.excessMana + parentFeeHeader.manaUsed;
1101
+ const excessMana = sum > manaTarget ? sum - manaTarget : 0n;
1102
+
1103
+ // ethPerFeeAsset = computeNewEthPerFeeAsset(max(parent.ethPerFeeAsset, MIN), modifier)
1104
+ const parentPrice =
1105
+ parentFeeHeader.ethPerFeeAsset > MIN_ETH_PER_FEE_ASSET ? parentFeeHeader.ethPerFeeAsset : MIN_ETH_PER_FEE_ASSET;
1106
+ let newPrice: bigint;
1107
+ if (feeAssetPriceModifier >= 0n) {
1108
+ newPrice = (parentPrice * (10_000n + feeAssetPriceModifier)) / 10_000n;
1109
+ } else {
1110
+ const absMod = -feeAssetPriceModifier;
1111
+ newPrice = (parentPrice * (10_000n - absMod)) / 10_000n;
1112
+ }
1113
+ if (newPrice < MIN_ETH_PER_FEE_ASSET) {
1114
+ newPrice = MIN_ETH_PER_FEE_ASSET;
1115
+ }
1116
+ if (newPrice > MAX_ETH_PER_FEE_ASSET) {
1117
+ newPrice = MAX_ETH_PER_FEE_ASSET;
1118
+ }
1119
+
1120
+ return {
1121
+ excessMana,
1122
+ manaUsed: childManaUsed,
1123
+ ethPerFeeAsset: newPrice,
1124
+ congestionCost: 0n,
1125
+ proverCost: 0n,
1126
+ };
1127
+ }
1128
+
1129
+ /** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
1130
+ public buildInvalidateBadAttestationRequest(
1131
+ checkpointNumber: CheckpointNumber,
1132
+ attestationsAndSigners: ViemCommitteeAttestations,
1133
+ committee: EthAddress[],
1134
+ invalidIndex: number,
1135
+ ): L1TxRequest {
1136
+ return {
1137
+ to: this.address,
1138
+ abi: RollupAbi,
1139
+ data: encodeFunctionData({
1140
+ abi: RollupAbi,
1141
+ functionName: 'invalidateBadAttestation',
1142
+ args: [
1143
+ BigInt(checkpointNumber),
1144
+ attestationsAndSigners,
1145
+ committee.map(addr => addr.toString()),
1146
+ BigInt(invalidIndex),
1147
+ ],
1148
+ }),
1149
+ };
1150
+ }
1151
+
1152
+ /** Creates a request to Rollup#invalidateInsufficientAttestations to be simulated or sent */
1153
+ public buildInvalidateInsufficientAttestationsRequest(
1154
+ checkpointNumber: CheckpointNumber,
1155
+ attestationsAndSigners: ViemCommitteeAttestations,
1156
+ committee: EthAddress[],
1157
+ ): L1TxRequest {
1158
+ return {
1159
+ to: this.address,
1160
+ abi: RollupAbi,
1161
+ data: encodeFunctionData({
1162
+ abi: RollupAbi,
1163
+ functionName: 'invalidateInsufficientAttestations',
1164
+ args: [BigInt(checkpointNumber), attestationsAndSigners, committee.map(addr => addr.toString())],
1165
+ }),
1166
+ };
1167
+ }
1168
+
1169
+ /** Calls getHasSubmitted directly. Returns whether the given prover has submitted a proof with the given length for the given epoch. */
1170
+ public getHasSubmittedProof(epochNumber: EpochNumber, numberOfCheckpointsInEpoch: number, prover: Hex | EthAddress) {
1171
+ if (prover instanceof EthAddress) {
1172
+ prover = prover.toString();
1173
+ }
1174
+ return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
1175
+ }
1176
+
1177
+ getManaMinFeeAt(timestamp: bigint, inFeeAsset: boolean, stateOverride?: StateOverride): Promise<bigint> {
1178
+ return this.rollup.read.getManaMinFeeAt([timestamp, inFeeAsset], { stateOverride });
1179
+ }
1180
+
1181
+ async getManaMinFeeComponentsAt(timestamp: bigint, inFeeAsset: boolean): Promise<ManaMinFeeComponents> {
1182
+ const result = await this.rollup.read.getManaMinFeeComponentsAt([timestamp, inFeeAsset]);
1183
+ return {
1184
+ sequencerCost: result.sequencerCost,
1185
+ proverCost: result.proverCost,
1186
+ congestionCost: result.congestionCost,
1187
+ congestionMultiplier: result.congestionMultiplier,
1188
+ };
1189
+ }
1190
+
1191
+ async getSlotAt(timestamp: bigint): Promise<SlotNumber> {
1192
+ return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([timestamp]));
1193
+ }
1194
+
1195
+ async status(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<RollupStatusResponse> {
1196
+ await checkBlockTag(options?.blockNumber, this.client);
1197
+ const result = await this.rollup.read.status([BigInt(checkpointNumber)], options);
1198
+ return {
1199
+ provenCheckpointNumber: CheckpointNumber.fromBigInt(result[0]),
1200
+ provenArchive: Fr.fromString(result[1]),
1201
+ pendingCheckpointNumber: CheckpointNumber.fromBigInt(result[2]),
1202
+ pendingArchive: Fr.fromString(result[3]),
1203
+ archiveOfMyCheckpoint: Fr.fromString(result[4]),
1204
+ };
1205
+ }
1206
+
1207
+ async canPruneAtTime(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<boolean> {
1208
+ await checkBlockTag(options?.blockNumber, this.client);
1209
+ return this.rollup.read.canPruneAtTime([timestamp], options);
1210
+ }
1211
+
1212
+ async archive(): Promise<Fr> {
1213
+ return Fr.fromString(await this.rollup.read.archive());
1214
+ }
1215
+
1216
+ async archiveAt(checkpointNumber: CheckpointNumber): Promise<Fr> {
1217
+ return Fr.fromString(await this.rollup.read.archiveAt([BigInt(checkpointNumber)]));
1218
+ }
1219
+
1220
+ getSequencerRewards(address: Hex | EthAddress): Promise<bigint> {
1221
+ if (address instanceof EthAddress) {
1222
+ address = address.toString();
1223
+ }
1224
+ return this.rollup.read.getSequencerRewards([address]);
1225
+ }
1226
+
1227
+ getSpecificProverRewardsForEpoch(epoch: bigint, prover: Hex | EthAddress): Promise<bigint> {
1228
+ if (prover instanceof EthAddress) {
1229
+ prover = prover.toString();
1230
+ }
1231
+ return this.rollup.read.getSpecificProverRewardsForEpoch([epoch, prover]);
1232
+ }
1233
+
1234
+ async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
1235
+ // Pin every read to a single L1 block so the attester count and the chunked index reads
1236
+ // observe a consistent set. Without this, the count and each chunk default to `latest` and
1237
+ // can straddle a block boundary (or reorg), yielding an inconsistent or truncated set.
1238
+ const block = await this.client.getBlock();
1239
+ const blockNumber = block.number ?? undefined;
1240
+ const ts = timestamp ?? block.timestamp;
1241
+ const attesterSize = await this.getActiveAttesterCount({ blockNumber });
1242
+ const gse = new GSEContract(this.client, await this.getGSE());
1243
+ const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
1244
+ const chunks = chunk(indices, 1000);
1245
+
1246
+ const results = await Promise.all(
1247
+ chunks.map(chunk => gse.getAttestersFromIndicesAtTime(this.address, ts, chunk, { blockNumber })),
1248
+ );
1249
+ return results.flat().map(addr => EthAddress.fromString(addr));
1250
+ }
1251
+
1252
+ async getAttesterView(address: Hex | EthAddress): Promise<AttesterView> {
1253
+ if (address instanceof EthAddress) {
1254
+ address = address.toString();
1255
+ }
1256
+ const result = await this.rollup.read.getAttesterView([address]);
1257
+ return {
1258
+ status: result.status as AttesterStatus,
1259
+ effectiveBalance: result.effectiveBalance,
1260
+ exit: {
1261
+ withdrawalId: result.exit.withdrawalId,
1262
+ amount: result.exit.amount,
1263
+ exitableAt: result.exit.exitableAt,
1264
+ recipientOrWithdrawer: EthAddress.fromString(result.exit.recipientOrWithdrawer),
1265
+ isRecipient: result.exit.isRecipient,
1266
+ exists: result.exit.exists,
1267
+ },
1268
+ config: {
1269
+ publicKey: {
1270
+ x: result.config.publicKey.x,
1271
+ y: result.config.publicKey.y,
1272
+ },
1273
+ withdrawer: EthAddress.fromString(result.config.withdrawer),
1274
+ },
1275
+ };
1276
+ }
1277
+
1278
+ async getStatus(address: Hex | EthAddress): Promise<AttesterStatus> {
1279
+ if (address instanceof EthAddress) {
1280
+ address = address.toString();
1281
+ }
1282
+ return (await this.rollup.read.getStatus([address])) as AttesterStatus;
1283
+ }
1284
+
1285
+ async getBlobCommitmentsHash(checkpointNumber: CheckpointNumber): Promise<Buffer32> {
1286
+ return Buffer32.fromString(await this.rollup.read.getBlobCommitmentsHash([BigInt(checkpointNumber)]));
1287
+ }
1288
+
1289
+ async getCurrentBlobCommitmentsHash(): Promise<Buffer32> {
1290
+ return Buffer32.fromString(await this.rollup.read.getCurrentBlobCommitmentsHash());
1291
+ }
1292
+
1293
+ async getStakingAsset(): Promise<EthAddress> {
1294
+ return EthAddress.fromString(await this.rollup.read.getStakingAsset());
1295
+ }
1296
+
1297
+ async getRewardConfig(): Promise<RewardConfig> {
1298
+ const result = await this.rollup.read.getRewardConfig();
1299
+ return {
1300
+ rewardDistributor: EthAddress.fromString(result.rewardDistributor),
1301
+ sequencerBps: BigInt(result.sequencerBps),
1302
+ booster: EthAddress.fromString(result.booster),
1303
+ checkpointReward: result.checkpointReward,
1304
+ };
1305
+ }
1306
+
1307
+ setupEpoch(l1TxUtils: L1TxUtils) {
1308
+ return l1TxUtils.sendAndMonitorTransaction({
1309
+ to: this.address,
1310
+ abi: RollupAbi,
1311
+ data: encodeFunctionData({
1312
+ abi: RollupAbi,
1313
+ functionName: 'setupEpoch',
1314
+ args: [],
1315
+ }),
1316
+ });
1317
+ }
1318
+
1319
+ vote(l1TxUtils: L1TxUtils, proposalId: bigint) {
1320
+ return l1TxUtils.sendAndMonitorTransaction({
1321
+ to: this.address,
1322
+ abi: RollupAbi,
1323
+ data: encodeFunctionData({
1324
+ abi: RollupAbi,
1325
+ functionName: 'vote',
1326
+ args: [proposalId],
1327
+ }),
1328
+ });
1329
+ }
1330
+
1331
+ /**
1332
+ * Watches for SlasherUpdated events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit them and
1333
+ * removals are never reported, and events mined within roughly one polling interval of subscribing may be missed.
1334
+ * Slasher rotations are rare governance operations, so by default this polls much slower than the other watchers.
1335
+ */
1336
+ public listenToSlasherChanged(
1337
+ callback: (args: { oldSlasher: `0x${string}`; newSlasher: `0x${string}` }) => unknown,
1338
+ options?: WatchContractEventOptions,
1339
+ ): WatchContractEventReturnType {
1340
+ return watchContractEvent(
1341
+ this.client,
1342
+ this.logger,
1343
+ {
1344
+ address: this.address,
1345
+ abi: RollupAbi,
1346
+ eventName: 'SlasherUpdated',
1347
+ onLog: log => {
1348
+ const { oldSlasher, newSlasher } = log.args;
1349
+ if (oldSlasher && newSlasher) {
1350
+ return callback({ oldSlasher, newSlasher });
1351
+ }
1352
+ },
1353
+ },
1354
+ { pollingIntervalMs: SLASHER_UPDATED_POLLING_INTERVAL_MS, ...options },
1355
+ );
1356
+ }
1357
+
1358
+ /**
1359
+ * Watches for CheckpointInvalidated events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit
1360
+ * them and removals are never reported, and events mined within roughly one polling interval of subscribing may
1361
+ * be missed.
1362
+ */
1363
+ public listenToCheckpointInvalidated(
1364
+ callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
1365
+ options?: WatchContractEventOptions,
1366
+ ): WatchContractEventReturnType {
1367
+ return watchContractEvent(
1368
+ this.client,
1369
+ this.logger,
1370
+ {
1371
+ address: this.address,
1372
+ abi: RollupAbi,
1373
+ eventName: 'CheckpointInvalidated',
1374
+ onLog: log => {
1375
+ const { checkpointNumber } = log.args;
1376
+ if (checkpointNumber !== undefined) {
1377
+ return callback({ checkpointNumber: CheckpointNumber.fromBigInt(checkpointNumber), event: log });
1378
+ }
1379
+ },
1380
+ },
1381
+ options,
1382
+ );
1383
+ }
1384
+
1385
+ public async getSlashEvents(l1BlockHash: Hex): Promise<{ amount: bigint; attester: EthAddress }[]> {
1386
+ const events = await this.rollup.getEvents.Slashed({}, { blockHash: l1BlockHash, strict: true });
1387
+ return events.map(event => ({
1388
+ amount: event.args.amount!,
1389
+ attester: EthAddress.fromString(event.args.attester!),
1390
+ }));
1391
+ }
1392
+
1393
+ /**
1394
+ * Watches for Slashed events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit them and
1395
+ * removals are never reported, and events mined within roughly one polling interval of subscribing may be missed.
1396
+ */
1397
+ public listenToSlash(
1398
+ callback: (args: { amount: bigint; attester: EthAddress }) => unknown,
1399
+ options?: WatchContractEventOptions,
1400
+ ): WatchContractEventReturnType {
1401
+ return watchContractEvent(
1402
+ this.client,
1403
+ this.logger,
1404
+ {
1405
+ address: this.address,
1406
+ abi: RollupAbi,
1407
+ eventName: 'Slashed',
1408
+ strict: true,
1409
+ onLog: log => callback({ amount: log.args.amount, attester: EthAddress.fromString(log.args.attester) }),
1410
+ },
1411
+ options,
1412
+ );
1413
+ }
1414
+
1415
+ /**
1416
+ * Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
1417
+ * The Rollup inherits from Ownable and emits this event in its constructor, so the event
1418
+ * is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
1419
+ * probe to detect RPC nodes that prune historical logs.
1420
+ */
1421
+ async getOwnershipTransferredEventsAtDeploy() {
1422
+ const l1StartBlock = await this.getL1StartBlock();
1423
+ return await this.rollup.getEvents.OwnershipTransferred({}, { fromBlock: l1StartBlock, toBlock: l1StartBlock });
1424
+ }
1425
+
1426
+ /** Fetches CheckpointProposed events within the given block range. */
1427
+ async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
1428
+ const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
1429
+ return logs
1430
+ .filter(log => log.blockNumber! >= fromBlock && log.blockNumber! <= toBlock)
1431
+ .map(log => ({
1432
+ l1BlockNumber: log.blockNumber!,
1433
+ l1BlockHash: Buffer32.fromString(log.blockHash!),
1434
+ l1TransactionHash: log.transactionHash!,
1435
+ args: {
1436
+ checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
1437
+ archive: Fr.fromString(log.args.archive!),
1438
+ versionedBlobHashes: log.args.versionedBlobHashes!.map(h => Buffer.from(h.slice(2), 'hex')),
1439
+ attestationsHash: (() => {
1440
+ if (!log.args.attestationsHash) {
1441
+ throw new Error(
1442
+ `CheckpointProposed event missing attestationsHash for checkpoint ${log.args.checkpointNumber}`,
1443
+ );
1444
+ }
1445
+ return Buffer32.fromString(log.args.attestationsHash);
1446
+ })(),
1447
+ payloadDigest: (() => {
1448
+ if (!log.args.payloadDigest) {
1449
+ throw new Error(
1450
+ `CheckpointProposed event missing payloadDigest for checkpoint ${log.args.checkpointNumber}`,
1451
+ );
1452
+ }
1453
+ return Buffer32.fromString(log.args.payloadDigest);
1454
+ })(),
1455
+ },
1456
+ }));
1457
+ }
1458
+
1459
+ /** Packs pending and proven checkpoint numbers into the chain tips storage format. */
1460
+ static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
1461
+ return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
1462
+ }
1463
+
1464
+ /** Storage slot for the chain tips (offset 0 within the STF storage struct). */
1465
+ static get chainTipsStorageSlot(): bigint {
1466
+ return BigInt(RollupContract.stfStorageSlot);
1467
+ }
1468
+
1469
+ /**
1470
+ * Computes the storage slot for a field within a tempCheckpointLog entry.
1471
+ * @param checkpointNumber - The checkpoint number
1472
+ * @param field - The field within the CompressedTempCheckpointLog struct
1473
+ */
1474
+ async getTempCheckpointLogStorageSlot(
1475
+ checkpointNumber: CheckpointNumber,
1476
+ field: TempCheckpointLogField,
1477
+ ): Promise<bigint> {
1478
+ const [epochDuration, proofSubmissionEpochs] = await Promise.all([
1479
+ this.getEpochDuration(),
1480
+ this.getProofSubmissionEpochs(),
1481
+ ]);
1482
+ return this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, { epochDuration, proofSubmissionEpochs });
1483
+ }
1484
+
1485
+ private computeTempCheckpointLogStorageSlot(
1486
+ checkpointNumber: CheckpointNumber,
1487
+ field: TempCheckpointLogField,
1488
+ constants: { epochDuration: number; proofSubmissionEpochs: number },
1489
+ ): bigint {
1490
+ const fieldOffset = BigInt(field);
1491
+ const { epochDuration, proofSubmissionEpochs } = constants;
1492
+ const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
1493
+ const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
1494
+ const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
1495
+ const entryBase = BigInt(
1496
+ keccak256(
1497
+ encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
1498
+ ),
1499
+ );
1500
+ return entryBase + fieldOffset;
1501
+ }
1502
+ }