@aztec/ethereum 3.0.0-canary.a9708bd → 3.0.0-devnet.2

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 (144) hide show
  1. package/dest/client.d.ts +1 -1
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/config.d.ts +11 -6
  4. package/dest/config.d.ts.map +1 -1
  5. package/dest/config.js +124 -64
  6. package/dest/contracts/empire_base.d.ts +1 -1
  7. package/dest/contracts/empire_base.d.ts.map +1 -1
  8. package/dest/contracts/empire_slashing_proposer.d.ts +2 -2
  9. package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
  10. package/dest/contracts/empire_slashing_proposer.js +1 -1
  11. package/dest/contracts/fee_asset_handler.d.ts +3 -3
  12. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  13. package/dest/contracts/governance.js +7 -3
  14. package/dest/contracts/governance_proposer.d.ts +1 -2
  15. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  16. package/dest/contracts/governance_proposer.js +1 -2
  17. package/dest/contracts/multicall.d.ts +3 -5
  18. package/dest/contracts/multicall.d.ts.map +1 -1
  19. package/dest/contracts/multicall.js +6 -4
  20. package/dest/contracts/rollup.d.ts +39 -19
  21. package/dest/contracts/rollup.d.ts.map +1 -1
  22. package/dest/contracts/rollup.js +84 -88
  23. package/dest/contracts/slasher_contract.d.ts +10 -0
  24. package/dest/contracts/slasher_contract.d.ts.map +1 -1
  25. package/dest/contracts/slasher_contract.js +18 -0
  26. package/dest/contracts/tally_slashing_proposer.d.ts +22 -3
  27. package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
  28. package/dest/contracts/tally_slashing_proposer.js +55 -5
  29. package/dest/deploy_l1_contracts.d.ts +22 -7
  30. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  31. package/dest/deploy_l1_contracts.js +555 -362
  32. package/dest/index.d.ts +1 -1
  33. package/dest/index.d.ts.map +1 -1
  34. package/dest/index.js +1 -1
  35. package/dest/l1_artifacts.d.ts +8729 -6014
  36. package/dest/l1_artifacts.d.ts.map +1 -1
  37. package/dest/l1_artifacts.js +10 -5
  38. package/dest/l1_contract_addresses.d.ts +5 -1
  39. package/dest/l1_contract_addresses.d.ts.map +1 -1
  40. package/dest/l1_contract_addresses.js +16 -26
  41. package/dest/l1_reader.d.ts +1 -1
  42. package/dest/l1_reader.d.ts.map +1 -1
  43. package/dest/l1_reader.js +8 -8
  44. package/dest/l1_tx_utils/config.d.ts +59 -0
  45. package/dest/l1_tx_utils/config.d.ts.map +1 -0
  46. package/dest/l1_tx_utils/config.js +73 -0
  47. package/dest/l1_tx_utils/constants.d.ts +6 -0
  48. package/dest/l1_tx_utils/constants.d.ts.map +1 -0
  49. package/dest/l1_tx_utils/constants.js +14 -0
  50. package/dest/l1_tx_utils/factory.d.ts +24 -0
  51. package/dest/l1_tx_utils/factory.d.ts.map +1 -0
  52. package/dest/l1_tx_utils/factory.js +12 -0
  53. package/dest/l1_tx_utils/index.d.ts +10 -0
  54. package/dest/l1_tx_utils/index.d.ts.map +1 -0
  55. package/dest/l1_tx_utils/index.js +10 -0
  56. package/dest/l1_tx_utils/interfaces.d.ts +76 -0
  57. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -0
  58. package/dest/l1_tx_utils/interfaces.js +4 -0
  59. package/dest/l1_tx_utils/l1_tx_utils.d.ts +95 -0
  60. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -0
  61. package/dest/l1_tx_utils/l1_tx_utils.js +610 -0
  62. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +26 -0
  63. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +1 -0
  64. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +26 -0
  65. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +81 -0
  66. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -0
  67. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +294 -0
  68. package/dest/l1_tx_utils/signer.d.ts +4 -0
  69. package/dest/l1_tx_utils/signer.d.ts.map +1 -0
  70. package/dest/l1_tx_utils/signer.js +16 -0
  71. package/dest/l1_tx_utils/types.d.ts +67 -0
  72. package/dest/l1_tx_utils/types.d.ts.map +1 -0
  73. package/dest/l1_tx_utils/types.js +26 -0
  74. package/dest/l1_tx_utils/utils.d.ts +4 -0
  75. package/dest/l1_tx_utils/utils.d.ts.map +1 -0
  76. package/dest/l1_tx_utils/utils.js +14 -0
  77. package/dest/publisher_manager.d.ts +7 -2
  78. package/dest/publisher_manager.d.ts.map +1 -1
  79. package/dest/publisher_manager.js +36 -8
  80. package/dest/queries.d.ts.map +1 -1
  81. package/dest/queries.js +11 -12
  82. package/dest/test/chain_monitor.d.ts +11 -0
  83. package/dest/test/chain_monitor.d.ts.map +1 -1
  84. package/dest/test/chain_monitor.js +81 -12
  85. package/dest/test/delayed_tx_utils.d.ts +2 -2
  86. package/dest/test/delayed_tx_utils.d.ts.map +1 -1
  87. package/dest/test/delayed_tx_utils.js +2 -2
  88. package/dest/test/eth_cheat_codes.d.ts +32 -6
  89. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  90. package/dest/test/eth_cheat_codes.js +115 -28
  91. package/dest/test/rollup_cheat_codes.d.ts +11 -9
  92. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  93. package/dest/test/rollup_cheat_codes.js +38 -6
  94. package/dest/test/upgrade_utils.d.ts.map +1 -1
  95. package/dest/test/upgrade_utils.js +3 -2
  96. package/dest/utils.d.ts.map +1 -1
  97. package/dest/utils.js +10 -161
  98. package/dest/zkPassportVerifierAddress.js +1 -1
  99. package/package.json +7 -7
  100. package/src/client.ts +1 -1
  101. package/src/config.ts +136 -68
  102. package/src/contracts/empire_base.ts +1 -1
  103. package/src/contracts/empire_slashing_proposer.ts +7 -3
  104. package/src/contracts/fee_asset_handler.ts +1 -1
  105. package/src/contracts/governance.ts +3 -3
  106. package/src/contracts/governance_proposer.ts +3 -4
  107. package/src/contracts/multicall.ts +12 -10
  108. package/src/contracts/rollup.ts +104 -106
  109. package/src/contracts/slasher_contract.ts +22 -0
  110. package/src/contracts/tally_slashing_proposer.ts +54 -6
  111. package/src/deploy_l1_contracts.ts +570 -328
  112. package/src/index.ts +1 -1
  113. package/src/l1_artifacts.ts +14 -6
  114. package/src/l1_contract_addresses.ts +17 -26
  115. package/src/l1_reader.ts +9 -9
  116. package/src/l1_tx_utils/README.md +177 -0
  117. package/src/l1_tx_utils/config.ts +140 -0
  118. package/src/l1_tx_utils/constants.ts +18 -0
  119. package/src/l1_tx_utils/factory.ts +64 -0
  120. package/src/l1_tx_utils/index.ts +12 -0
  121. package/src/l1_tx_utils/interfaces.ts +86 -0
  122. package/src/l1_tx_utils/l1_tx_utils.ts +718 -0
  123. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +77 -0
  124. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +372 -0
  125. package/src/l1_tx_utils/signer.ts +28 -0
  126. package/src/l1_tx_utils/types.ts +85 -0
  127. package/src/l1_tx_utils/utils.ts +16 -0
  128. package/src/publisher_manager.ts +51 -9
  129. package/src/queries.ts +13 -8
  130. package/src/test/chain_monitor.ts +89 -9
  131. package/src/test/delayed_tx_utils.ts +2 -2
  132. package/src/test/eth_cheat_codes.ts +142 -29
  133. package/src/test/rollup_cheat_codes.ts +54 -14
  134. package/src/test/upgrade_utils.ts +3 -2
  135. package/src/utils.ts +13 -185
  136. package/src/zkPassportVerifierAddress.ts +1 -1
  137. package/dest/l1_tx_utils.d.ts +0 -250
  138. package/dest/l1_tx_utils.d.ts.map +0 -1
  139. package/dest/l1_tx_utils.js +0 -826
  140. package/dest/l1_tx_utils_with_blobs.d.ts +0 -19
  141. package/dest/l1_tx_utils_with_blobs.d.ts.map +0 -1
  142. package/dest/l1_tx_utils_with_blobs.js +0 -85
  143. package/src/l1_tx_utils.ts +0 -1105
  144. package/src/l1_tx_utils_with_blobs.ts +0 -144
