@aztec-labs/ethereum 6.0.0-rc.3 → 6.0.0

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 (114) hide show
  1. package/dest/blob_tx.d.ts +24 -0
  2. package/dest/blob_tx.d.ts.map +1 -0
  3. package/dest/blob_tx.js +94 -0
  4. package/dest/bn254/hash_to_point.d.ts +32 -0
  5. package/dest/bn254/hash_to_point.d.ts.map +1 -0
  6. package/dest/bn254/hash_to_point.js +80 -0
  7. package/dest/bn254/index.d.ts +3 -0
  8. package/dest/bn254/index.d.ts.map +1 -0
  9. package/dest/bn254/index.js +2 -0
  10. package/dest/bn254/proof_of_possession_gas.d.ts +29 -0
  11. package/dest/bn254/proof_of_possession_gas.d.ts.map +1 -0
  12. package/dest/bn254/proof_of_possession_gas.js +24 -0
  13. package/dest/client.d.ts +1 -1
  14. package/dest/client.d.ts.map +1 -1
  15. package/dest/client.js +5 -3
  16. package/dest/contracts/errors.d.ts +10 -3
  17. package/dest/contracts/errors.d.ts.map +1 -1
  18. package/dest/contracts/errors.js +10 -4
  19. package/dest/contracts/inbox.d.ts +1 -1
  20. package/dest/contracts/inbox.d.ts.map +1 -1
  21. package/dest/contracts/inbox.js +2 -0
  22. package/dest/contracts/log.d.ts +7 -1
  23. package/dest/contracts/log.d.ts.map +1 -1
  24. package/dest/contracts/log.js +6 -0
  25. package/dest/contracts/rollup.d.ts +32 -4
  26. package/dest/contracts/rollup.d.ts.map +1 -1
  27. package/dest/contracts/rollup.js +63 -7
  28. package/dest/deploy_aztec_l1_contracts.d.ts +16 -1
  29. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  30. package/dest/deploy_aztec_l1_contracts.js +201 -65
  31. package/dest/foundry_binary.d.ts +1 -1
  32. package/dest/foundry_binary.d.ts.map +1 -1
  33. package/dest/foundry_binary.js +2 -1
  34. package/dest/l1_tx_utils/amsterdam.d.ts +1 -1
  35. package/dest/l1_tx_utils/amsterdam.d.ts.map +1 -1
  36. package/dest/l1_tx_utils/amsterdam.js +4 -1
  37. package/dest/l1_tx_utils/constants.d.ts +2 -1
  38. package/dest/l1_tx_utils/constants.d.ts.map +1 -1
  39. package/dest/l1_tx_utils/constants.js +3 -0
  40. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +1 -1
  41. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -1
  42. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +6 -1
  43. package/dest/l1_tx_utils/index.d.ts +2 -1
  44. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  45. package/dest/l1_tx_utils/index.js +1 -0
  46. package/dest/l1_tx_utils/l1_tx_utils.d.ts +21 -1
  47. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  48. package/dest/l1_tx_utils/l1_tx_utils.js +71 -5
  49. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +8 -1
  50. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  51. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +1 -1
  52. package/dest/l1_tx_utils/send_cost.d.ts +15 -0
  53. package/dest/l1_tx_utils/send_cost.d.ts.map +1 -0
  54. package/dest/l1_tx_utils/send_cost.js +10 -0
  55. package/dest/l1_tx_utils/signer.d.ts +2 -2
  56. package/dest/l1_tx_utils/signer.d.ts.map +1 -1
  57. package/dest/l1_tx_utils/signer.js +2 -2
  58. package/dest/l1_tx_utils/tx_delayer.d.ts +1 -1
  59. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -1
  60. package/dest/l1_tx_utils/tx_delayer.js +8 -26
  61. package/dest/publisher_manager.d.ts +127 -13
  62. package/dest/publisher_manager.d.ts.map +1 -1
  63. package/dest/publisher_manager.js +320 -79
  64. package/dest/test/anvil_hardfork.d.ts +42 -0
  65. package/dest/test/anvil_hardfork.d.ts.map +1 -0
  66. package/dest/test/anvil_hardfork.js +107 -0
  67. package/dest/test/eth_cheat_codes.d.ts +30 -5
  68. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  69. package/dest/test/eth_cheat_codes.js +283 -101
  70. package/dest/test/index.d.ts +3 -1
  71. package/dest/test/index.d.ts.map +1 -1
  72. package/dest/test/index.js +2 -0
  73. package/dest/test/l1_hardfork.d.ts +43 -0
  74. package/dest/test/l1_hardfork.d.ts.map +1 -0
  75. package/dest/test/l1_hardfork.js +51 -0
  76. package/dest/test/recording_rpc_server.d.ts +1 -1
  77. package/dest/test/recording_rpc_server.d.ts.map +1 -1
  78. package/dest/test/recording_rpc_server.js +1 -0
  79. package/dest/test/rollup_cheat_codes.d.ts +1 -1
  80. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  81. package/dest/test/rollup_cheat_codes.js +7 -7
  82. package/dest/test/start_anvil.d.ts +14 -2
  83. package/dest/test/start_anvil.d.ts.map +1 -1
  84. package/dest/test/start_anvil.js +28 -11
  85. package/package.json +13 -11
  86. package/src/blob_tx.ts +118 -0
  87. package/src/bn254/hash_to_point.ts +95 -0
  88. package/src/bn254/index.ts +2 -0
  89. package/src/bn254/proof_of_possession_gas.ts +54 -0
  90. package/src/client.ts +6 -3
  91. package/src/contracts/errors.ts +12 -4
  92. package/src/contracts/inbox.ts +4 -0
  93. package/src/contracts/log.ts +12 -0
  94. package/src/contracts/rollup.ts +63 -6
  95. package/src/deploy_aztec_l1_contracts.ts +238 -63
  96. package/src/foundry_binary.ts +2 -1
  97. package/src/l1_tx_utils/amsterdam.ts +4 -1
  98. package/src/l1_tx_utils/constants.ts +4 -0
  99. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +11 -1
  100. package/src/l1_tx_utils/index.ts +1 -0
  101. package/src/l1_tx_utils/l1_tx_utils.ts +72 -5
  102. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +1 -1
  103. package/src/l1_tx_utils/send_cost.ts +21 -0
  104. package/src/l1_tx_utils/signer.ts +3 -2
  105. package/src/l1_tx_utils/tx_delayer.ts +8 -37
  106. package/src/publisher_manager.ts +458 -83
  107. package/src/test/anvil_hardfork.ts +95 -0
  108. package/src/test/eth_cheat_codes.ts +306 -81
  109. package/src/test/fixtures/sepolia_blob_tx.json +10 -0
  110. package/src/test/index.ts +2 -0
  111. package/src/test/l1_hardfork.ts +71 -0
  112. package/src/test/recording_rpc_server.ts +1 -0
  113. package/src/test/rollup_cheat_codes.ts +9 -9
  114. package/src/test/start_anvil.ts +41 -13
