@aztec/ethereum 0.0.1-commit.3fd054f6 → 0.0.1-commit.431c48d
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 +19 -3
- package/dest/client.d.ts.map +1 -1
- package/dest/client.js +52 -3
- package/dest/config.d.ts +15 -6
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +29 -9
- 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 +135 -35
- package/dest/contracts/governance.d.ts.map +1 -1
- package/dest/contracts/governance.js +226 -15
- package/dest/contracts/governance_proposer.d.ts +33 -4
- package/dest/contracts/governance_proposer.d.ts.map +1 -1
- package/dest/contracts/governance_proposer.js +52 -13
- 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 +12 -7
- 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 +79 -13
- package/dest/contracts/multicall.d.ts.map +1 -1
- package/dest/contracts/multicall.js +117 -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 +5418 -28
- package/dest/contracts/rollup.d.ts.map +1 -1
- package/dest/contracts/rollup.js +389 -95
- package/dest/contracts/slashing_proposer.d.ts +171 -0
- package/dest/contracts/slashing_proposer.d.ts.map +1 -0
- package/dest/contracts/slashing_proposer.js +753 -0
- package/dest/contracts/watch_event.d.ts +36 -0
- package/dest/contracts/watch_event.d.ts.map +1 -0
- package/dest/contracts/watch_event.js +66 -0
- package/dest/deploy_aztec_l1_contracts.d.ts +4 -7
- package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
- package/dest/deploy_aztec_l1_contracts.js +15 -8
- 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/generated/l1-contracts-defaults.d.ts +2 -2
- package/dest/generated/l1-contracts-defaults.js +2 -2
- 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_tx_utils.d.ts +9 -1
- package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
- package/dest/l1_tx_utils/l1_tx_utils.js +115 -54
- 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 +49 -19
- 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/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 +62 -3
- 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 +26 -3
- package/dest/test/eth_cheat_codes.d.ts.map +1 -1
- package/dest/test/eth_cheat_codes.js +61 -40
- 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/recording_rpc_server.d.ts +18 -0
- package/dest/test/recording_rpc_server.d.ts.map +1 -0
- package/dest/test/recording_rpc_server.js +68 -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 +45 -14
- package/package.json +6 -6
- package/src/client.ts +63 -1
- package/src/config.ts +44 -13
- 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 +310 -10
- package/src/contracts/governance_proposer.ts +56 -13
- package/src/contracts/gse.ts +7 -2
- package/src/contracts/inbox.ts +13 -5
- package/src/contracts/index.ts +2 -2
- package/src/contracts/multicall.ts +179 -105
- package/src/contracts/outbox.ts +42 -8
- package/src/contracts/rollup.ts +429 -86
- package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +119 -45
- package/src/contracts/watch_event.ts +110 -0
- package/src/deploy_aztec_l1_contracts.ts +19 -10
- package/src/foundry_binary.ts +57 -0
- package/src/generated/l1-contracts-defaults.ts +2 -2
- 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_tx_utils.ts +107 -50
- package/src/l1_tx_utils/readonly_l1_tx_utils.ts +49 -13
- package/src/l1_tx_utils/tx_delayer.ts +16 -7
- package/src/publisher_manager.ts +54 -18
- package/src/queries.ts +70 -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 +57 -43
- package/src/test/index.ts +1 -0
- package/src/test/recording_rpc_server.ts +69 -0
- package/src/test/rollup_cheat_codes.ts +115 -6
- package/src/test/start_anvil.ts +38 -6
- package/src/utils.ts +41 -17
- 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 +0 -140
- package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
- package/dest/contracts/tally_slashing_proposer.js +0 -320
- package/src/contracts/empire_slashing_proposer.ts +0 -265
|
@@ -3,11 +3,13 @@ import type { ViemClient } from '@aztec/ethereum/types';
|
|
|
3
3
|
import { mergeAbis, tryExtractEvent } from '@aztec/ethereum/utils';
|
|
4
4
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
5
5
|
import { Buffer32 } from '@aztec/foundation/buffer';
|
|
6
|
+
import { memoize } from '@aztec/foundation/decorators';
|
|
6
7
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
7
8
|
import { Signature } from '@aztec/foundation/eth-signature';
|
|
9
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
8
10
|
import { hexToBuffer } from '@aztec/foundation/string';
|
|
9
11
|
import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
|
|
10
|
-
import {
|
|
12
|
+
import { SlashingProposerAbi } from '@aztec/l1-artifacts/SlashingProposerAbi';
|
|
11
13
|
|
|
12
14
|
import {
|
|
13
15
|
type GetContractReturnType,
|
|
@@ -18,14 +20,23 @@ import {
|
|
|
18
20
|
getContract,
|
|
19
21
|
} from 'viem';
|
|
20
22
|
|
|
23
|
+
import { type WatchContractEventOptions, watchContractEvent } from './watch_event.js';
|
|
24
|
+
|
|
21
25
|
/**
|
|
22
|
-
* Wrapper around the
|
|
26
|
+
* Wrapper around the SlashingProposer contract that provides
|
|
23
27
|
* a TypeScript interface for interacting with the consensus-based slashing system.
|
|
24
28
|
*/
|
|
25
|
-
export class
|
|
26
|
-
private readonly contract: GetContractReturnType<typeof
|
|
29
|
+
export class SlashingProposerContract {
|
|
30
|
+
private readonly contract: GetContractReturnType<typeof SlashingProposerAbi, ViemClient>;
|
|
31
|
+
private readonly logger = createLogger('ethereum:slashing_proposer');
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Slash target validators of the last round asked for. Safe to cache because a round's targets are the committees
|
|
35
|
+
* of epochs that had already ended when the round opened (see SLASH_OFFSET_IN_ROUNDS), and those committees are
|
|
36
|
+
* sampled from validator set and randao snapshots taken before the epoch started, so they cannot change while the
|
|
37
|
+
* round is live. The promise is cached rather than the result, so concurrent callers share one in-flight read.
|
|
38
|
+
*/
|
|
39
|
+
private slashTargetValidators: { round: bigint; validators: Promise<EthAddress[]> } | undefined;
|
|
29
40
|
|
|
30
41
|
constructor(
|
|
31
42
|
public readonly client: ViemClient,
|
|
@@ -33,7 +44,7 @@ export class TallySlashingProposerContract {
|
|
|
33
44
|
) {
|
|
34
45
|
this.contract = getContract({
|
|
35
46
|
address: typeof address === 'string' ? address : address.toString(),
|
|
36
|
-
abi:
|
|
47
|
+
abi: SlashingProposerAbi,
|
|
37
48
|
client,
|
|
38
49
|
});
|
|
39
50
|
}
|
|
@@ -66,6 +77,8 @@ export class TallySlashingProposerContract {
|
|
|
66
77
|
return this.contract.read.EXECUTION_DELAY_IN_ROUNDS();
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
/** Returns the slash amounts for the three slash unit levels. Immutable on the contract, so memoized. */
|
|
81
|
+
@memoize
|
|
69
82
|
public getSlashingAmounts(): Promise<[bigint, bigint, bigint]> {
|
|
70
83
|
return Promise.all([
|
|
71
84
|
this.contract.read.SLASH_AMOUNT_SMALL(),
|
|
@@ -136,12 +149,12 @@ export class TallySlashingProposerContract {
|
|
|
136
149
|
|
|
137
150
|
/** Tries to extract a VoteCast event from the given logs. */
|
|
138
151
|
public tryExtractVoteCastEvent(logs: Log[]) {
|
|
139
|
-
return tryExtractEvent(logs, this.address.toString(),
|
|
152
|
+
return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'VoteCast');
|
|
140
153
|
}
|
|
141
154
|
|
|
142
155
|
/** Tries to extract a RoundExecuted event from the given logs. */
|
|
143
156
|
public tryExtractRoundExecutedEvent(logs: Log[]) {
|
|
144
|
-
return tryExtractEvent(logs, this.address.toString(),
|
|
157
|
+
return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'RoundExecuted');
|
|
145
158
|
}
|
|
146
159
|
|
|
147
160
|
/**
|
|
@@ -161,9 +174,9 @@ export class TallySlashingProposerContract {
|
|
|
161
174
|
|
|
162
175
|
return {
|
|
163
176
|
to: this.contract.address,
|
|
164
|
-
abi:
|
|
177
|
+
abi: SlashingProposerAbi,
|
|
165
178
|
data: encodeFunctionData({
|
|
166
|
-
abi:
|
|
179
|
+
abi: SlashingProposerAbi,
|
|
167
180
|
functionName: 'vote',
|
|
168
181
|
args: [votes, signature.toViemSignature()],
|
|
169
182
|
}),
|
|
@@ -173,7 +186,7 @@ export class TallySlashingProposerContract {
|
|
|
173
186
|
/** Returns the typed data definition to EIP712-sign for voting */
|
|
174
187
|
public buildVoteTypedData(votes: Hex, slot: SlotNumber): TypedDataDefinition {
|
|
175
188
|
const domain = {
|
|
176
|
-
name: '
|
|
189
|
+
name: 'SlashingProposer',
|
|
177
190
|
version: '1',
|
|
178
191
|
chainId: this.client.chain.id,
|
|
179
192
|
verifyingContract: this.contract.address,
|
|
@@ -209,9 +222,9 @@ export class TallySlashingProposerContract {
|
|
|
209
222
|
public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
|
|
210
223
|
return {
|
|
211
224
|
to: this.contract.address,
|
|
212
|
-
abi:
|
|
225
|
+
abi: SlashingProposerAbi,
|
|
213
226
|
data: encodeFunctionData({
|
|
214
|
-
abi:
|
|
227
|
+
abi: SlashingProposerAbi,
|
|
215
228
|
functionName: 'vote',
|
|
216
229
|
args: [votes, signature],
|
|
217
230
|
}),
|
|
@@ -227,75 +240,136 @@ export class TallySlashingProposerContract {
|
|
|
227
240
|
public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
|
|
228
241
|
return {
|
|
229
242
|
to: this.contract.address,
|
|
230
|
-
abi: mergeAbis([
|
|
243
|
+
abi: mergeAbis([SlashingProposerAbi, SlasherAbi]),
|
|
231
244
|
data: encodeFunctionData({
|
|
232
|
-
abi:
|
|
245
|
+
abi: SlashingProposerAbi,
|
|
233
246
|
functionName: 'executeRound',
|
|
234
247
|
args: [round, committees.map(c => c.map(addr => addr.toString()))],
|
|
235
248
|
}),
|
|
236
249
|
};
|
|
237
250
|
}
|
|
238
251
|
|
|
252
|
+
/**
|
|
253
|
+
* Returns the validators eligible to be voted against in a round, in the order votes encode them. Cached for the
|
|
254
|
+
* last round asked for: reading them runs a committee sampling simulation on L1, and every vote of a round decodes
|
|
255
|
+
* against the same list, so a caller walking a round's votes would otherwise repeat that call per vote. Only the
|
|
256
|
+
* last round is kept since callers move forward round by round.
|
|
257
|
+
*/
|
|
258
|
+
public getSlashTargetValidators(round: bigint): Promise<EthAddress[]> {
|
|
259
|
+
const cached = this.slashTargetValidators;
|
|
260
|
+
if (cached?.round === round) {
|
|
261
|
+
return cached.validators;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const entry = { round, validators: this.fetchSlashTargetValidators(round) };
|
|
265
|
+
this.slashTargetValidators = entry;
|
|
266
|
+
// A failed read must not stay cached, or every later vote of the round would replay the same rejection
|
|
267
|
+
entry.validators.catch(() => {
|
|
268
|
+
if (this.slashTargetValidators === entry) {
|
|
269
|
+
this.slashTargetValidators = undefined;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
return entry.validators;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private async fetchSlashTargetValidators(round: bigint): Promise<EthAddress[]> {
|
|
276
|
+
const { result } = await this.contract.simulate.getSlashTargetCommittees([round]);
|
|
277
|
+
return result.flat().map(validator => EthAddress.fromString(validator));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Returns the slash amount voted for each target validator by a single vote of a round.
|
|
282
|
+
* @param index - Position of the vote within the round, from 0 (inclusive) to the round's vote count (exclusive)
|
|
283
|
+
*/
|
|
284
|
+
public async getVoteAt(round: bigint, index: bigint): Promise<SlashVoteTarget[]> {
|
|
285
|
+
const [validators, vote, slashAmounts] = await Promise.all([
|
|
286
|
+
this.getSlashTargetValidators(round),
|
|
287
|
+
this.contract.read.getVotes([round, index]),
|
|
288
|
+
this.getSlashingAmounts(),
|
|
289
|
+
]);
|
|
290
|
+
return decodeVote(vote, validators, slashAmounts);
|
|
291
|
+
}
|
|
292
|
+
|
|
239
293
|
/** Returns the last vote emitted for a given round */
|
|
240
294
|
public async getLastVote(round: bigint) {
|
|
241
295
|
const { voteCount } = await this.getRound(round);
|
|
242
|
-
|
|
243
|
-
const vote = await this.contract.read.getVotes([round, voteCount - 1n]);
|
|
244
|
-
const decoded = decodeSlashConsensusVotes(hexToBuffer(vote));
|
|
245
|
-
const slashAmounts = await this.getSlashingAmounts();
|
|
246
|
-
return decoded
|
|
247
|
-
.map((units, i) => ({
|
|
248
|
-
validator: EthAddress.fromString(validators[i]),
|
|
249
|
-
slashAmount: slashAmounts[units - 1] ?? 0n,
|
|
250
|
-
}))
|
|
251
|
-
.filter(v => v.slashAmount > 0n);
|
|
296
|
+
return await this.getVoteAt(round, voteCount - 1n);
|
|
252
297
|
}
|
|
253
298
|
|
|
254
299
|
/**
|
|
255
|
-
* Listen for VoteCast events
|
|
300
|
+
* Listen for VoteCast events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit them and
|
|
301
|
+
* removals are never reported, and events mined within roughly one polling interval of subscribing may be missed.
|
|
256
302
|
* @param callback - Callback function to handle vote cast events
|
|
257
303
|
* @returns Unwatch function
|
|
258
304
|
*/
|
|
259
|
-
public listenToVoteCast(
|
|
260
|
-
|
|
261
|
-
|
|
305
|
+
public listenToVoteCast(
|
|
306
|
+
callback: (args: { round: bigint; proposer: string }) => unknown,
|
|
307
|
+
options?: WatchContractEventOptions,
|
|
308
|
+
): () => void {
|
|
309
|
+
return watchContractEvent(
|
|
310
|
+
this.client,
|
|
311
|
+
this.logger,
|
|
262
312
|
{
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
313
|
+
address: this.contract.address,
|
|
314
|
+
abi: SlashingProposerAbi,
|
|
315
|
+
eventName: 'VoteCast',
|
|
316
|
+
onLog: log => {
|
|
317
|
+
const { round, proposer } = log.args;
|
|
318
|
+
if (round !== undefined && proposer) {
|
|
319
|
+
return callback({ round, proposer });
|
|
269
320
|
}
|
|
270
321
|
},
|
|
271
322
|
},
|
|
323
|
+
options,
|
|
272
324
|
);
|
|
273
325
|
}
|
|
274
326
|
|
|
275
327
|
/**
|
|
276
|
-
* Listen for RoundExecuted events
|
|
328
|
+
* Listen for RoundExecuted events. Events are delivered by polling `eth_getLogs`: a reorg may re-emit them and
|
|
329
|
+
* removals are never reported, and events mined within roughly one polling interval of subscribing may be missed.
|
|
277
330
|
* @param callback - Callback function to handle round executed events
|
|
278
331
|
* @returns Unwatch function
|
|
279
332
|
*/
|
|
280
333
|
public listenToRoundExecuted(
|
|
281
|
-
callback: (args: { round: bigint; slashCount: bigint; l1BlockHash: Hex }) =>
|
|
334
|
+
callback: (args: { round: bigint; slashCount: bigint; l1BlockHash: Hex }) => unknown,
|
|
335
|
+
options?: WatchContractEventOptions,
|
|
282
336
|
): () => void {
|
|
283
|
-
return
|
|
284
|
-
|
|
337
|
+
return watchContractEvent(
|
|
338
|
+
this.client,
|
|
339
|
+
this.logger,
|
|
285
340
|
{
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
341
|
+
address: this.contract.address,
|
|
342
|
+
abi: SlashingProposerAbi,
|
|
343
|
+
eventName: 'RoundExecuted',
|
|
344
|
+
onLog: log => {
|
|
345
|
+
const { round, slashCount } = log.args;
|
|
346
|
+
if (round !== undefined && slashCount !== undefined) {
|
|
347
|
+
return callback({ round, slashCount, l1BlockHash: log.blockHash });
|
|
292
348
|
}
|
|
293
349
|
},
|
|
294
350
|
},
|
|
351
|
+
options,
|
|
295
352
|
);
|
|
296
353
|
}
|
|
297
354
|
}
|
|
298
355
|
|
|
356
|
+
/**
|
|
357
|
+
* A validator targeted by a slashing vote, with the amount voted. The position is the validator's index in the
|
|
358
|
+
* round's flattened slash target committees — the unit the contract tallies quorum by. A validator sitting in
|
|
359
|
+
* several of the round's committees holds several positions, each with its own tally.
|
|
360
|
+
*/
|
|
361
|
+
export type SlashVoteTarget = { validator: EthAddress; slashAmount: bigint; position: number };
|
|
362
|
+
|
|
363
|
+
function decodeVote(vote: Hex, validators: EthAddress[], slashAmounts: [bigint, bigint, bigint]): SlashVoteTarget[] {
|
|
364
|
+
return decodeSlashConsensusVotes(hexToBuffer(vote))
|
|
365
|
+
.map((units, position) => ({
|
|
366
|
+
validator: validators[position],
|
|
367
|
+
slashAmount: slashAmounts[units - 1] ?? 0n,
|
|
368
|
+
position,
|
|
369
|
+
}))
|
|
370
|
+
.filter(v => v.slashAmount > 0n);
|
|
371
|
+
}
|
|
372
|
+
|
|
299
373
|
/**
|
|
300
374
|
* Decodes a Buffer containing slash votes back into an array of numbers.
|
|
301
375
|
* Each vote is represented as a 2-bit value (0, 1, 2, or 3) representing slashing units.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
2
|
+
import { RunningPromise } from '@aztec/foundation/promise';
|
|
3
|
+
|
|
4
|
+
import type { Abi, ContractEventName, GetContractEventsReturnType, Hex } from 'viem';
|
|
5
|
+
|
|
6
|
+
import type { ViemClient } from '../types.js';
|
|
7
|
+
|
|
8
|
+
/** Default maximum number of blocks queried per `eth_getLogs` request. */
|
|
9
|
+
const DEFAULT_MAX_BLOCK_RANGE = 100;
|
|
10
|
+
|
|
11
|
+
/** Options for {@link watchContractEvent}. */
|
|
12
|
+
export type WatchContractEventOptions = {
|
|
13
|
+
/** How often to poll for new L1 blocks, in ms. Defaults to the client's polling interval. */
|
|
14
|
+
pollingIntervalMs?: number;
|
|
15
|
+
/** Maximum number of blocks per `eth_getLogs` request; wider catch-up ranges are split into chunks. */
|
|
16
|
+
maxBlockRange?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** Event identity and handler for {@link watchContractEvent}. */
|
|
20
|
+
export type WatchContractEventParameters<
|
|
21
|
+
TAbi extends Abi,
|
|
22
|
+
TEventName extends ContractEventName<TAbi>,
|
|
23
|
+
TStrict extends boolean | undefined = undefined,
|
|
24
|
+
> = {
|
|
25
|
+
address: Hex;
|
|
26
|
+
abi: TAbi;
|
|
27
|
+
eventName: TEventName;
|
|
28
|
+
/** Whether logs must decode against the event's indexed/non-indexed arguments (defaults to false). */
|
|
29
|
+
strict?: TStrict;
|
|
30
|
+
/** Called once per event log. Errors, thrown or from a returned promise, are caught and logged. */
|
|
31
|
+
onLog: (log: GetContractEventsReturnType<TAbi, TEventName, TStrict>[number]) => unknown;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Watches for contract events by polling `eth_blockNumber` (cached by viem per client) and fetching new logs with
|
|
36
|
+
* ranged `eth_getLogs` requests, never installing server-side filters. Server-side filters are unusable against
|
|
37
|
+
* real-world endpoints: a load balancer routes polls to backends that never saw the filter, and providers report a
|
|
38
|
+
* purged filter with error codes viem does not recognize as "filter gone", so viem's own watchers either churn
|
|
39
|
+
* recreating filters or poll a dead filter id forever while silently missing events.
|
|
40
|
+
*
|
|
41
|
+
* The block that is latest on the first poll becomes the starting cursor, so its events are reported and events from
|
|
42
|
+
* earlier blocks are not. Requests never span more than `maxBlockRange` blocks: catching up after downtime is
|
|
43
|
+
* chunked into multiple requests, and the cursor advances per successful chunk so a failure retries from where it
|
|
44
|
+
* left off on the next tick. A reorg may re-emit events and removals are never reported.
|
|
45
|
+
*
|
|
46
|
+
* @returns A function that stops the watcher.
|
|
47
|
+
*/
|
|
48
|
+
export function watchContractEvent<
|
|
49
|
+
TAbi extends Abi,
|
|
50
|
+
TEventName extends ContractEventName<TAbi>,
|
|
51
|
+
TStrict extends boolean | undefined = undefined,
|
|
52
|
+
>(
|
|
53
|
+
client: ViemClient,
|
|
54
|
+
logger: Logger,
|
|
55
|
+
params: WatchContractEventParameters<TAbi, TEventName, TStrict>,
|
|
56
|
+
options: WatchContractEventOptions = {},
|
|
57
|
+
): () => void {
|
|
58
|
+
const { address, abi, eventName, strict, onLog } = params;
|
|
59
|
+
const pollingIntervalMs = options.pollingIntervalMs ?? client.pollingInterval;
|
|
60
|
+
const maxBlockRange = BigInt(options.maxBlockRange ?? DEFAULT_MAX_BLOCK_RANGE);
|
|
61
|
+
|
|
62
|
+
const logHandlerError = (err: unknown) => logger.error(`Error handling ${eventName} L1 event`, err);
|
|
63
|
+
const deliver = (log: GetContractEventsReturnType<TAbi, TEventName, TStrict>[number]) => {
|
|
64
|
+
try {
|
|
65
|
+
const result = onLog(log);
|
|
66
|
+
if (result instanceof Promise) {
|
|
67
|
+
result.catch(logHandlerError);
|
|
68
|
+
}
|
|
69
|
+
} catch (err) {
|
|
70
|
+
logHandlerError(err);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
let stopped = false;
|
|
75
|
+
let nextBlock: bigint | undefined;
|
|
76
|
+
|
|
77
|
+
const poll = async () => {
|
|
78
|
+
const latestBlock = await client.getBlockNumber();
|
|
79
|
+
if (nextBlock === undefined) {
|
|
80
|
+
nextBlock = latestBlock;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
let fromBlock: bigint = nextBlock;
|
|
84
|
+
while (fromBlock <= latestBlock && !stopped) {
|
|
85
|
+
const chunkEnd = fromBlock + maxBlockRange - 1n;
|
|
86
|
+
const toBlock = chunkEnd < latestBlock ? chunkEnd : latestBlock;
|
|
87
|
+
const logs = await client.getContractEvents<TAbi, TEventName, TStrict, bigint, bigint>({
|
|
88
|
+
address,
|
|
89
|
+
abi,
|
|
90
|
+
eventName,
|
|
91
|
+
strict,
|
|
92
|
+
fromBlock,
|
|
93
|
+
toBlock,
|
|
94
|
+
});
|
|
95
|
+
for (const log of logs) {
|
|
96
|
+
deliver(log);
|
|
97
|
+
}
|
|
98
|
+
fromBlock = toBlock + 1n;
|
|
99
|
+
nextBlock = fromBlock;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const handlePollError = (err: unknown) => logger.warn(`Error polling for ${eventName} L1 events`, { err });
|
|
104
|
+
const runner = new RunningPromise(poll, logger, pollingIntervalMs, handlePollError).start();
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
stopped = true;
|
|
108
|
+
void runner.stop();
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -19,9 +19,10 @@ import { mainnet, sepolia } from 'viem/chains';
|
|
|
19
19
|
|
|
20
20
|
import { createEthereumChain, isAnvilTestChain } from './chain.js';
|
|
21
21
|
import { createExtendedL1Client } from './client.js';
|
|
22
|
-
import type
|
|
22
|
+
import { type L1ContractsConfig, assertValidSlotDurations } from './config.js';
|
|
23
23
|
import { deployMulticall3 } from './contracts/multicall.js';
|
|
24
24
|
import { RollupContract } from './contracts/rollup.js';
|
|
25
|
+
import { resolveFoundryBinary } from './foundry_binary.js';
|
|
25
26
|
import type { L1ContractAddresses } from './l1_contract_addresses.js';
|
|
26
27
|
import type { ExtendedViemWalletClient } from './types.js';
|
|
27
28
|
|
|
@@ -93,11 +94,16 @@ function runProcess<T>(
|
|
|
93
94
|
|
|
94
95
|
// Covers an edge where where we may have a cached BlobLib that is not meant for production.
|
|
95
96
|
// Despite the profile apparently sometimes cached code remains (so says Lasse after his ignition-monorepo arc).
|
|
96
|
-
async function maybeForgeForceProductionBuild(
|
|
97
|
+
async function maybeForgeForceProductionBuild(
|
|
98
|
+
forgeBin: string,
|
|
99
|
+
l1ContractsPath: string,
|
|
100
|
+
script: string,
|
|
101
|
+
chainId: number,
|
|
102
|
+
) {
|
|
97
103
|
if (chainId === mainnet.id) {
|
|
98
104
|
logger.info(`Recompiling ${script} with production profile for mainnet deployment`);
|
|
99
105
|
logger.info('This may take a minute but ensures production BlobLib is used.');
|
|
100
|
-
await runProcess(
|
|
106
|
+
await runProcess(forgeBin, ['build', script, '--force'], { FOUNDRY_PROFILE: 'production' }, l1ContractsPath);
|
|
101
107
|
}
|
|
102
108
|
}
|
|
103
109
|
|
|
@@ -230,7 +236,6 @@ export function computeValidatorData(operator: Operator): ValidatorJson {
|
|
|
230
236
|
export interface RollupUpgradeAddresses {
|
|
231
237
|
rollupAddress: string;
|
|
232
238
|
verifierAddress: string;
|
|
233
|
-
slashFactoryAddress: string;
|
|
234
239
|
inboxAddress: string;
|
|
235
240
|
outboxAddress: string;
|
|
236
241
|
feeJuicePortalAddress: string;
|
|
@@ -243,7 +248,6 @@ export interface RollupUpgradeAddresses {
|
|
|
243
248
|
export interface ForgeRollupUpgradeResult {
|
|
244
249
|
rollupAddress: Hex;
|
|
245
250
|
verifierAddress: Hex;
|
|
246
|
-
slashFactoryAddress: Hex;
|
|
247
251
|
inboxAddress: Hex;
|
|
248
252
|
outboxAddress: Hex;
|
|
249
253
|
feeJuicePortalAddress: Hex;
|
|
@@ -284,6 +288,7 @@ export async function deployAztecL1Contracts(
|
|
|
284
288
|
args: DeployAztecL1ContractsArgs,
|
|
285
289
|
): Promise<DeployAztecL1ContractsReturnType> {
|
|
286
290
|
logger.info(`Deploying L1 contracts with config: ${jsonStringify(args)}`);
|
|
291
|
+
assertValidSlotDurations(args);
|
|
287
292
|
if (args.initialValidators && args.initialValidators.length > 0 && args.existingTokenAddress) {
|
|
288
293
|
throw new Error(
|
|
289
294
|
'Cannot deploy with both initialValidators and existingTokenAddress. ' +
|
|
@@ -321,8 +326,9 @@ export async function deployAztecL1Contracts(
|
|
|
321
326
|
// Use foundry-artifacts from l1-artifacts package
|
|
322
327
|
const l1ContractsPath = prepareL1ContractsForDeployment();
|
|
323
328
|
|
|
329
|
+
const forgeBin = resolveFoundryBinary('forge');
|
|
324
330
|
const FORGE_SCRIPT = 'script/deploy/DeployAztecL1Contracts.s.sol';
|
|
325
|
-
await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
331
|
+
await maybeForgeForceProductionBuild(forgeBin, l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
326
332
|
|
|
327
333
|
// Verify contracts on Etherscan when on mainnet/sepolia and ETHERSCAN_API_KEY is available.
|
|
328
334
|
const isVerifiableChain = chainId === mainnet.id || chainId === sepolia.id;
|
|
@@ -347,6 +353,8 @@ export async function deployAztecL1Contracts(
|
|
|
347
353
|
...(shouldVerify ? ['--verify'] : []),
|
|
348
354
|
];
|
|
349
355
|
const forgeEnv = {
|
|
356
|
+
// Resolved forge binary picked up by forge_broadcast.js, so it works without forge on PATH.
|
|
357
|
+
FORGE_BIN: forgeBin,
|
|
350
358
|
// Env vars required by l1-contracts/script/deploy/DeploymentConfiguration.sol.
|
|
351
359
|
NETWORK: getActiveNetworkName(),
|
|
352
360
|
FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
|
|
@@ -403,7 +411,6 @@ export async function deployAztecL1Contracts(
|
|
|
403
411
|
governanceProposerAddress: EthAddress.fromString(result.governanceProposerAddress),
|
|
404
412
|
governanceAddress: EthAddress.fromString(result.governanceAddress),
|
|
405
413
|
stakingAssetAddress: EthAddress.fromString(result.stakingAssetAddress),
|
|
406
|
-
slashFactoryAddress: result.slashFactoryAddress ? EthAddress.fromString(result.slashFactoryAddress) : undefined,
|
|
407
414
|
feeAssetHandlerAddress: result.feeAssetHandlerAddress
|
|
408
415
|
? EthAddress.fromString(result.feeAssetHandlerAddress)
|
|
409
416
|
: undefined,
|
|
@@ -585,7 +592,7 @@ export function getDeployRollupForUpgradeEnvVars(
|
|
|
585
592
|
AZTEC_EXIT_DELAY_SECONDS: args.exitDelaySeconds.toString(),
|
|
586
593
|
AZTEC_PROVING_COST_PER_MANA: args.provingCostPerMana.toString(),
|
|
587
594
|
AZTEC_INITIAL_ETH_PER_FEE_ASSET: args.initialEthPerFeeAsset.toString(),
|
|
588
|
-
|
|
595
|
+
AZTEC_SLASHER_ENABLED: args.slasherEnabled ? 'true' : 'false',
|
|
589
596
|
AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: args.slashingRoundSizeInEpochs.toString(),
|
|
590
597
|
AZTEC_SLASHING_QUORUM: args.slashingQuorum?.toString(),
|
|
591
598
|
AZTEC_SLASHING_OFFSET_IN_ROUNDS: args.slashingOffsetInRounds.toString(),
|
|
@@ -615,12 +622,15 @@ export const deployRollupForUpgrade = async (
|
|
|
615
622
|
// Use foundry-artifacts from l1-artifacts package
|
|
616
623
|
const l1ContractsPath = prepareL1ContractsForDeployment();
|
|
617
624
|
|
|
625
|
+
const forgeBin = resolveFoundryBinary('forge');
|
|
618
626
|
const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
|
|
619
|
-
await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
627
|
+
await maybeForgeForceProductionBuild(forgeBin, l1ContractsPath, FORGE_SCRIPT, chainId);
|
|
620
628
|
|
|
621
629
|
const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
|
|
622
630
|
const forgeArgs = [FORGE_SCRIPT, '--sig', 'run()', '--private-key', privateKey, '--rpc-url', rpcUrl];
|
|
623
631
|
const forgeEnv = {
|
|
632
|
+
// Resolved forge binary picked up by forge_broadcast.js, so it works without forge on PATH.
|
|
633
|
+
FORGE_BIN: forgeBin,
|
|
624
634
|
FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
|
|
625
635
|
// Env vars required by l1-contracts/script/deploy/RollupConfiguration.sol.
|
|
626
636
|
REGISTRY_ADDRESS: registryAddress.toString(),
|
|
@@ -645,6 +655,5 @@ export const deployRollupForUpgrade = async (
|
|
|
645
655
|
|
|
646
656
|
return {
|
|
647
657
|
rollup,
|
|
648
|
-
slashFactoryAddress: result.slashFactoryAddress,
|
|
649
658
|
};
|
|
650
659
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import { accessSync, constants } from 'fs';
|
|
3
|
+
import { homedir } from 'os';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
|
|
6
|
+
function isExecutable(path: string): boolean {
|
|
7
|
+
try {
|
|
8
|
+
accessSync(path, constants.X_OK);
|
|
9
|
+
return true;
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Locate a Foundry binary (`anvil`, `forge`, ...) without relying on the caller's PATH. Order:
|
|
17
|
+
* 1. `$<NAME>_BIN` (e.g. `$ANVIL_BIN`, `$FORGE_BIN`) — explicit override, e.g. for CI with a pinned
|
|
18
|
+
* version. Throws if set but not pointing at an executable, instead of silently falling back.
|
|
19
|
+
* 2. `~/.aztec/current/internal-bin/<name>` — where aztec-up installs it.
|
|
20
|
+
* 3. `~/.aztec/current/bin/aztec-<name>` — the publicly-exposed symlink.
|
|
21
|
+
* 4. `~/.foundry/bin/<name>` — standalone foundryup install.
|
|
22
|
+
* 5. `command -v <name>` — anything else on PATH.
|
|
23
|
+
*
|
|
24
|
+
* Throws with a directive message if none work.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveFoundryBinary(name: string): string {
|
|
27
|
+
const envVar = `${name.toUpperCase()}_BIN`;
|
|
28
|
+
const envBin = process.env[envVar];
|
|
29
|
+
if (envBin) {
|
|
30
|
+
if (!isExecutable(envBin)) {
|
|
31
|
+
throw new Error(`$${envVar} is set to ${envBin}, which does not exist or is not executable.`);
|
|
32
|
+
}
|
|
33
|
+
return envBin;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const candidates = [
|
|
37
|
+
join(homedir(), '.aztec', 'current', 'internal-bin', name),
|
|
38
|
+
join(homedir(), '.aztec', 'current', 'bin', `aztec-${name}`),
|
|
39
|
+
join(homedir(), '.foundry', 'bin', name),
|
|
40
|
+
];
|
|
41
|
+
for (const path of candidates) {
|
|
42
|
+
if (isExecutable(path)) {
|
|
43
|
+
return path;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const which = spawnSync('sh', ['-c', `command -v ${name}`], { encoding: 'utf8' });
|
|
48
|
+
if (which.status === 0 && which.stdout.trim()) {
|
|
49
|
+
return which.stdout.trim();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw new Error(
|
|
53
|
+
`${name} binary not found. Tried $${envVar}, ~/.aztec/current/internal-bin/${name}, ` +
|
|
54
|
+
`~/.aztec/current/bin/aztec-${name}, ~/.foundry/bin/${name}, and $PATH. ` +
|
|
55
|
+
`Install via \`aztec-up\` or set ${envVar} to a working binary.`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -13,12 +13,12 @@ export const l1ContractsDefaultEnv = {
|
|
|
13
13
|
AZTEC_EJECTION_THRESHOLD: 50000000000000000000,
|
|
14
14
|
AZTEC_LOCAL_EJECTION_THRESHOLD: 98000000000000000000,
|
|
15
15
|
AZTEC_EXIT_DELAY_SECONDS: 172800,
|
|
16
|
-
AZTEC_INBOX_LAG:
|
|
16
|
+
AZTEC_INBOX_LAG: 2,
|
|
17
17
|
AZTEC_PROOF_SUBMISSION_EPOCHS: 1,
|
|
18
18
|
AZTEC_MANA_TARGET: 100000000,
|
|
19
19
|
AZTEC_PROVING_COST_PER_MANA: 100,
|
|
20
20
|
AZTEC_INITIAL_ETH_PER_FEE_ASSET: 10000000,
|
|
21
|
-
|
|
21
|
+
AZTEC_SLASHER_ENABLED: true,
|
|
22
22
|
AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: 4,
|
|
23
23
|
AZTEC_SLASHING_LIFETIME_IN_ROUNDS: 5,
|
|
24
24
|
AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS: 0,
|
package/src/l1_artifacts.ts
CHANGED
|
@@ -3,10 +3,6 @@ import {
|
|
|
3
3
|
CoinIssuerBytecode,
|
|
4
4
|
DateGatedRelayerAbi,
|
|
5
5
|
DateGatedRelayerBytecode,
|
|
6
|
-
EmpireSlasherDeploymentExtLibAbi,
|
|
7
|
-
EmpireSlasherDeploymentExtLibBytecode,
|
|
8
|
-
EmpireSlashingProposerAbi,
|
|
9
|
-
EmpireSlashingProposerBytecode,
|
|
10
6
|
FeeAssetHandlerAbi,
|
|
11
7
|
FeeAssetHandlerBytecode,
|
|
12
8
|
FeeJuicePortalAbi,
|
|
@@ -42,16 +38,14 @@ import {
|
|
|
42
38
|
RollupLinkReferences,
|
|
43
39
|
RollupOperationsExtLibAbi,
|
|
44
40
|
RollupOperationsExtLibBytecode,
|
|
45
|
-
SlashFactoryAbi,
|
|
46
|
-
SlashFactoryBytecode,
|
|
47
41
|
SlasherAbi,
|
|
48
42
|
SlasherBytecode,
|
|
43
|
+
SlasherDeploymentExtLibAbi,
|
|
44
|
+
SlasherDeploymentExtLibBytecode,
|
|
45
|
+
SlashingProposerAbi,
|
|
46
|
+
SlashingProposerBytecode,
|
|
49
47
|
StakingAssetHandlerAbi,
|
|
50
48
|
StakingAssetHandlerBytecode,
|
|
51
|
-
TallySlasherDeploymentExtLibAbi,
|
|
52
|
-
TallySlasherDeploymentExtLibBytecode,
|
|
53
|
-
TallySlashingProposerAbi,
|
|
54
|
-
TallySlashingProposerBytecode,
|
|
55
49
|
TestERC20Abi,
|
|
56
50
|
TestERC20Bytecode,
|
|
57
51
|
ValidatorOperationsExtLibAbi,
|
|
@@ -107,15 +101,10 @@ export const RollupArtifact = {
|
|
|
107
101
|
contractAbi: RewardExtLibAbi,
|
|
108
102
|
contractBytecode: RewardExtLibBytecode as Hex,
|
|
109
103
|
},
|
|
110
|
-
|
|
111
|
-
name: '
|
|
112
|
-
contractAbi:
|
|
113
|
-
contractBytecode:
|
|
114
|
-
},
|
|
115
|
-
EmpireSlasherDeploymentExtLib: {
|
|
116
|
-
name: 'EmpireSlasherDeploymentExtLib',
|
|
117
|
-
contractAbi: EmpireSlasherDeploymentExtLibAbi,
|
|
118
|
-
contractBytecode: EmpireSlasherDeploymentExtLibBytecode as Hex,
|
|
104
|
+
SlasherDeploymentExtLib: {
|
|
105
|
+
name: 'SlasherDeploymentExtLib',
|
|
106
|
+
contractAbi: SlasherDeploymentExtLibAbi,
|
|
107
|
+
contractBytecode: SlasherDeploymentExtLibBytecode as Hex,
|
|
119
108
|
},
|
|
120
109
|
},
|
|
121
110
|
},
|
|
@@ -175,22 +164,10 @@ export const SlasherArtifact = {
|
|
|
175
164
|
contractBytecode: SlasherBytecode as Hex,
|
|
176
165
|
};
|
|
177
166
|
|
|
178
|
-
export const
|
|
179
|
-
name: '
|
|
180
|
-
contractAbi:
|
|
181
|
-
contractBytecode:
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
export const TallySlashingProposerArtifact = {
|
|
185
|
-
name: 'TallySlashingProposer',
|
|
186
|
-
contractAbi: TallySlashingProposerAbi,
|
|
187
|
-
contractBytecode: TallySlashingProposerBytecode as Hex,
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
export const SlashFactoryArtifact = {
|
|
191
|
-
name: 'SlashFactory',
|
|
192
|
-
contractAbi: SlashFactoryAbi,
|
|
193
|
-
contractBytecode: SlashFactoryBytecode as Hex,
|
|
167
|
+
export const SlashingProposerArtifact = {
|
|
168
|
+
name: 'SlashingProposer',
|
|
169
|
+
contractAbi: SlashingProposerAbi,
|
|
170
|
+
contractBytecode: SlashingProposerBytecode as Hex,
|
|
194
171
|
};
|
|
195
172
|
|
|
196
173
|
export const RegisterNewRollupVersionPayloadArtifact = {
|