@aztec/ethereum 0.0.0-test.1 → 0.0.1-commit.017a351

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