@@ -0,0 +1,54 @@
1
+ import { computeBn254RegistrationDigestForPrivateKey } from './hash_to_point.js';
2
+
3
+ /**
4
+ * Upper-bound model of the minimum gas stipend `Bn254LibWrapper.proofOfPossession` needs to accept a valid key, as a
5
+ * function of the `hashToPoint` loop work for that key:
6
+ * `fixed + perAttempt * attempts + perSqrtCall * sqrtCalls + ceil(memoryNumerator * attempts^2 / memoryDenominator)`.
7
+ */
8
+ export type ProofOfPossessionGasModel = {
9
+ /** Work that does not depend on the key: pairing, scalar multiplications, hashing, stipend headroom. */
10
+ fixed: number;
11
+ /** Per loop attempt: keccak, ABI encoding and loop overhead. */
12
+ perAttempt: number;
13
+ /** Per attempt that reaches the modexp square root. */
14
+ perSqrtCall: number;
15
+ /** Memory growth from allocations that every attempt leaves behind, quadratic in the attempts. */
16
+ memoryNumerator: number;
17
+ memoryDenominator: number;
18
+ };
19
+
20
+ /**
21
+ * Conservative min-stipend fit of the BN254 proof-of-possession gas calibration vectors measured under amsterdam
22
+ * pricing, which matches osaka on this path. The vectors are aztec-packages `l1-contracts/test/fixtures/
23
+ * bn254_pop_gas_vectors.json`, copied verbatim to `fixtures/` next to this file.
24
+ */
25
+ export const AMSTERDAM_PROOF_OF_POSSESSION_GAS_MODEL: ProofOfPossessionGasModel = {
26
+ fixed: 132_694,
27
+ perAttempt: 515,
28
+ perSqrtCall: 4_580,
29
+ memoryNumerator: 95_975,
30
+ memoryDenominator: 1_000_000,
31
+ };
32
+
33
+ /** Estimated minimum stipend for the proof-of-possession check of a key with the given `hashToPoint` work. */
34
+ export function estimateProofOfPossessionGas(model: ProofOfPossessionGasModel, attempts: number, sqrtCalls: number) {
35
+ const memory = BigInt(model.memoryNumerator) * BigInt(attempts) ** 2n;
36
+ const denominator = BigInt(model.memoryDenominator);
37
+ return (
38
+ model.fixed +
39
+ model.perAttempt * attempts +
40
+ model.perSqrtCall * sqrtCalls +
41
+ Number((memory + denominator - 1n) / denominator)
42
+ );
43
+ }
44
+
45
+ /** Estimated minimum stipend for the proof-of-possession check of a BN254 BLS private key. */
46
+ export function estimateBlsKeyProofOfPossessionGas(model: ProofOfPossessionGasModel, privateKey: string) {
47
+ const { attempts, sqrtCalls } = computeBn254RegistrationDigestForPrivateKey(privateKey);
48
+ return estimateProofOfPossessionGas(model, attempts, sqrtCalls);
49
+ }
50
+
51
+ /** Lowest gas estimate any key can have: one attempt that reaches the square root and succeeds. */
52
+ export function minimumProofOfPossessionGas(model: ProofOfPossessionGasModel) {
53
+ return estimateProofOfPossessionGas(model, 1, 1);
54
+ }
package/src/client.ts CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
20
20
  import { foundry } from 'viem/chains';