@@ -3,7 +3,6 @@ import { EthAddress } from '@aztec/foundation/eth-address';
3
3
  import type { ViemSignature } from '@aztec/foundation/eth-signature';
4
4
  import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
5
5
  import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
6
- import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
7
6
 
8
7
  import chunk from 'lodash.chunk';
9
8
  import {
@@ -11,8 +10,8 @@ import {
11
10
  type GetContractReturnType,
12
11
  type Hex,
13
12
  type StateOverride,
13
+ type WatchContractEventReturnType,
14
14
  encodeFunctionData,
15
- getAddress,
16
15
  getContract,
17
16
  hexToBigInt,
18
17
  keccak256,
@@ -22,7 +21,7 @@ import { getPublicClient } from '../client.js';
22
21
  import type { DeployL1ContractsReturnType } from '../deploy_l1_contracts.js';
23
22
  import type { L1ContractAddresses } from '../l1_contract_addresses.js';
24
23
  import type { L1ReaderConfig } from '../l1_reader.js';
25
- import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils.js';
24
+ import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
26
25
  import type { ViemClient } from '../types.js';
27
26
  import { formatViemError } from '../utils.js';
28
27
  import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
@@ -160,13 +159,12 @@ export class RollupContract {
160
159
  public async getSlashingProposer(): Promise<
161
160
  EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
162
161
  > {
163
- const slasherAddress = await this.rollup.read.getSlasher();
164
- if (EthAddress.fromString(slasherAddress).isZero()) {
162
+ const slasher = await this.getSlasherContract();
163
+ if (!slasher) {
165
164
  return undefined;
166
165
  }
167
166
 
168
- const slasher = getContract({ address: slasherAddress, abi: SlasherAbi, client: this.client });
169
- const proposerAddress = await slasher.read.PROPOSER();
167
+ const proposerAddress = await slasher.getProposer();
170
168
  const proposerAbi = [
171
169
  {
172
170
  type: 'function',
@@ -177,7 +175,7 @@ export class RollupContract {
177
175
  },
178
176
  ] as const;
179
177
 
180
- const proposer = getContract({ address: proposerAddress, abi: proposerAbi, client: this.client });
178
+ const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
181
179
  const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
182
180
  if (proposerType === SlashingProposerType.Tally.valueOf()) {
183
181
  return new TallySlashingProposerContract(this.client, proposerAddress);
@@ -223,6 +221,16 @@ export class RollupContract {
223
221
  return this.rollup.read.getEjectionThreshold();
224
222
  }
225
223
 
224
+ @memoize
225
+ getLocalEjectionThreshold() {
226
+ return this.rollup.read.getLocalEjectionThreshold();
227
+ }
228
+
229
+ @memoize
230
+ getLagInEpochs() {
231
+ return this.rollup.read.getLagInEpochs();
232
+ }
233
+
226
234
  @memoize
227
235
  getActivationThreshold() {
228
236
  return this.rollup.read.getActivationThreshold();
@@ -260,20 +268,51 @@ export class RollupContract {
260
268
 
261
269
  @memoize
262
270
  async getGenesisArchiveTreeRoot(): Promise<`0x${string}`> {
263
- const block = await this.rollup.read.getBlock([0n]);
264
- return block.archive;
271
+ return await this.rollup.read.archiveAt([0n]);
272
+ }
273
+
274
+ /**
275
+ * Returns rollup constants used for epoch queries.
276
+ * Return type is `L1RollupConstants` which is defined in stdlib,
277
+ * so we cant reference it until we move this contract to that package.
278
+ */
279
+ @memoize
280
+ public async getRollupConstants(): Promise<{
281
+ l1StartBlock: bigint;
282
+ l1GenesisTime: bigint;
283
+ slotDuration: number;
284
+ epochDuration: number;
285
+ proofSubmissionEpochs: number;
286
+ }> {
287
+ const [l1StartBlock, l1GenesisTime, slotDuration, epochDuration, proofSubmissionEpochs] = await Promise.all([
288
+ this.getL1StartBlock(),
289
+ this.getL1GenesisTime(),
290
+ this.getSlotDuration(),
291
+ this.getEpochDuration(),
292
+ this.getProofSubmissionEpochs(),
293
+ ]);
294
+ return {
295
+ l1StartBlock,
296
+ l1GenesisTime,
297
+ slotDuration: Number(slotDuration),
298
+ epochDuration: Number(epochDuration),
299
+ proofSubmissionEpochs: Number(proofSubmissionEpochs),
300
+ };
265
301
  }
266
302
 
267
- getSlasher() {
303
+ getSlasherAddress() {
268
304
  return this.rollup.read.getSlasher();
269
305
  }
270
306
 
271
307
  /**
272
308
  * Returns a SlasherContract instance for interacting with the slasher contract.
273
309
  */
274
- async getSlasherContract(): Promise<SlasherContract> {
275
- const slasherAddress = await this.getSlasher();
276
- return new SlasherContract(this.client, EthAddress.fromString(slasherAddress));
310
+ async getSlasherContract(): Promise<SlasherContract | undefined> {
311
+ const slasherAddress = EthAddress.fromString(await this.getSlasherAddress());
312
+ if (slasherAddress.isZero()) {
313
+ return undefined;
314
+ }
315
+ return new SlasherContract(this.client, slasherAddress);
277
316
  }
278
317
 
279
318
  getOwner() {
@@ -285,13 +324,11 @@ export class RollupContract {
285
324
  }
286
325
 
287
326
  public async getSlashingProposerAddress() {
288
- const slasherAddress = await this.getSlasher();
289
- const slasher = getContract({
290
- address: getAddress(slasherAddress.toString()),
291
- abi: SlasherAbi,
292
- client: this.client,
293
- });
294
- return EthAddress.fromString(await slasher.read.PROPOSER());
327
+ const slasher = await this.getSlasherContract();
328
+ if (!slasher) {
329
+ return EthAddress.ZERO;
330
+ }
331
+ return await slasher.getProposer();
295
332
  }
296
333
 
297
334
  getBlockReward() {
@@ -388,8 +425,8 @@ export class RollupContract {
388
425
  return result;
389
426
  }
390
427
 
391
- getBlock(blockNumber: bigint) {
392
- return this.rollup.read.getBlock([blockNumber]);
428
+ getBlock(blockNumber: bigint | number) {
429
+ return this.rollup.read.getBlock([BigInt(blockNumber)]);
393
430
  }
394
431
 
395
432
  getTips() {
@@ -404,8 +441,19 @@ export class RollupContract {
404
441
  return this.rollup.read.getEntryQueueLength();
405
442
  }
406
443
 
407
- async getEpochNumber(blockNumber?: bigint) {
408
- blockNumber ??= await this.getBlockNumber();
444
+ getAvailableValidatorFlushes() {
445
+ return this.rollup.read.getAvailableValidatorFlushes();
446
+ }
447
+
448
+ getNextFlushableEpoch() {
449
+ return this.rollup.read.getNextFlushableEpoch();
450
+ }
451
+
452
+ getCurrentEpochNumber(): Promise<bigint> {
453
+ return this.rollup.read.getCurrentEpoch();
454
+ }
455
+
456
+ getEpochNumberForBlock(blockNumber: bigint) {
409
457
  return this.rollup.read.getEpochForBlock([BigInt(blockNumber)]);
410
458
  }
411
459
 
@@ -461,6 +509,7 @@ export class RollupContract {
461
509
  ViemHeader,
462
510
  ViemCommitteeAttestations,
463
511
  `0x${string}`[],
512
+ ViemSignature,
464
513
  `0x${string}`,
465
514
  `0x${string}`,
466
515
  {
@@ -483,77 +532,6 @@ export class RollupContract {
483
532
  }
484
533
  }
485
534
 
486
- /**
487
- * Packs an array of committee attestations into the format expected by the Solidity contract
488
- *
489
- * @param attestations - Array of committee attestations with addresses and signatures
490
- * @returns Packed attestations with bitmap and tightly packed signature/address data
491
- */
492
- static packAttestations(attestations: ViemCommitteeAttestation[]): ViemCommitteeAttestations {
493
- const length = attestations.length;
494
-
495
- // Calculate bitmap size (1 bit per attestation, rounded up to nearest byte)
496
- const bitmapSize = Math.ceil(length / 8);
497
- const signatureIndices = new Uint8Array(bitmapSize);
498
-
499
- // Calculate total data size needed
500
- let totalDataSize = 0;
501
- for (let i = 0; i < length; i++) {
502
- const signature = attestations[i].signature;
503
- // Check if signature is empty (v = 0)
504
- const isEmpty = signature.v === 0;
505
-
506
- if (!isEmpty) {
507
- totalDataSize += 65; // v (1) + r (32) + s (32)
508
- } else {
509
- totalDataSize += 20; // address only
510
- }
511
- }
512
-
513
- const signaturesOrAddresses = new Uint8Array(totalDataSize);
514
- let dataIndex = 0;
515
-
516
- // Pack the data
517
- for (let i = 0; i < length; i++) {
518
- const attestation = attestations[i];
519
- const signature = attestation.signature;
520
-
521
- // Check if signature is empty
522
- const isEmpty = signature.v === 0;
523
-
524
- if (!isEmpty) {
525
- // Set bit in bitmap (bit 7-0 in each byte, left to right)
526
- const byteIndex = Math.floor(i / 8);
527
- const bitIndex = 7 - (i % 8);
528
- signatureIndices[byteIndex] |= 1 << bitIndex;
529
-
530
- // Pack signature: v + r + s
531
- signaturesOrAddresses[dataIndex] = signature.v;
532
- dataIndex++;
533
-
534
- // Pack r (32 bytes)
535
- const rBytes = Buffer.from(signature.r.slice(2), 'hex');
536
- signaturesOrAddresses.set(rBytes, dataIndex);
537
- dataIndex += 32;
538
-
539
- // Pack s (32 bytes)
540
- const sBytes = Buffer.from(signature.s.slice(2), 'hex');
541
- signaturesOrAddresses.set(sBytes, dataIndex);
542
- dataIndex += 32;
543
- } else {
544
- // Pack address only (20 bytes)
545
- const addrBytes = Buffer.from(attestation.addr.slice(2), 'hex');
546
- signaturesOrAddresses.set(addrBytes, dataIndex);
547
- dataIndex += 20;
548
- }
549
- }
550
-
551
- return {
552
- signatureIndices: `0x${Buffer.from(signatureIndices).toString('hex')}`,
553
- signaturesOrAddresses: `0x${Buffer.from(signaturesOrAddresses).toString('hex')}`,
554
- };
555
- }
556
-
557
535
  /**
558
536
  * @notice Calls `canProposeAtTime` with the time of the next Ethereum block and the sender address
559
537
  *
@@ -618,7 +596,7 @@ export class RollupContract {
618
596
  /** Creates a request to Rollup#invalidateBadAttestation to be simulated or sent */
619
597
  public buildInvalidateBadAttestationRequest(
620
598
  blockNumber: number,
621
- attestations: ViemCommitteeAttestation[],
599
+ attestationsAndSigners: ViemCommitteeAttestations,
622
600
  committee: EthAddress[],
623
601
  invalidIndex: number,
624
602
  ): L1TxRequest {
@@ -629,7 +607,7 @@ export class RollupContract {
629
607
  functionName: 'invalidateBadAttestation',
630
608
  args: [
631
609
  BigInt(blockNumber),
632
- RollupContract.packAttestations(attestations),
610
+ attestationsAndSigners,
633
611
  committee.map(addr => addr.toString()),
634
612
  BigInt(invalidIndex),
635
613
  ],
@@ -640,7 +618,7 @@ export class RollupContract {
640
618
  /** Creates a request to Rollup#invalidateInsufficientAttestations to be simulated or sent */
641
619
  public buildInvalidateInsufficientAttestationsRequest(
642
620
  blockNumber: number,
643
- attestations: ViemCommitteeAttestation[],
621
+ attestationsAndSigners: ViemCommitteeAttestations,
644
622
  committee: EthAddress[],
645
623
  ): L1TxRequest {
646
624
  return {
@@ -648,11 +626,7 @@ export class RollupContract {
648
626
  data: encodeFunctionData({
649
627
  abi: RollupAbi,
650
628
  functionName: 'invalidateInsufficientAttestations',
651
- args: [
652
- BigInt(blockNumber),
653
- RollupContract.packAttestations(attestations),
654
- committee.map(addr => addr.toString()),
655
- ],
629
+ args: [BigInt(blockNumber), attestationsAndSigners, committee.map(addr => addr.toString())],
656
630
  }),
657
631
  };
658
632
  }
@@ -742,6 +716,10 @@ export class RollupContract {
742
716
  return this.rollup.read.getStakingAsset();
743
717
  }
744
718
 
719
+ getRewardConfig() {
720
+ return this.rollup.read.getRewardConfig();
721
+ }
722
+
745
723
  setupEpoch(l1TxUtils: L1TxUtils) {
746
724
  return l1TxUtils.sendAndMonitorTransaction({
747
725
  to: this.address,
@@ -764,7 +742,9 @@ export class RollupContract {
764
742
  });
765
743
  }
766
744
 
767
- public listenToSlasherChanged(callback: (args: { oldSlasher: `0x${string}`; newSlasher: `0x${string}` }) => unknown) {
745
+ public listenToSlasherChanged(
746
+ callback: (args: { oldSlasher: `0x${string}`; newSlasher: `0x${string}` }) => unknown,
747
+ ): WatchContractEventReturnType {
768
748
  return this.rollup.watchEvent.SlasherUpdated(
769
749
  {},
770
750
  {
@@ -780,6 +760,22 @@ export class RollupContract {
780
760
  );
781
761
  }
782
762
 
763
+ public listenToBlockInvalidated(callback: (args: { blockNumber: bigint }) => unknown): WatchContractEventReturnType {
764
+ return this.rollup.watchEvent.BlockInvalidated(
765
+ {},
766
+ {
767
+ onLogs: logs => {
768
+ for (const log of logs) {
769
+ const args = log.args;
770
+ if (args.blockNumber !== undefined) {
771
+ callback({ blockNumber: args.blockNumber });
772
+ }
773
+ }
774
+ },
775
+ },
776
+ );
777
+ }
778
+
783
779
  public async getSlashEvents(l1BlockHash: Hex): Promise<{ amount: bigint; attester: EthAddress }[]> {
784
780
  const events = await this.rollup.getEvents.Slashed({}, { blockHash: l1BlockHash, strict: true });
785
781
  return events.map(event => ({
@@ -788,7 +784,9 @@ export class RollupContract {
788
784
  }));
789
785
  }
790
786
 
791
- public listenToSlash(callback: (args: { amount: bigint; attester: EthAddress }) => unknown) {
787
+ public listenToSlash(
788
+ callback: (args: { amount: bigint; attester: EthAddress }) => unknown,
789
+ ): WatchContractEventReturnType {
792
790
  return this.rollup.watchEvent.Slashed(
793
791
  {},
794
792
  {
@@ -38,6 +38,19 @@ export class SlasherContract {
38
38
  }
39
39
  }
40
40
 
41
+ /**
42
+ * Checks if slashing is currently enabled. Slashing can be disabled by the vetoer.
43
+ * @returns True if slashing is enabled, false otherwise
44
+ */
45
+ public async isSlashingEnabled(): Promise<boolean> {
46
+ try {
47
+ return await this.contract.read.isSlashingEnabled();
48
+ } catch (error) {
49
+ this.log.error(`Error checking if slashing is enabled`, error);
50
+ throw error;
51
+ }
52
+ }
53
+
41
54
  /**
42
55
  * Gets the current vetoer address.
43
56
  * @returns The vetoer address
@@ -47,6 +60,15 @@ export class SlasherContract {
47
60
  return EthAddress.fromString(vetoer);
48
61
  }
49
62
 
63
+ /**
64
+ * Gets the disable duration by the vetoer.
65
+ * @returns The disable duration in seconds
66
+ */
67
+ public async getSlashingDisableDuration(): Promise<number> {
68
+ const duration = await this.contract.read.SLASHING_DISABLE_DURATION();
69
+ return Number(duration);
70
+ }
71
+
50
72
  /**
51
73
  * Gets the current governance address.
52
74
  * @returns The governance address
@@ -2,9 +2,9 @@ import { type L1TxRequest, type ViemClient, tryExtractEvent } from '@aztec/ether
2
2
  import { Buffer32 } from '@aztec/foundation/buffer';
3
3
  import { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { Signature } from '@aztec/foundation/eth-signature';
5
+ import { hexToBuffer } from '@aztec/foundation/string';
5
6
  import { TallySlashingProposerAbi } from '@aztec/l1-artifacts/TallySlashingProposerAbi';
6
7
 
7
- import EventEmitter from 'events';
8
8
  import {
9
9
  type GetContractReturnType,
10
10
  type Hex,
@@ -18,7 +18,7 @@ import {
18
18
  * Wrapper around the TallySlashingProposer contract that provides
19
19
  * a TypeScript interface for interacting with the consensus-based slashing system.
20
20
  */
21
- export class TallySlashingProposerContract extends EventEmitter {
21
+ export class TallySlashingProposerContract {
22
22
  private readonly contract: GetContractReturnType<typeof TallySlashingProposerAbi, ViemClient>;
23
23
 
24
24
  public readonly type = 'tally' as const;
@@ -27,7 +27,6 @@ export class TallySlashingProposerContract extends EventEmitter {
27
27
  public readonly client: ViemClient,
28
28
  address: Hex | EthAddress,
29
29
  ) {
30
- super();
31
30
  this.contract = getContract({
32
31
  address: typeof address === 'string' ? address : address.toString(),
33
32
  abi: TallySlashingProposerAbi,
@@ -86,11 +85,20 @@ export class TallySlashingProposerContract extends EventEmitter {
86
85
  */
87
86
  public async getRound(round: bigint): Promise<{
88
87
  isExecuted: boolean;
89
- readyToExecute: boolean;
90
88
  voteCount: bigint;
91
89
  }> {
92
- const [isExecuted, readyToExecute, voteCount] = await this.contract.read.getRound([round]);
93
- return { isExecuted, readyToExecute, voteCount };
90
+ const [isExecuted, voteCount] = await this.contract.read.getRound([round]);
91
+ return { isExecuted, voteCount };
92
+ }
93
+
94
+ /**
95
+ * Check if a round is ready to execute at a given slot
96
+ * @param round - The round number to check
97
+ * @param slot - The slot number to check at
98
+ * @returns Whether the round is ready to execute
99
+ */
100
+ public async isRoundReadyToExecute(round: bigint, slot: bigint): Promise<boolean> {
101
+ return await this.contract.read.isRoundReadyToExecute([round, slot]);
94
102
  }
95
103
 
96
104
  /** Returns the slash actions and payload address for a given round (zero if no slash actions) */
@@ -221,6 +229,21 @@ export class TallySlashingProposerContract extends EventEmitter {
221
229
  };
222
230
  }
223
231
 
232
+ /** Returns the last vote emitted for a given round */
233
+ public async getLastVote(round: bigint) {
234
+ const { voteCount } = await this.getRound(round);
235
+ const validators = (await this.contract.simulate.getSlashTargetCommittees([round])).result.flat();
236
+ const vote = await this.contract.read.getVotes([round, voteCount - 1n]);
237
+ const decoded = decodeSlashConsensusVotes(hexToBuffer(vote));
238
+ const slashAmounts = await this.getSlashingAmounts();
239
+ return decoded
240
+ .map((units, i) => ({
241
+ validator: EthAddress.fromString(validators[i]),
242
+ slashAmount: slashAmounts[units - 1] ?? 0n,
243
+ }))
244
+ .filter(v => v.slashAmount > 0n);
245
+ }
246
+
224
247
  /**
225
248
  * Listen for VoteCast events
226
249
  * @param callback - Callback function to handle vote cast events
@@ -265,3 +288,28 @@ export class TallySlashingProposerContract extends EventEmitter {
265
288
  );
266
289
  }
267
290
  }
291
+
292
+ /**
293
+ * Decodes a Buffer containing slash votes back into an array of numbers.
294
+ * Each vote is represented as a 2-bit value (0, 1, 2, or 3) representing slashing units.
295
+ * @dev This should live in stdlib next to encodeSlashConsensusVotes but is here since we
296
+ * do not have a dependency to stdlib from the ethereum package. We need a larger refactor to fix this.
297
+ * @param buffer - The Buffer containing encoded slash votes
298
+ * @returns An array of numbers representing the slash votes
299
+ */
300
+ export function decodeSlashConsensusVotes(buffer: Buffer): number[] {
301
+ const votes: number[] = [];
302
+ for (let i = 0; i < buffer.length; i++) {
303
+ const voteByte = buffer.readUInt8(i);
304
+ // Decode votes from Solidity's bit order (LSB to MSB)
305
+ // Bits 0-1: validator at index i*4
306
+ // Bits 2-3: validator at index i*4+1
307
+ // Bits 4-5: validator at index i*4+2
308
+ // Bits 6-7: validator at index i*4+3
309
+ votes.push((voteByte >> 0) & 0x03);
310
+ votes.push((voteByte >> 2) & 0x03);
311
+ votes.push((voteByte >> 4) & 0x03);
312
+ votes.push((voteByte >> 6) & 0x03);
313
+ }
314
+ return votes;
315
+ }