@aztec/ethereum 0.0.1-commit.4d3c002 → 0.0.1-commit.4d9804df
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.
- package/dest/client.d.ts +17 -3
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +49 -3
- package/dest/config.d.ts +27 -8
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +59 -12
- package/dest/contracts/chain_state_override.d.ts +78 -0
- package/dest/contracts/chain_state_override.d.ts.map +1 -0
- package/dest/contracts/chain_state_override.js +137 -0
- package/dest/contracts/empire_base.d.ts +1 -3
- package/dest/contracts/empire_base.d.ts.map +1 -1
- package/dest/contracts/fee_asset_price_oracle.d.ts +6 -2
- package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -1
- package/dest/contracts/fee_asset_price_oracle.js +11 -6
- package/dest/contracts/governance.d.ts +108 -33
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +193 -12
- package/dest/contracts/governance_proposer.d.ts +33 -3
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +51 -9
- package/dest/contracts/gse.d.ts +4 -2
- package/dest/contracts/gse.d.ts.map +1 -1
- package/dest/contracts/gse.js +2 -2
- package/dest/contracts/inbox.d.ts +3 -3
- package/dest/contracts/inbox.d.ts.map +1 -1
- package/dest/contracts/inbox.js +9 -3
- package/dest/contracts/index.d.ts +3 -3
- package/dest/contracts/index.d.ts.map +1 -1
- package/dest/contracts/index.js +2 -2
- package/dest/contracts/multicall.d.ts +82 -14
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +119 -95
- package/dest/contracts/outbox.d.ts +27 -4
- package/dest/contracts/outbox.d.ts.map +1 -1
- package/dest/contracts/outbox.js +34 -7
- package/dest/contracts/rollup.d.ts +5383 -31
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +223 -90
- package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +3 -4
- package/dest/contracts/slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +13 -15
- package/dest/deploy_aztec_l1_contracts.d.ts +18 -10
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +28 -17
- package/dest/foundry_binary.d.ts +13 -0
- package/dest/foundry_binary.d.ts.map +1 -0
- package/dest/foundry_binary.js +52 -0
- package/dest/l1_artifacts.d.ts +14941 -16273
- package/dest/l1_artifacts.d.ts.map +1 -1
- package/dest/l1_artifacts.js +9 -24
- package/dest/l1_contract_addresses.d.ts +61 -65
- package/dest/l1_contract_addresses.d.ts.map +1 -1
- package/dest/l1_contract_addresses.js +101 -82
- package/dest/l1_reader.d.ts +3 -5
- package/dest/l1_reader.d.ts.map +1 -1
- package/dest/l1_reader.js +3 -6
- package/dest/l1_tx_utils/config.js +3 -3
- package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +23 -4
- package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_fee_analyzer.js +65 -3
- package/dest/l1_tx_utils/l1_tx_utils.d.ts +7 -1
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +105 -32
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/readonly_l1_tx_utils.js +2 -1
- package/dest/l1_tx_utils/tx_delayer.d.ts +5 -3
- package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -1
- package/dest/l1_tx_utils/tx_delayer.js +7 -5
- package/dest/l1_tx_utils/types.d.ts +27 -1
- package/dest/l1_tx_utils/types.d.ts.map +1 -1
- package/dest/l1_tx_utils/types.js +10 -0
- package/dest/publisher_manager.d.ts +18 -6
- package/dest/publisher_manager.d.ts.map +1 -1
- package/dest/publisher_manager.js +44 -7
- package/dest/queries.d.ts +13 -2
- package/dest/queries.d.ts.map +1 -1
- package/dest/queries.js +69 -4
- package/dest/test/blob_kzg_warmup.d.ts +19 -0
- package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
- package/dest/test/blob_kzg_warmup.js +64 -0
- package/dest/test/chain_monitor.d.ts +34 -2
- package/dest/test/chain_monitor.d.ts.map +1 -1
- package/dest/test/chain_monitor.js +76 -6
- package/dest/test/eth_cheat_codes.d.ts +32 -3
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +83 -37
- package/dest/test/index.d.ts +2 -1
- package/dest/test/index.d.ts.map +1 -1
- package/dest/test/index.js +1 -0
- package/dest/test/rollup_cheat_codes.d.ts +60 -3
- package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
- package/dest/test/rollup_cheat_codes.js +86 -7
- package/dest/test/start_anvil.d.ts +1 -1
- package/dest/test/start_anvil.d.ts.map +1 -1
- package/dest/test/start_anvil.js +41 -5
- package/dest/utils.d.ts +9 -2
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +34 -6
- package/package.json +6 -7
- package/src/client.ts +57 -1
- package/src/config.ts +82 -16
- package/src/contracts/chain_state_override.ts +200 -0
- package/src/contracts/empire_base.ts +0 -2
- package/src/contracts/fee_asset_price_oracle.ts +10 -5
- package/src/contracts/governance.ts +266 -9
- package/src/contracts/governance_proposer.ts +52 -5
- package/src/contracts/gse.ts +7 -2
- package/src/contracts/inbox.ts +12 -4
- package/src/contracts/index.ts +2 -2
- package/src/contracts/multicall.ts +183 -106
- package/src/contracts/outbox.ts +42 -8
- package/src/contracts/rollup.ts +252 -96
- package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +14 -16
- package/src/deploy_aztec_l1_contracts.ts +32 -18
- package/src/foundry_binary.ts +57 -0
- package/src/l1_artifacts.ts +12 -35
- package/src/l1_contract_addresses.ts +120 -94
- package/src/l1_reader.ts +4 -9
- package/src/l1_tx_utils/config.ts +3 -3
- package/src/l1_tx_utils/l1_fee_analyzer.ts +75 -3
- package/src/l1_tx_utils/l1_tx_utils.ts +98 -34
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +2 -1
- package/src/l1_tx_utils/tx_delayer.ts +16 -7
- package/src/l1_tx_utils/types.ts +32 -0
- package/src/publisher_manager.ts +54 -18
- package/src/queries.ts +76 -4
- package/src/test/blob_kzg_warmup.ts +65 -0
- package/src/test/chain_monitor.ts +93 -5
- package/src/test/eth_cheat_codes.ts +76 -41
- package/src/test/index.ts +1 -0
- package/src/test/rollup_cheat_codes.ts +115 -6
- package/src/test/start_anvil.ts +38 -6
- package/src/utils.ts +30 -6
- package/dest/contracts/empire_slashing_proposer.d.ts +0 -69
- package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/empire_slashing_proposer.js +0 -216
- package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
- package/dest/generated/l1-contracts-defaults.d.ts +0 -30
- package/dest/generated/l1-contracts-defaults.d.ts.map +0 -1
- package/dest/generated/l1-contracts-defaults.js +0 -30
- package/src/contracts/empire_slashing_proposer.ts +0 -265
- package/src/generated/l1-contracts-defaults.ts +0 -32
package/src/contracts/rollup.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
6
6
|
import type { ViemSignature } from '@aztec/foundation/eth-signature';
|
|
7
7
|
import { createLogger } from '@aztec/foundation/log';
|
|
8
8
|
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
9
|
+
import { getErrorCause } from '@aztec/foundation/types';
|
|
9
10
|
import { EscapeHatchAbi } from '@aztec/l1-artifacts/EscapeHatchAbi';
|
|
10
11
|
import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
|
|
11
12
|
import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
|
|
@@ -13,10 +14,14 @@ import { RollupStorage } from '@aztec/l1-artifacts/RollupStorage';
|
|
|
13
14
|
import chunk from 'lodash.chunk';
|
|
14
15
|
import {
|
|
15
16
|
type Account,
|
|
17
|
+
ContractFunctionRevertedError,
|
|
16
18
|
type GetContractReturnType,
|
|
17
19
|
type Hex,
|
|
20
|
+
type Log,
|
|
21
|
+
RpcRequestError,
|
|
18
22
|
type StateOverride,
|
|
19
23
|
type WatchContractEventReturnType,
|
|
24
|
+
decodeErrorResult,
|
|
20
25
|
encodeAbiParameters,
|
|
21
26
|
encodeFunctionData,
|
|
22
27
|
getContract,
|
|
@@ -31,11 +36,10 @@ import type { L1ReaderConfig } from '../l1_reader.js';
|
|
|
31
36
|
import type { L1TxRequest, L1TxUtils } from '../l1_tx_utils/index.js';
|
|
32
37
|
import type { ViemClient } from '../types.js';
|
|
33
38
|
import { formatViemError } from '../utils.js';
|
|
34
|
-
import { EmpireSlashingProposerContract } from './empire_slashing_proposer.js';
|
|
35
39
|
import { GSEContract } from './gse.js';
|
|
36
40
|
import type { L1EventLog } from './log.js';
|
|
37
41
|
import { SlasherContract } from './slasher_contract.js';
|
|
38
|
-
import {
|
|
42
|
+
import { SlashingProposerContract } from './slashing_proposer.js';
|
|
39
43
|
import { checkBlockTag } from './utils.js';
|
|
40
44
|
|
|
41
45
|
export type ViemCommitteeAttestation = {
|
|
@@ -57,7 +61,6 @@ export type L1RollupContractAddresses = Pick<
|
|
|
57
61
|
| 'feeJuiceAddress'
|
|
58
62
|
| 'stakingAssetAddress'
|
|
59
63
|
| 'rewardDistributorAddress'
|
|
60
|
-
| 'slashFactoryAddress'
|
|
61
64
|
| 'gseAddress'
|
|
62
65
|
>;
|
|
63
66
|
|
|
@@ -80,6 +83,7 @@ export type ViemHeader = {
|
|
|
80
83
|
feeRecipient: `0x${string}`;
|
|
81
84
|
gasFees: ViemGasFees;
|
|
82
85
|
totalManaUsed: bigint;
|
|
86
|
+
accumulatedFees: bigint;
|
|
83
87
|
};
|
|
84
88
|
|
|
85
89
|
export type ViemGasFees = {
|
|
@@ -87,12 +91,6 @@ export type ViemGasFees = {
|
|
|
87
91
|
feePerL2Gas: bigint;
|
|
88
92
|
};
|
|
89
93
|
|
|
90
|
-
export enum SlashingProposerType {
|
|
91
|
-
None = 0,
|
|
92
|
-
Tally = 1,
|
|
93
|
-
Empire = 2,
|
|
94
|
-
}
|
|
95
|
-
|
|
96
94
|
/**
|
|
97
95
|
* Status of a validator/attester in the staking system.
|
|
98
96
|
* Matches the Status enum in StakingLib.sol
|
|
@@ -136,6 +134,31 @@ export type L1FeeData = {
|
|
|
136
134
|
blobFee: bigint;
|
|
137
135
|
};
|
|
138
136
|
|
|
137
|
+
/** Field offsets within the CompressedTempCheckpointLog struct in Solidity storage. */
|
|
138
|
+
export enum TempCheckpointLogField {
|
|
139
|
+
HeaderHash = 0,
|
|
140
|
+
BlobCommitmentsHash = 1,
|
|
141
|
+
OutHash = 2,
|
|
142
|
+
AttestationsHash = 3,
|
|
143
|
+
PayloadDigest = 4,
|
|
144
|
+
SlotNumber = 5,
|
|
145
|
+
FeeHeader = 6,
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Field-level override input for `tempCheckpointLogs[checkpointNumber]`. Covers the fields the
|
|
150
|
+
* `propose()` path actually reads back. `payloadDigest` is `Buffer32` because it carries an
|
|
151
|
+
* arbitrary `bytes32` value rather than a BN254 scalar. `slotNumber` carries the uint32 portion
|
|
152
|
+
* of the on-chain `CompressedSlot`.
|
|
153
|
+
*/
|
|
154
|
+
export type TempCheckpointLogOverrideFields = {
|
|
155
|
+
headerHash?: Fr;
|
|
156
|
+
outHash?: Fr;
|
|
157
|
+
payloadDigest?: Buffer32;
|
|
158
|
+
slotNumber?: SlotNumber;
|
|
159
|
+
feeHeader?: FeeHeader;
|
|
160
|
+
};
|
|
161
|
+
|
|
139
162
|
/** Components of the minimum fee per mana, as returned by the L1 rollup contract. */
|
|
140
163
|
export type ManaMinFeeComponents = {
|
|
141
164
|
sequencerCost: bigint;
|
|
@@ -212,6 +235,32 @@ export type CheckpointProposedArgs = {
|
|
|
212
235
|
/** Log type for CheckpointProposed events. */
|
|
213
236
|
export type CheckpointProposedLog = L1EventLog<CheckpointProposedArgs>;
|
|
214
237
|
|
|
238
|
+
const INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR = 'ValidatorSelection__InsufficientValidatorSetSize';
|
|
239
|
+
|
|
240
|
+
function isValidatorSelectionError(err: unknown, errorName: string): boolean {
|
|
241
|
+
return (
|
|
242
|
+
getErrorCause(err, ContractFunctionRevertedError)?.data?.errorName === errorName ||
|
|
243
|
+
decodeRpcRequestErrorName(err) === errorName
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function decodeRpcRequestErrorName(err: unknown): string | undefined {
|
|
248
|
+
const data = getErrorCause(err, RpcRequestError)?.data;
|
|
249
|
+
if (!isHexString(data)) {
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
return decodeErrorResult({ abi: RollupAbi, data }).errorName;
|
|
255
|
+
} catch {
|
|
256
|
+
return undefined;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function isHexString(value: unknown): value is Hex {
|
|
261
|
+
return typeof value === 'string' && value.startsWith('0x');
|
|
262
|
+
}
|
|
263
|
+
|
|
215
264
|
export class RollupContract {
|
|
216
265
|
private readonly rollup: GetContractReturnType<typeof RollupAbi, ViemClient>;
|
|
217
266
|
private readonly logger = createLogger('ethereum:rollup');
|
|
@@ -244,7 +293,7 @@ export class RollupContract {
|
|
|
244
293
|
|
|
245
294
|
static getFromConfig(config: L1ReaderConfig) {
|
|
246
295
|
const client = getPublicClient(config);
|
|
247
|
-
const address = config.
|
|
296
|
+
const address = config.rollupAddress.toString();
|
|
248
297
|
return new RollupContract(client, address);
|
|
249
298
|
}
|
|
250
299
|
|
|
@@ -270,34 +319,18 @@ export class RollupContract {
|
|
|
270
319
|
return this.rollup;
|
|
271
320
|
}
|
|
272
321
|
|
|
273
|
-
public async getSlashingProposer(): Promise<
|
|
274
|
-
EmpireSlashingProposerContract | TallySlashingProposerContract | undefined
|
|
275
|
-
> {
|
|
322
|
+
public async getSlashingProposer(): Promise<SlashingProposerContract | undefined> {
|
|
276
323
|
const slasher = await this.getSlasherContract();
|
|
277
324
|
if (!slasher) {
|
|
278
325
|
return undefined;
|
|
279
326
|
}
|
|
280
327
|
|
|
281
328
|
const proposerAddress = await slasher.getProposer();
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
type: 'function',
|
|
285
|
-
name: 'SLASHING_PROPOSER_TYPE',
|
|
286
|
-
inputs: [],
|
|
287
|
-
outputs: [{ name: '', type: 'uint8', internalType: 'enum SlasherFlavor' }],
|
|
288
|
-
stateMutability: 'view',
|
|
289
|
-
},
|
|
290
|
-
] as const;
|
|
291
|
-
|
|
292
|
-
const proposer = getContract({ address: proposerAddress.toString(), abi: proposerAbi, client: this.client });
|
|
293
|
-
const proposerType = await proposer.read.SLASHING_PROPOSER_TYPE();
|
|
294
|
-
if (proposerType === SlashingProposerType.Tally.valueOf()) {
|
|
295
|
-
return new TallySlashingProposerContract(this.client, proposerAddress);
|
|
296
|
-
} else if (proposerType === SlashingProposerType.Empire.valueOf()) {
|
|
297
|
-
return new EmpireSlashingProposerContract(this.client, proposerAddress);
|
|
298
|
-
} else {
|
|
299
|
-
throw new Error(`Unknown slashing proposer type: ${proposerType}`);
|
|
329
|
+
if (proposerAddress.isZero()) {
|
|
330
|
+
return undefined;
|
|
300
331
|
}
|
|
332
|
+
|
|
333
|
+
return new SlashingProposerContract(this.client, proposerAddress);
|
|
301
334
|
}
|
|
302
335
|
|
|
303
336
|
@memoize
|
|
@@ -522,8 +555,9 @@ export class RollupContract {
|
|
|
522
555
|
return EthAddress.fromString(await this.rollup.read.owner());
|
|
523
556
|
}
|
|
524
557
|
|
|
525
|
-
async getActiveAttesterCount(): Promise<number> {
|
|
526
|
-
|
|
558
|
+
async getActiveAttesterCount(options?: { blockNumber?: bigint }): Promise<number> {
|
|
559
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
560
|
+
return Number(await this.rollup.read.getActiveAttesterCount(options));
|
|
527
561
|
}
|
|
528
562
|
|
|
529
563
|
public async getSlashingProposerAddress() {
|
|
@@ -538,8 +572,9 @@ export class RollupContract {
|
|
|
538
572
|
return this.rollup.read.getCheckpointReward();
|
|
539
573
|
}
|
|
540
574
|
|
|
541
|
-
async getCheckpointNumber(): Promise<CheckpointNumber> {
|
|
542
|
-
|
|
575
|
+
async getCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
576
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
577
|
+
return CheckpointNumber.fromBigInt(await this.rollup.read.getPendingCheckpointNumber(options));
|
|
543
578
|
}
|
|
544
579
|
|
|
545
580
|
async getProvenCheckpointNumber(options?: { blockNumber?: bigint }): Promise<CheckpointNumber> {
|
|
@@ -547,18 +582,31 @@ export class RollupContract {
|
|
|
547
582
|
return CheckpointNumber.fromBigInt(await this.rollup.read.getProvenCheckpointNumber(options));
|
|
548
583
|
}
|
|
549
584
|
|
|
550
|
-
async getSlotNumber(): Promise<SlotNumber> {
|
|
551
|
-
|
|
585
|
+
async getSlotNumber(options?: { blockNumber?: bigint }): Promise<SlotNumber> {
|
|
586
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
587
|
+
return SlotNumber.fromBigInt(await this.rollup.read.getCurrentSlot(options));
|
|
552
588
|
}
|
|
553
589
|
|
|
554
|
-
async getL1FeesAt(timestamp: bigint): Promise<L1FeeData> {
|
|
555
|
-
|
|
590
|
+
async getL1FeesAt(timestamp: bigint, options?: { blockNumber?: bigint }): Promise<L1FeeData> {
|
|
591
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
592
|
+
const result = await this.rollup.read.getL1FeesAt([timestamp], options);
|
|
556
593
|
return {
|
|
557
594
|
baseFee: result.baseFee,
|
|
558
595
|
blobFee: result.blobFee,
|
|
559
596
|
};
|
|
560
597
|
}
|
|
561
598
|
|
|
599
|
+
async getFeeHeader(checkpointNumber: bigint): Promise<FeeHeader> {
|
|
600
|
+
const result = await this.rollup.read.getFeeHeader([checkpointNumber]);
|
|
601
|
+
return {
|
|
602
|
+
excessMana: result.excessMana,
|
|
603
|
+
manaUsed: result.manaUsed,
|
|
604
|
+
ethPerFeeAsset: result.ethPerFeeAsset,
|
|
605
|
+
congestionCost: result.congestionCost,
|
|
606
|
+
proverCost: result.proverCost,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
562
610
|
getEthPerFeeAsset(): Promise<bigint> {
|
|
563
611
|
return this.rollup.read.getEthPerFeeAsset();
|
|
564
612
|
}
|
|
@@ -572,7 +620,7 @@ export class RollupContract {
|
|
|
572
620
|
args: [timestamp],
|
|
573
621
|
})
|
|
574
622
|
.catch(e => {
|
|
575
|
-
if (e
|
|
623
|
+
if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
|
|
576
624
|
return { result: undefined };
|
|
577
625
|
}
|
|
578
626
|
throw e;
|
|
@@ -602,7 +650,7 @@ export class RollupContract {
|
|
|
602
650
|
args: [],
|
|
603
651
|
})
|
|
604
652
|
.catch(e => {
|
|
605
|
-
if (e
|
|
653
|
+
if (isValidatorSelectionError(e, INSUFFICIENT_VALIDATOR_SET_SIZE_ERROR)) {
|
|
606
654
|
return { result: undefined };
|
|
607
655
|
}
|
|
608
656
|
throw e;
|
|
@@ -633,8 +681,9 @@ export class RollupContract {
|
|
|
633
681
|
return EthAddress.fromString(result);
|
|
634
682
|
}
|
|
635
683
|
|
|
636
|
-
async getCheckpoint(checkpointNumber: CheckpointNumber): Promise<CheckpointLog> {
|
|
637
|
-
|
|
684
|
+
async getCheckpoint(checkpointNumber: CheckpointNumber, options?: { blockNumber?: bigint }): Promise<CheckpointLog> {
|
|
685
|
+
await checkBlockTag(options?.blockNumber, this.client);
|
|
686
|
+
const result = await this.rollup.read.getCheckpoint([BigInt(checkpointNumber)], options);
|
|
638
687
|
return {
|
|
639
688
|
archive: Fr.fromString(result.archive),
|
|
640
689
|
headerHash: Buffer32.fromString(result.headerHash),
|
|
@@ -667,6 +716,30 @@ export class RollupContract {
|
|
|
667
716
|
);
|
|
668
717
|
}
|
|
669
718
|
|
|
719
|
+
/**
|
|
720
|
+
* Returns the effective pending checkpoint, accounting for potential prunes.
|
|
721
|
+
* When a prune can happen, the L1 contract uses the proven checkpoint instead of the pending one.
|
|
722
|
+
* This mirrors the behavior of getEffectivePendingCheckpointNumber in STFLib.sol.
|
|
723
|
+
* @param atTimestamp - The timestamp to evaluate pruneability at. Defaults to the current L1 block timestamp.
|
|
724
|
+
* @param options - Optional L1 block number to pin the queries to.
|
|
725
|
+
*/
|
|
726
|
+
getEffectivePendingCheckpoint(atTimestamp?: bigint, options?: { blockNumber?: bigint }) {
|
|
727
|
+
return retry(
|
|
728
|
+
async () => {
|
|
729
|
+
const timestamp = atTimestamp ?? (await this.client.getBlock()).timestamp;
|
|
730
|
+
const canPrune = await this.canPruneAtTime(timestamp, options);
|
|
731
|
+
if (canPrune) {
|
|
732
|
+
const provenCheckpointNumber = await this.getProvenCheckpointNumber(options);
|
|
733
|
+
return await this.getCheckpoint(provenCheckpointNumber, options);
|
|
734
|
+
}
|
|
735
|
+
const pendingCheckpointNumber = await this.getCheckpointNumber(options);
|
|
736
|
+
return await this.getCheckpoint(pendingCheckpointNumber, options);
|
|
737
|
+
},
|
|
738
|
+
'getting effective pending checkpoint',
|
|
739
|
+
makeBackoff([0.5, 0.5, 0.5]),
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
|
|
670
743
|
async getTips(): Promise<{ pending: CheckpointNumber; proven: CheckpointNumber }> {
|
|
671
744
|
const { pending, proven } = await this.rollup.read.getTips();
|
|
672
745
|
return {
|
|
@@ -741,7 +814,7 @@ export class RollupContract {
|
|
|
741
814
|
}
|
|
742
815
|
|
|
743
816
|
async getEpochProofPublicInputs(
|
|
744
|
-
args: readonly [bigint, bigint, EpochProofPublicInputArgs, readonly
|
|
817
|
+
args: readonly [bigint, bigint, EpochProofPublicInputArgs, readonly ViemHeader[], `0x${string}`],
|
|
745
818
|
): Promise<Fr[]> {
|
|
746
819
|
const result = await this.rollup.read.getEpochProofPublicInputs(args);
|
|
747
820
|
return result.map(Fr.fromString);
|
|
@@ -789,19 +862,11 @@ export class RollupContract {
|
|
|
789
862
|
archive: Buffer,
|
|
790
863
|
account: `0x${string}` | Account,
|
|
791
864
|
timestamp: bigint,
|
|
792
|
-
|
|
793
|
-
forcePendingCheckpointNumber?: CheckpointNumber;
|
|
794
|
-
forceArchive?: { checkpointNumber: CheckpointNumber; archive: Fr };
|
|
795
|
-
} = {},
|
|
865
|
+
stateOverride: StateOverride = [],
|
|
796
866
|
): Promise<{ slot: SlotNumber; checkpointNumber: CheckpointNumber; timeOfNextL1Slot: bigint }> {
|
|
797
867
|
const timeOfNextL1Slot = timestamp;
|
|
798
868
|
const who = typeof account === 'string' ? account : account.address;
|
|
799
869
|
|
|
800
|
-
const stateOverride = RollupContract.mergeStateOverrides(
|
|
801
|
-
await this.makePendingCheckpointNumberOverride(opts.forcePendingCheckpointNumber),
|
|
802
|
-
opts.forceArchive ? this.makeArchiveOverride(opts.forceArchive.checkpointNumber, opts.forceArchive.archive) : [],
|
|
803
|
-
);
|
|
804
|
-
|
|
805
870
|
try {
|
|
806
871
|
const {
|
|
807
872
|
result: [slot, checkpointNumber],
|
|
@@ -825,20 +890,32 @@ export class RollupContract {
|
|
|
825
890
|
}
|
|
826
891
|
|
|
827
892
|
/**
|
|
828
|
-
* Returns a state override that sets the pending
|
|
829
|
-
*
|
|
830
|
-
*
|
|
893
|
+
* Returns a state override that sets the pending and/or proven checkpoint numbers. Useful for simulations.
|
|
894
|
+
* Both values share a single storage slot (pending in the upper 128 bits, proven in the lower 128 bits), so
|
|
895
|
+
* a single combined override is emitted to avoid the second state-diff clobbering the first. The current live
|
|
896
|
+
* value is read once to preserve any half not being overridden. Returns an empty override if neither is set.
|
|
897
|
+
*
|
|
898
|
+
* Throws if the resulting `proven > pending`, which would crash the simulation: `STFLib.canPruneAtTime` calls
|
|
899
|
+
* `getEpochForCheckpoint(proven + 1)` whenever `pending != proven`, and that asserts `_n <= tips.pending`.
|
|
831
900
|
*/
|
|
832
|
-
public async
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
901
|
+
public async makeChainTipsOverride(override: {
|
|
902
|
+
pending?: CheckpointNumber;
|
|
903
|
+
proven?: CheckpointNumber;
|
|
904
|
+
}): Promise<StateOverride> {
|
|
905
|
+
if (override.pending === undefined && override.proven === undefined) {
|
|
836
906
|
return [];
|
|
837
907
|
}
|
|
838
908
|
const slot = RollupContract.stfStorageSlot;
|
|
839
909
|
const currentValue = await this.client.getStorageAt({ address: this.address, slot });
|
|
840
|
-
const
|
|
841
|
-
const
|
|
910
|
+
const currentRaw = currentValue ? hexToBigInt(currentValue) : 0n;
|
|
911
|
+
const currentPending = currentRaw >> 128n;
|
|
912
|
+
const currentProven = currentRaw & ((1n << 128n) - 1n);
|
|
913
|
+
const newPending = override.pending !== undefined ? BigInt(override.pending) : currentPending;
|
|
914
|
+
const newProven = override.proven !== undefined ? BigInt(override.proven) : currentProven;
|
|
915
|
+
if (newProven > newPending) {
|
|
916
|
+
throw new Error(`Invalid chain tips override: proven (${newProven}) > pending (${newPending})`);
|
|
917
|
+
}
|
|
918
|
+
const newValue = (newPending << 128n) | newProven;
|
|
842
919
|
return [
|
|
843
920
|
{
|
|
844
921
|
address: this.address,
|
|
@@ -848,39 +925,56 @@ export class RollupContract {
|
|
|
848
925
|
}
|
|
849
926
|
|
|
850
927
|
/**
|
|
851
|
-
* Returns a state override that
|
|
852
|
-
*
|
|
928
|
+
* Returns a state override that patches `tempCheckpointLogs[checkpointNumber]` with every field that
|
|
929
|
+
* the L1 contract reads during `propose()` for the checkpoint that builds on top of it (proposer
|
|
930
|
+
* pipelining simulation). Mirrors the writes done by `ProposeLib.addTempCheckpointLog`.
|
|
931
|
+
*
|
|
932
|
+
* `blobCommitmentsHash` and `attestationsHash` are intentionally not exposed here — the propose path
|
|
933
|
+
* never asserts against them, so leaving them at storage zero is harmless.
|
|
853
934
|
*/
|
|
854
|
-
public async
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
935
|
+
public async makeTempCheckpointLogOverride(
|
|
936
|
+
checkpointNumber: CheckpointNumber,
|
|
937
|
+
fields: TempCheckpointLogOverrideFields,
|
|
938
|
+
): Promise<StateOverride> {
|
|
939
|
+
const constants = await this.getRollupConstants();
|
|
940
|
+
const slotAt = (field: TempCheckpointLogField) =>
|
|
941
|
+
`0x${this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, constants).toString(16).padStart(64, '0')}` as const;
|
|
942
|
+
const word = (v: bigint) => `0x${v.toString(16).padStart(64, '0')}` as const;
|
|
861
943
|
|
|
862
|
-
|
|
863
|
-
const structBaseSlot = hexToBigInt(
|
|
864
|
-
keccak256(
|
|
865
|
-
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsMappingBase]),
|
|
866
|
-
),
|
|
867
|
-
);
|
|
944
|
+
const stateDiff: { slot: `0x${string}`; value: `0x${string}` }[] = [];
|
|
868
945
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
946
|
+
if (fields.headerHash) {
|
|
947
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.HeaderHash), value: fields.headerHash.toString() });
|
|
948
|
+
}
|
|
949
|
+
if (fields.outHash) {
|
|
950
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.OutHash), value: fields.outHash.toString() });
|
|
951
|
+
}
|
|
952
|
+
if (fields.payloadDigest) {
|
|
953
|
+
stateDiff.push({
|
|
954
|
+
slot: slotAt(TempCheckpointLogField.PayloadDigest),
|
|
955
|
+
value: fields.payloadDigest.toString() as `0x${string}`,
|
|
956
|
+
});
|
|
957
|
+
}
|
|
958
|
+
if (fields.slotNumber !== undefined) {
|
|
959
|
+
// CompressedSlot is uint32 on L1 (SafeCast.toUint32 reverts on overflow). Match that behavior here
|
|
960
|
+
// so a malformed override surfaces immediately rather than silently truncating into a wrong slot.
|
|
961
|
+
const slotNumber = BigInt(fields.slotNumber);
|
|
962
|
+
if (slotNumber < 0n || slotNumber > 0xffffffffn) {
|
|
963
|
+
throw new Error(`slotNumber ${slotNumber} does not fit in uint32`);
|
|
964
|
+
}
|
|
965
|
+
stateDiff.push({ slot: slotAt(TempCheckpointLogField.SlotNumber), value: word(slotNumber) });
|
|
966
|
+
}
|
|
967
|
+
if (fields.feeHeader) {
|
|
968
|
+
stateDiff.push({
|
|
969
|
+
slot: slotAt(TempCheckpointLogField.FeeHeader),
|
|
970
|
+
value: word(RollupContract.compressFeeHeader(fields.feeHeader)),
|
|
971
|
+
});
|
|
972
|
+
}
|
|
872
973
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
{
|
|
878
|
-
slot: `0x${feeHeaderSlot.toString(16).padStart(64, '0')}`,
|
|
879
|
-
value: `0x${compressed.toString(16).padStart(64, '0')}`,
|
|
880
|
-
},
|
|
881
|
-
],
|
|
882
|
-
},
|
|
883
|
-
];
|
|
974
|
+
if (stateDiff.length === 0) {
|
|
975
|
+
return [];
|
|
976
|
+
}
|
|
977
|
+
return [{ address: this.address, stateDiff }];
|
|
884
978
|
}
|
|
885
979
|
|
|
886
980
|
/**
|
|
@@ -1098,13 +1192,20 @@ export class RollupContract {
|
|
|
1098
1192
|
}
|
|
1099
1193
|
|
|
1100
1194
|
async getAttesters(timestamp?: bigint): Promise<EthAddress[]> {
|
|
1101
|
-
|
|
1195
|
+
// Pin every read to a single L1 block so the attester count and the chunked index reads
|
|
1196
|
+
// observe a consistent set. Without this, the count and each chunk default to `latest` and
|
|
1197
|
+
// can straddle a block boundary (or reorg), yielding an inconsistent or truncated set.
|
|
1198
|
+
const block = await this.client.getBlock();
|
|
1199
|
+
const blockNumber = block.number ?? undefined;
|
|
1200
|
+
const ts = timestamp ?? block.timestamp;
|
|
1201
|
+
const attesterSize = await this.getActiveAttesterCount({ blockNumber });
|
|
1102
1202
|
const gse = new GSEContract(this.client, await this.getGSE());
|
|
1103
|
-
const ts = timestamp ?? (await this.client.getBlock()).timestamp;
|
|
1104
1203
|
const indices = Array.from({ length: attesterSize }, (_, i) => BigInt(i));
|
|
1105
1204
|
const chunks = chunk(indices, 1000);
|
|
1106
1205
|
|
|
1107
|
-
const results = await Promise.all(
|
|
1206
|
+
const results = await Promise.all(
|
|
1207
|
+
chunks.map(chunk => gse.getAttestersFromIndicesAtTime(this.address, ts, chunk, { blockNumber })),
|
|
1208
|
+
);
|
|
1108
1209
|
return results.flat().map(addr => EthAddress.fromString(addr));
|
|
1109
1210
|
}
|
|
1110
1211
|
|
|
@@ -1206,7 +1307,7 @@ export class RollupContract {
|
|
|
1206
1307
|
}
|
|
1207
1308
|
|
|
1208
1309
|
public listenToCheckpointInvalidated(
|
|
1209
|
-
callback: (args: { checkpointNumber: CheckpointNumber }) => unknown,
|
|
1310
|
+
callback: (args: { checkpointNumber: CheckpointNumber; event: Log }) => unknown,
|
|
1210
1311
|
): WatchContractEventReturnType {
|
|
1211
1312
|
return this.rollup.watchEvent.CheckpointInvalidated(
|
|
1212
1313
|
{},
|
|
@@ -1215,7 +1316,7 @@ export class RollupContract {
|
|
|
1215
1316
|
for (const log of logs) {
|
|
1216
1317
|
const args = log.args;
|
|
1217
1318
|
if (args.checkpointNumber !== undefined) {
|
|
1218
|
-
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber) });
|
|
1319
|
+
callback({ checkpointNumber: CheckpointNumber.fromBigInt(args.checkpointNumber), event: log });
|
|
1219
1320
|
}
|
|
1220
1321
|
}
|
|
1221
1322
|
},
|
|
@@ -1248,6 +1349,17 @@ export class RollupContract {
|
|
|
1248
1349
|
);
|
|
1249
1350
|
}
|
|
1250
1351
|
|
|
1352
|
+
/**
|
|
1353
|
+
* Fetches OwnershipTransferred events emitted on the L1 block this rollup was deployed on.
|
|
1354
|
+
* The Rollup inherits from Ownable and emits this event in its constructor, so the event
|
|
1355
|
+
* is guaranteed to exist on `l1StartBlock` for any correctly deployed rollup. Used as a
|
|
1356
|
+
* probe to detect RPC nodes that prune historical logs.
|
|
1357
|
+
*/
|
|
1358
|
+
async getOwnershipTransferredEventsAtDeploy() {
|
|
1359
|
+
const l1StartBlock = await this.getL1StartBlock();
|
|
1360
|
+
return await this.rollup.getEvents.OwnershipTransferred({}, { fromBlock: l1StartBlock, toBlock: l1StartBlock });
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1251
1363
|
/** Fetches CheckpointProposed events within the given block range. */
|
|
1252
1364
|
async getCheckpointProposedEvents(fromBlock: bigint, toBlock: bigint): Promise<CheckpointProposedLog[]> {
|
|
1253
1365
|
const logs = await this.rollup.getEvents.CheckpointProposed({}, { fromBlock, toBlock });
|
|
@@ -1280,4 +1392,48 @@ export class RollupContract {
|
|
|
1280
1392
|
},
|
|
1281
1393
|
}));
|
|
1282
1394
|
}
|
|
1395
|
+
|
|
1396
|
+
/** Packs pending and proven checkpoint numbers into the chain tips storage format. */
|
|
1397
|
+
static packChainTips(pendingCheckpointNumber: bigint, provenCheckpointNumber: bigint): bigint {
|
|
1398
|
+
return (pendingCheckpointNumber << 128n) | (provenCheckpointNumber & ((1n << 128n) - 1n));
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
/** Storage slot for the chain tips (offset 0 within the STF storage struct). */
|
|
1402
|
+
static get chainTipsStorageSlot(): bigint {
|
|
1403
|
+
return BigInt(RollupContract.stfStorageSlot);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/**
|
|
1407
|
+
* Computes the storage slot for a field within a tempCheckpointLog entry.
|
|
1408
|
+
* @param checkpointNumber - The checkpoint number
|
|
1409
|
+
* @param field - The field within the CompressedTempCheckpointLog struct
|
|
1410
|
+
*/
|
|
1411
|
+
async getTempCheckpointLogStorageSlot(
|
|
1412
|
+
checkpointNumber: CheckpointNumber,
|
|
1413
|
+
field: TempCheckpointLogField,
|
|
1414
|
+
): Promise<bigint> {
|
|
1415
|
+
const [epochDuration, proofSubmissionEpochs] = await Promise.all([
|
|
1416
|
+
this.getEpochDuration(),
|
|
1417
|
+
this.getProofSubmissionEpochs(),
|
|
1418
|
+
]);
|
|
1419
|
+
return this.computeTempCheckpointLogStorageSlot(checkpointNumber, field, { epochDuration, proofSubmissionEpochs });
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
private computeTempCheckpointLogStorageSlot(
|
|
1423
|
+
checkpointNumber: CheckpointNumber,
|
|
1424
|
+
field: TempCheckpointLogField,
|
|
1425
|
+
constants: { epochDuration: number; proofSubmissionEpochs: number },
|
|
1426
|
+
): bigint {
|
|
1427
|
+
const fieldOffset = BigInt(field);
|
|
1428
|
+
const { epochDuration, proofSubmissionEpochs } = constants;
|
|
1429
|
+
const roundaboutSize = BigInt(epochDuration) * (BigInt(proofSubmissionEpochs) + 1n) + 1n;
|
|
1430
|
+
const tempCheckpointLogsBase = BigInt(RollupContract.stfStorageSlot) + 2n;
|
|
1431
|
+
const circularIndex = BigInt(checkpointNumber) % roundaboutSize;
|
|
1432
|
+
const entryBase = BigInt(
|
|
1433
|
+
keccak256(
|
|
1434
|
+
encodeAbiParameters([{ type: 'uint256' }, { type: 'uint256' }], [circularIndex, tempCheckpointLogsBase]),
|
|
1435
|
+
),
|
|
1436
|
+
);
|
|
1437
|
+
return entryBase + fieldOffset;
|
|
1438
|
+
}
|
|
1283
1439
|
}
|
|
@@ -7,7 +7,7 @@ import { EthAddress } from '@aztec/foundation/eth-address';
|
|
|
7
7
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
8
8
|
import { hexToBuffer } from '@aztec/foundation/string';
|
|
9
9
|
import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
|
|
10
|
-
import {
|
|
10
|
+
import { SlashingProposerAbi } from '@aztec/l1-artifacts/SlashingProposerAbi';
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
type GetContractReturnType,
|
|
@@ -19,13 +19,11 @@ import {
|
|
|
19
19
|
} from 'viem';
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Wrapper around the
|
|
22
|
+
* Wrapper around the SlashingProposer contract that provides
|
|
23
23
|
* a TypeScript interface for interacting with the consensus-based slashing system.
|
|
24
24
|
*/
|
|
25
|
-
export class
|
|
26
|
-
private readonly contract: GetContractReturnType<typeof
|
|
27
|
-
|
|
28
|
-
public readonly type = 'tally' as const;
|
|
25
|
+
export class SlashingProposerContract {
|
|
26
|
+
private readonly contract: GetContractReturnType<typeof SlashingProposerAbi, ViemClient>;
|
|
29
27
|
|
|
30
28
|
constructor(
|
|
31
29
|
public readonly client: ViemClient,
|
|
@@ -33,7 +31,7 @@ export class TallySlashingProposerContract {
|
|
|
33
31
|
) {
|
|
34
32
|
this.contract = getContract({
|
|
35
33
|
address: typeof address === 'string' ? address : address.toString(),
|
|
36
|
-
abi:
|
|
34
|
+
abi: SlashingProposerAbi,
|
|
37
35
|
client,
|
|
38
36
|
});
|
|
39
37
|
}
|
|
@@ -136,12 +134,12 @@ export class TallySlashingProposerContract {
|
|
|
136
134
|
|
|
137
135
|
/** Tries to extract a VoteCast event from the given logs. */
|
|
138
136
|
public tryExtractVoteCastEvent(logs: Log[]) {
|
|
139
|
-
return tryExtractEvent(logs, this.address.toString(),
|
|
137
|
+
return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'VoteCast');
|
|
140
138
|
}
|
|
141
139
|
|
|
142
140
|
/** Tries to extract a RoundExecuted event from the given logs. */
|
|
143
141
|
public tryExtractRoundExecutedEvent(logs: Log[]) {
|
|
144
|
-
return tryExtractEvent(logs, this.address.toString(),
|
|
142
|
+
return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'RoundExecuted');
|
|
145
143
|
}
|
|
146
144
|
|
|
147
145
|
/**
|
|
@@ -161,9 +159,9 @@ export class TallySlashingProposerContract {
|
|
|
161
159
|
|
|
162
160
|
return {
|
|
163
161
|
to: this.contract.address,
|
|
164
|
-
abi:
|
|
162
|
+
abi: SlashingProposerAbi,
|
|
165
163
|
data: encodeFunctionData({
|
|
166
|
-
abi:
|
|
164
|
+
abi: SlashingProposerAbi,
|
|
167
165
|
functionName: 'vote',
|
|
168
166
|
args: [votes, signature.toViemSignature()],
|
|
169
167
|
}),
|
|
@@ -173,7 +171,7 @@ export class TallySlashingProposerContract {
|
|
|
173
171
|
/** Returns the typed data definition to EIP712-sign for voting */
|
|
174
172
|
public buildVoteTypedData(votes: Hex, slot: SlotNumber): TypedDataDefinition {
|
|
175
173
|
const domain = {
|
|
176
|
-
name: '
|
|
174
|
+
name: 'SlashingProposer',
|
|
177
175
|
version: '1',
|
|
178
176
|
chainId: this.client.chain.id,
|
|
179
177
|
verifyingContract: this.contract.address,
|
|
@@ -209,9 +207,9 @@ export class TallySlashingProposerContract {
|
|
|
209
207
|
public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
|
|
210
208
|
return {
|
|
211
209
|
to: this.contract.address,
|
|
212
|
-
abi:
|
|
210
|
+
abi: SlashingProposerAbi,
|
|
213
211
|
data: encodeFunctionData({
|
|
214
|
-
abi:
|
|
212
|
+
abi: SlashingProposerAbi,
|
|
215
213
|
functionName: 'vote',
|
|
216
214
|
args: [votes, signature],
|
|
217
215
|
}),
|
|
@@ -227,9 +225,9 @@ export class TallySlashingProposerContract {
|
|
|
227
225
|
public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
|
|
228
226
|
return {
|
|
229
227
|
to: this.contract.address,
|
|
230
|
-
abi: mergeAbis([
|
|
228
|
+
abi: mergeAbis([SlashingProposerAbi, SlasherAbi]),
|
|
231
229
|
data: encodeFunctionData({
|
|
232
|
-
abi:
|
|
230
|
+
abi: SlashingProposerAbi,
|
|
233
231
|
functionName: 'executeRound',
|
|
234
232
|
args: [round, committees.map(c => c.map(addr => addr.toString()))],
|
|
235
233
|
}),
|