21
21
 
22
- import { createEthereumChain } from './chain.js';
22
+ import { createEthereumChain, isAnvilTestChain } from './chain.js';
23
23
  import type { ExtendedViemWalletClient, ViemPublicClient } from './types.js';
24
24
 
25
25
  type Config = {
@@ -45,7 +45,10 @@ export class L1RpcError extends Error {
45
45
 
46
46
  /** Creates a viem fallback HTTP transport for the given L1 RPC URLs. */
47
47
  export function makeL1HttpTransport(rpcUrls: string[], opts?: { timeout?: number }) {
48
- return wrapL1RpcTransport(fallback(rpcUrls.map(url => http(url, { batch: false, timeout: opts?.timeout }))));
48
+ // viem caps response bodies at 10MB by default, which large getLogs or debug trace responses can exceed.
49
+ return wrapL1RpcTransport(
50
+ fallback(rpcUrls.map(url => http(url, { batch: false, timeout: opts?.timeout, maxResponseBodySize: false }))),
51
+ );
49
52
  }
50
53
 
51
54
  /** Returns the HTTP status from an L1 RPC error's cause chain, if one is available. */
@@ -158,7 +161,7 @@ export function createExtendedL1Client(
158
161
  account: hdAccount,
159
162
  chain,
160
163
  transport: makeL1HttpTransport(rpcUrls, { timeout: opts?.httpTimeoutMS }),
161
- pollingInterval: pollingIntervalMS,
164
+ pollingInterval: pollingIntervalMS ?? (isAnvilTestChain(chain.id) ? 100 : undefined),
162
165
  }).extend(publicActions);
163
166
 
164
167
  return extendedClient;
@@ -5,9 +5,17 @@ export class BlockTagTooOldError extends Error {
5
5
  }
6
6
  }
7
7
 
8
- export class NoCommitteeError extends Error {
9
- constructor() {
10
- super('The committee does not exist on L1');
11
- this.name = 'NoCommitteeError';
8
+ /**
9
+ * The committee for an epoch cannot be served yet because its sample seed can still change on L1: the randao it
10
+ * samples has not been checkpointed, and the epoch that would checkpoint it is not over. Thrown by callers that cannot
11
+ * act without the committee and retry later.
12
+ */
13
+ export class CommitteeNotStableError extends Error {
14
+ constructor(
15
+ public readonly epoch: bigint,
16
+ public readonly l1BlockNumber: bigint,
17
+ ) {
18
+ super(`The committee for epoch ${epoch} is not stable yet as of L1 block ${l1BlockNumber}`);
19
+ this.name = 'CommitteeNotStableError';
12
20
  }
13
21
  }
@@ -192,6 +192,8 @@ export class InboxContract {
192
192
  blockNumber: bigint | null;
193
193
  blockHash: `0x${string}` | null;
194
194
  transactionHash: `0x${string}` | null;
195
+ transactionIndex: number | null;
196
+ logIndex: number | null;
195
197
  args: {
196
198
  hash?: `0x${string}`;
197
199
  inboxRollingHash?: `0x${string}`;
@@ -210,6 +212,8 @@ export class InboxContract {
210
212
  l1BlockNumber: log.blockNumber!,
211
213
  l1BlockHash: Buffer32.fromString(log.blockHash!),
212
214
  l1TransactionHash: log.transactionHash!,
215
+ l1TransactionIndex: log.transactionIndex!,
216
+ l1LogIndex: log.logIndex!,
213
217
  args: {
214
218
  index: message.index,
215
219
  leaf: Fr.fromString(log.args.hash!),
@@ -8,10 +8,22 @@ export type L1EventLog<T> = {
8
8
  l1BlockHash: Buffer32;
9
9
  /** L1 transaction hash that emitted the event. */
10
10
  l1TransactionHash: `0x${string}`;
11
+ /** Index of the emitting transaction within its L1 block. */
12
+ l1TransactionIndex: number;
13
+ /** Index of the log within its L1 block (not within its transaction), as `eth_getLogs` reports it. */
14
+ l1LogIndex: number;
11
15
  /** Event-specific arguments. */
12
16
  args: T;
13
17
  };
14
18
 
19
+ /** Orders L1 event logs by their position on L1: block number, then transaction index, then log index. */
20
+ export function compareL1LogPosition(a: L1EventLog<unknown>, b: L1EventLog<unknown>): number {
21
+ if (a.l1BlockNumber !== b.l1BlockNumber) {
22
+ return a.l1BlockNumber < b.l1BlockNumber ? -1 : 1;
23
+ }
24
+ return a.l1TransactionIndex - b.l1TransactionIndex || a.l1LogIndex - b.l1LogIndex;
25
+ }
26
+
15
27
  /**
16
28
  * Message fragments providers use to refuse a log query for being too wide or returning too much, the failures a
17
29
  * narrower range is known to fix. Matched on text because the JSON-RPC boundary leaves no error class or code behind
@@ -12,6 +12,7 @@ import { Signature, type ViemSignature } from '@aztec-labs/foundation/eth-signat
12
12
  import { createLogger } from '@aztec-labs/foundation/log';
13
13
  import { makeBackoff, retry } from '@aztec-labs/foundation/retry';
14
14
  import { getErrorCause } from '@aztec-labs/foundation/types';
15
+ import { Buffer } from 'buffer';
15
16
  import chunk from 'lodash.chunk';
16
17
  import {
17
18
  type AbiParameter,
@@ -342,6 +343,7 @@ export type CheckpointProposedArgs = {
342
343
  export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
343
344
 
344
345
  const INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR = 'ValidatorSelection__InsufficientValidatorSetSize';
346
+ const EPOCH_NOT_STABLE_ERROR = 'ValidatorSelection__EpochNotStable';
345
347
 
346
348
  /** SlasherUpdated events are rare governance operations, so their watcher polls well below the client's interval. */
347
349
  const SLASHER_UPDATED_POLLING_INTERVAL_MS = 60_000;
@@ -354,6 +356,11 @@ function isValidatorSelectionError(err: unknown, errorName: string): boolean {
354
356
  );
355
357
  }
356
358
 
359
+ /** Whether a rollup read reverted because the requested epoch's committee or seed can still change. */
360
+ export function isEpochNotStableError(err: unknown): boolean {
361
+ return isValidatorSelectionError(err, EPOCH_NOT_STABLE_ERROR);
362
+ }
363
+
357
364
  function decodeRpcRequestErrorName(err: unknown): string | undefined {
358
365
  const data = getErrorCause(err, RpcRequestError)?.data;
359
366
  if (!isHexString(data)) {
@@ -383,6 +390,7 @@ export class RollupContract {
383
390
  private readonly logger = createLogger('ethereum:rollup');
384
391
 
385
392
  private static cachedStfStorageSlot: Hex | undefined;
393
+ private static cachedValidatorSelectionStorageSlot: Hex | undefined;
386
394
  private cachedEscapeHatch?: {
387
395
  address: EthAddress;
388
396
  contract: GetContractReturnType<typeof EscapeHatchAbi, ViemClient>;
@@ -400,6 +408,13 @@ export class RollupContract {
400
408
  return (RollupContract.cachedStfStorageSlot ??= keccak256(Buffer.from('aztec.stf.storage', 'utf-8')));
401
409
  }
402
410
 
411
+ /** Base slot of the rollup's validator selection storage struct, whose first field maps epochs to committee commitments. */
412
+ static get validatorSelectionStorageSlot(): Hex {
413
+ return (RollupContract.cachedValidatorSelectionStorageSlot ??= keccak256(
414
+ Buffer.from('aztec.validator_selection.storage', 'utf-8'),
415
+ ));
416
+ }
417
+
403
418
  static getFromL1ContractsValues(deployL1ContractsValues: DeployAztecL1ContractsReturnType) {
404
419
  const {
405
420
  l1Client,
@@ -645,15 +660,18 @@ export class RollupContract {
645
660
  * This function is intentionally defensive: any failure to query the escape hatch
646
661
  * (RPC issues, transient errors, etc.) is treated as "closed" to avoid callers
647
662
  * needing to sprinkle try/catch everywhere.
663
+ *
664
+ * `options.blockNumber` pins only the hatch's `isHatchOpen` read: the escape hatch address is always resolved at the
665
+ * latest L1 block.
648
666
  */
649
- async isEscapeHatchOpen(epoch: EpochNumber): Promise<boolean> {
667
+ async isEscapeHatchOpen(epoch: EpochNumber, options?: { blockNumber?: bigint }): Promise<boolean> {
650
668
  try {
651
669
  const escapeHatch = await this.getEscapeHatchContract();
652
670
  if (!escapeHatch) {
653
671
  return false;
654
672
  }
655
673
 
656
- const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)]);
674
+ const [isOpen] = await escapeHatch.read.isHatchOpen([BigInt(epoch)], options);
657
675
  return isOpen;
658
676
  } catch (err) {
659
677
  this.logger.warn('isEscapeHatchOpen failed (treating as closed); RPC or contract error may cause liveness risk', {
@@ -745,13 +763,14 @@ export class RollupContract {
745
763
  return this.rollup.read.getEthPerFeeAsset();
746
764
  }
747
765
 
748
- async getCommitteeAt(timestamp: bigint): Promise<EthAddress[] | undefined> {
766
+ async getCommitteeAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<EthAddress[] | undefined> {
749
767
  const { result } = await this.client
750
768
  .simulateContract({
751
769
  address: this.address,
752
770
  abi: RollupAbi,
753
771
  functionName: 'getCommitteeAt',
754
772
  args: [timestamp],
773
+ blockNumber: options?.blockNumber,
755
774
  })
756
775
  .catch(e => {
757
776
  if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
@@ -763,8 +782,8 @@ export class RollupContract {
763
782
  return result ? result.map(addr => EthAddress.fromString(addr)) : undefined;
764
783
  }
765
784
 
766
- async getSampleSeedAt(timestamp: bigint): Promise<Buffer32> {
767
- return Buffer32.fromBigInt(await this.rollup.read.getSampleSeedAt([timestamp]));
785
+ async getSampleSeedAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<Buffer32> {
786
+ return Buffer32.fromBigInt(await this.rollup.read.getSampleSeedAt([timestamp], options));
768
787
  }
769
788
 
770
789
  async getCurrentSampleSeed(): Promise<Buffer32> {
@@ -775,6 +794,26 @@ export class RollupContract {
775
794
  return EpochNumber.fromBigInt(await this.rollup.read.getCurrentEpoch());
776
795
  }
777
796
 
797
+ /**
798
+ * Whether `setupEpoch` has already run for the epoch, i.e. its committee commitment is stored, as of
799
+ * `options.blockNumber` (the latest L1 block by default).
800
+ * Read straight from storage because `getEpochCommitteeCommitment` computes the commitment when it is not stored.
801
+ */
802
+ async isEpochSetUp(epoch: EpochNumber, options?: { blockNumber?: bigint }): Promise<boolean> {
803
+ const slot = keccak256(
804
+ encodeAbiParameters(
805
+ [{ type: 'uint256' }, { type: 'uint256' }],
806
+ [BigInt(epoch), BigInt(RollupContract.validatorSelectionStorageSlot)],
807
+ ),
808
+ );
809
+ const value = await this.client.getStorageAt(
810
+ options?.blockNumber === undefined
811
+ ? { address: this.address, slot, blockTag: 'latest' }
812
+ : { address: this.address, slot, blockNumber: options.blockNumber },
813
+ );
814
+ return value !== undefined && hexToBigInt(value) !== 0n;
815
+ }
816
+
778
817
  async getCurrentEpochCommittee(): Promise<EthAddress[] | undefined> {
779
818
  const { result } = await this.client
780
819
  .simulateContract({
@@ -1317,6 +1356,21 @@ export class RollupContract {
1317
1356
  return this.rollup.read.getHasSubmitted([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
1318
1357
  }
1319
1358
 
1359
+ /**
1360
+ * Calls getHasSubmittedFullEpoch directly. Returns whether the given prover's proof with the given length for the
1361
+ * given epoch was registered as a full-epoch proof, which is the only kind that raises the prover's activity score.
1362
+ */
1363
+ public getHasSubmittedFullEpochProof(
1364
+ epochNumber: EpochNumber,
1365
+ numberOfCheckpointsInEpoch: number,
1366
+ prover: Hex | EthAddress,
1367
+ ): Promise<boolean> {
1368
+ if (prover instanceof EthAddress) {
1369
+ prover = prover.toString();
1370
+ }
1371
+ return this.rollup.read.getHasSubmittedFullEpoch([BigInt(epochNumber), BigInt(numberOfCheckpointsInEpoch), prover]);
1372
+ }
1373
+
1320
1374
  /**
1321
1375
  * Returns the minimum mana fee at the given timestamp.
1322
1376
  * @param options - Optional state override to simulate against, and optional L1 block number to pin the call
@@ -1391,7 +1445,8 @@ export class RollupContract {
1391
1445
  const block = await this.client.getBlock();
1392
1446
  const blockNumber = block.number ?? undefined;
1393
1447
  const ts = timestamp ?? block.timestamp;
1394
- const attesterSize = await this.getActiveAttesterCount({ blockNumber });
1448
+ // Count at ts, matching the addresses below; the current count would truncate a shrunk set.
1449
+ const attesterSize = await this.getAttesterCountAtTime(ts, { blockNumber });
1395
1450
  const gse = new GSEContract(this.client, await this.getGSE());
1396
1451
  const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
1397
1452
  const chunks = chunk(indices, 1000);
@@ -1822,6 +1877,8 @@ export class RollupContract {
1822
1877
  l1BlockNumber: log.blockNumber!,
1823
1878
  l1BlockHash: Buffer32.fromString(log.blockHash!),
1824
1879
  l1TransactionHash: log.transactionHash!,
1880
+ l1TransactionIndex: log.transactionIndex!,
1881
+ l1LogIndex: log.logIndex!,
1825
1882
  args: {
1826
1883
  checkpointNumber: CheckpointNumber.fromBigInt(log.args.checkpointNumber!),
1827
1884
  archive: Fr.fromString(log.args.archive!),