@aztec/ethereum 0.0.1-commit.993d52e → 0.0.1-commit.9a89641

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 (145) hide show
  1. package/dest/client.d.ts +25 -3
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/client.js +60 -8
  4. package/dest/config.d.ts +27 -8
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +60 -13
  7. package/dest/contracts/chain_state_override.d.ts +78 -0
  8. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  9. package/dest/contracts/chain_state_override.js +137 -0
  10. package/dest/contracts/empire_base.d.ts +1 -3
  11. package/dest/contracts/empire_base.d.ts.map +1 -1
  12. package/dest/contracts/fee_asset_price_oracle.d.ts +6 -2
  13. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -1
  14. package/dest/contracts/fee_asset_price_oracle.js +11 -6
  15. package/dest/contracts/governance.d.ts +135 -35
  16. package/dest/contracts/governance.d.ts.map +1 -1
  17. package/dest/contracts/governance.js +226 -15
  18. package/dest/contracts/governance_proposer.d.ts +33 -4
  19. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  20. package/dest/contracts/governance_proposer.js +52 -13
  21. package/dest/contracts/gse.d.ts +8 -2
  22. package/dest/contracts/gse.d.ts.map +1 -1
  23. package/dest/contracts/gse.js +11 -2
  24. package/dest/contracts/inbox.d.ts +3 -3
  25. package/dest/contracts/inbox.d.ts.map +1 -1
  26. package/dest/contracts/inbox.js +12 -7
  27. package/dest/contracts/index.d.ts +3 -3
  28. package/dest/contracts/index.d.ts.map +1 -1
  29. package/dest/contracts/index.js +2 -2
  30. package/dest/contracts/multicall.d.ts +130 -13
  31. package/dest/contracts/multicall.d.ts.map +1 -1
  32. package/dest/contracts/multicall.js +203 -94
  33. package/dest/contracts/outbox.d.ts +27 -4
  34. package/dest/contracts/outbox.d.ts.map +1 -1
  35. package/dest/contracts/outbox.js +34 -7
  36. package/dest/contracts/rollup.d.ts +5423 -27
  37. package/dest/contracts/rollup.d.ts.map +1 -1
  38. package/dest/contracts/rollup.js +396 -73
  39. package/dest/contracts/slashing_proposer.d.ts +170 -0
  40. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  41. package/dest/contracts/slashing_proposer.js +748 -0
  42. package/dest/deploy_aztec_l1_contracts.d.ts +18 -10
  43. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  44. package/dest/deploy_aztec_l1_contracts.js +28 -17
  45. package/dest/foundry_binary.d.ts +13 -0
  46. package/dest/foundry_binary.d.ts.map +1 -0
  47. package/dest/foundry_binary.js +52 -0
  48. package/dest/l1_artifacts.d.ts +14943 -16270
  49. package/dest/l1_artifacts.d.ts.map +1 -1
  50. package/dest/l1_artifacts.js +9 -24
  51. package/dest/l1_contract_addresses.d.ts +61 -65
  52. package/dest/l1_contract_addresses.d.ts.map +1 -1
  53. package/dest/l1_contract_addresses.js +101 -82
  54. package/dest/l1_reader.d.ts +5 -5
  55. package/dest/l1_reader.d.ts.map +1 -1
  56. package/dest/l1_reader.js +9 -7
  57. package/dest/l1_tx_utils/config.js +3 -3
  58. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +23 -4
  59. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -1
  60. package/dest/l1_tx_utils/l1_fee_analyzer.js +65 -3
  61. package/dest/l1_tx_utils/l1_tx_utils.d.ts +9 -1
  62. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  63. package/dest/l1_tx_utils/l1_tx_utils.js +138 -57
  64. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
  65. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  66. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +10 -5
  67. package/dest/l1_tx_utils/tx_delayer.d.ts +5 -3
  68. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -1
  69. package/dest/l1_tx_utils/tx_delayer.js +7 -5
  70. package/dest/l1_tx_utils/types.d.ts +27 -1
  71. package/dest/l1_tx_utils/types.d.ts.map +1 -1
  72. package/dest/l1_tx_utils/types.js +10 -0
  73. package/dest/publisher_manager.d.ts +34 -8
  74. package/dest/publisher_manager.d.ts.map +1 -1
  75. package/dest/publisher_manager.js +119 -8
  76. package/dest/queries.d.ts +13 -2
  77. package/dest/queries.d.ts.map +1 -1
  78. package/dest/queries.js +69 -4
  79. package/dest/test/blob_kzg_warmup.d.ts +19 -0
  80. package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
  81. package/dest/test/blob_kzg_warmup.js +64 -0
  82. package/dest/test/chain_monitor.d.ts +55 -4
  83. package/dest/test/chain_monitor.d.ts.map +1 -1
  84. package/dest/test/chain_monitor.js +104 -3
  85. package/dest/test/eth_cheat_codes.d.ts +37 -6
  86. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  87. package/dest/test/eth_cheat_codes.js +89 -41
  88. package/dest/test/index.d.ts +2 -1
  89. package/dest/test/index.d.ts.map +1 -1
  90. package/dest/test/index.js +1 -0
  91. package/dest/test/rollup_cheat_codes.d.ts +60 -3
  92. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  93. package/dest/test/rollup_cheat_codes.js +86 -7
  94. package/dest/test/start_anvil.d.ts +23 -3
  95. package/dest/test/start_anvil.d.ts.map +1 -1
  96. package/dest/test/start_anvil.js +182 -32
  97. package/dest/utils.d.ts +9 -2
  98. package/dest/utils.d.ts.map +1 -1
  99. package/dest/utils.js +48 -16
  100. package/package.json +6 -9
  101. package/src/client.ts +66 -2
  102. package/src/config.ts +83 -17
  103. package/src/contracts/chain_state_override.ts +200 -0
  104. package/src/contracts/empire_base.ts +0 -2
  105. package/src/contracts/fee_asset_price_oracle.ts +10 -5
  106. package/src/contracts/governance.ts +310 -10
  107. package/src/contracts/governance_proposer.ts +56 -13
  108. package/src/contracts/gse.ts +19 -2
  109. package/src/contracts/inbox.ts +13 -5
  110. package/src/contracts/index.ts +2 -2
  111. package/src/contracts/multicall.ts +247 -106
  112. package/src/contracts/outbox.ts +42 -8
  113. package/src/contracts/rollup.ts +447 -80
  114. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +91 -30
  115. package/src/deploy_aztec_l1_contracts.ts +32 -18
  116. package/src/foundry_binary.ts +57 -0
  117. package/src/l1_artifacts.ts +12 -35
  118. package/src/l1_contract_addresses.ts +120 -94
  119. package/src/l1_reader.ts +17 -10
  120. package/src/l1_tx_utils/config.ts +3 -3
  121. package/src/l1_tx_utils/l1_fee_analyzer.ts +75 -3
  122. package/src/l1_tx_utils/l1_tx_utils.ts +122 -48
  123. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +10 -5
  124. package/src/l1_tx_utils/tx_delayer.ts +16 -7
  125. package/src/l1_tx_utils/types.ts +32 -0
  126. package/src/publisher_manager.ts +143 -12
  127. package/src/queries.ts +76 -4
  128. package/src/test/blob_kzg_warmup.ts +65 -0
  129. package/src/test/chain_monitor.ts +148 -3
  130. package/src/test/eth_cheat_codes.ts +82 -45
  131. package/src/test/index.ts +1 -0
  132. package/src/test/rollup_cheat_codes.ts +115 -6
  133. package/src/test/start_anvil.ts +212 -32
  134. package/src/utils.ts +45 -18
  135. package/dest/contracts/empire_slashing_proposer.d.ts +0 -69
  136. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  137. package/dest/contracts/empire_slashing_proposer.js +0 -216
  138. package/dest/contracts/tally_slashing_proposer.d.ts +0 -140
  139. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  140. package/dest/contracts/tally_slashing_proposer.js +0 -320
  141. package/dest/generated/l1-contracts-defaults.d.ts +0 -30
  142. package/dest/generated/l1-contracts-defaults.d.ts.map +0 -1
  143. package/dest/generated/l1-contracts-defaults.js +0 -30
  144. package/src/contracts/empire_slashing_proposer.ts +0 -265
  145. package/src/generated/l1-contracts-defaults.ts +0 -32
@@ -3,11 +3,12 @@ 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';
8
9
  import { hexToBuffer } from '@aztec/foundation/string';
9
10
  import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
10
- import { TallySlashingProposerAbi } from '@aztec/l1-artifacts/TallySlashingProposerAbi';
11
+ import { SlashingProposerAbi } from '@aztec/l1-artifacts/SlashingProposerAbi';
11
12
 
12
13
  import {
13
14
  type GetContractReturnType,
@@ -19,13 +20,19 @@ import {
19
20
  } from 'viem';
20
21
 
21
22
  /**
22
- * Wrapper around the TallySlashingProposer contract that provides
23
+ * Wrapper around the SlashingProposer contract that provides
23
24
  * a TypeScript interface for interacting with the consensus-based slashing system.
24
25
  */
25
- export class TallySlashingProposerContract {
26
- private readonly contract: GetContractReturnType<typeof TallySlashingProposerAbi, ViemClient>;
26
+ export class SlashingProposerContract {
27
+ private readonly contract: GetContractReturnType<typeof SlashingProposerAbi, ViemClient>;
27
28
 
28
- public readonly type = 'tally' as const;
29
+ /**
30
+ * Slash target validators of the last round asked for. Safe to cache because a round's targets are the committees
31
+ * of epochs that had already ended when the round opened (see SLASH_OFFSET_IN_ROUNDS), and those committees are
32
+ * sampled from validator set and randao snapshots taken before the epoch started, so they cannot change while the
33
+ * round is live. The promise is cached rather than the result, so concurrent callers share one in-flight read.
34
+ */
35
+ private slashTargetValidators: { round: bigint; validators: Promise<EthAddress[]> } | undefined;
29
36
 
30
37
  constructor(
31
38
  public readonly client: ViemClient,
@@ -33,7 +40,7 @@ export class TallySlashingProposerContract {
33
40
  ) {
34
41
  this.contract = getContract({
35
42
  address: typeof address === 'string' ? address : address.toString(),
36
- abi: TallySlashingProposerAbi,
43
+ abi: SlashingProposerAbi,
37
44
  client,
38
45
  });
39
46
  }
@@ -66,6 +73,8 @@ export class TallySlashingProposerContract {
66
73
  return this.contract.read.EXECUTION_DELAY_IN_ROUNDS();
67
74
  }
68
75
 
76
+ /** Returns the slash amounts for the three slash unit levels. Immutable on the contract, so memoized. */
77
+ @memoize
69
78
  public getSlashingAmounts(): Promise<[bigint, bigint, bigint]> {
70
79
  return Promise.all([
71
80
  this.contract.read.SLASH_AMOUNT_SMALL(),
@@ -136,12 +145,12 @@ export class TallySlashingProposerContract {
136
145
 
137
146
  /** Tries to extract a VoteCast event from the given logs. */
138
147
  public tryExtractVoteCastEvent(logs: Log[]) {
139
- return tryExtractEvent(logs, this.address.toString(), TallySlashingProposerAbi, 'VoteCast');
148
+ return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'VoteCast');
140
149
  }
141
150
 
142
151
  /** Tries to extract a RoundExecuted event from the given logs. */
143
152
  public tryExtractRoundExecutedEvent(logs: Log[]) {
144
- return tryExtractEvent(logs, this.address.toString(), TallySlashingProposerAbi, 'RoundExecuted');
153
+ return tryExtractEvent(logs, this.address.toString(), SlashingProposerAbi, 'RoundExecuted');
145
154
  }
146
155
 
147
156
  /**
@@ -161,9 +170,9 @@ export class TallySlashingProposerContract {
161
170
 
162
171
  return {
163
172
  to: this.contract.address,
164
- abi: TallySlashingProposerAbi,
173
+ abi: SlashingProposerAbi,
165
174
  data: encodeFunctionData({
166
- abi: TallySlashingProposerAbi,
175
+ abi: SlashingProposerAbi,
167
176
  functionName: 'vote',
168
177
  args: [votes, signature.toViemSignature()],
169
178
  }),
@@ -173,7 +182,7 @@ export class TallySlashingProposerContract {
173
182
  /** Returns the typed data definition to EIP712-sign for voting */
174
183
  public buildVoteTypedData(votes: Hex, slot: SlotNumber): TypedDataDefinition {
175
184
  const domain = {
176
- name: 'TallySlashingProposer',
185
+ name: 'SlashingProposer',
177
186
  version: '1',
178
187
  chainId: this.client.chain.id,
179
188
  verifyingContract: this.contract.address,
@@ -209,9 +218,9 @@ export class TallySlashingProposerContract {
209
218
  public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
210
219
  return {
211
220
  to: this.contract.address,
212
- abi: TallySlashingProposerAbi,
221
+ abi: SlashingProposerAbi,
213
222
  data: encodeFunctionData({
214
- abi: TallySlashingProposerAbi,
223
+ abi: SlashingProposerAbi,
215
224
  functionName: 'vote',
216
225
  args: [votes, signature],
217
226
  }),
@@ -227,44 +236,79 @@ export class TallySlashingProposerContract {
227
236
  public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
228
237
  return {
229
238
  to: this.contract.address,
230
- abi: mergeAbis([TallySlashingProposerAbi, SlasherAbi]),
239
+ abi: mergeAbis([SlashingProposerAbi, SlasherAbi]),
231
240
  data: encodeFunctionData({
232
- abi: TallySlashingProposerAbi,
241
+ abi: SlashingProposerAbi,
233
242
  functionName: 'executeRound',
234
243
  args: [round, committees.map(c => c.map(addr => addr.toString()))],
235
244
  }),
236
245
  };
237
246
  }
238
247
 
248
+ /**
249
+ * Returns the validators eligible to be voted against in a round, in the order votes encode them. Cached for the
250
+ * last round asked for: reading them runs a committee sampling simulation on L1, and every vote of a round decodes
251
+ * against the same list, so a caller walking a round's votes would otherwise repeat that call per vote. Only the
252
+ * last round is kept since callers move forward round by round.
253
+ */
254
+ public getSlashTargetValidators(round: bigint): Promise<EthAddress[]> {
255
+ const cached = this.slashTargetValidators;
256
+ if (cached?.round === round) {
257
+ return cached.validators;
258
+ }
259
+
260
+ const entry = { round, validators: this.fetchSlashTargetValidators(round) };
261
+ this.slashTargetValidators = entry;
262
+ // A failed read must not stay cached, or every later vote of the round would replay the same rejection
263
+ entry.validators.catch(() => {
264
+ if (this.slashTargetValidators === entry) {
265
+ this.slashTargetValidators = undefined;
266
+ }
267
+ });
268
+ return entry.validators;
269
+ }
270
+
271
+ private async fetchSlashTargetValidators(round: bigint): Promise<EthAddress[]> {
272
+ const { result } = await this.contract.simulate.getSlashTargetCommittees([round]);
273
+ return result.flat().map(validator => EthAddress.fromString(validator));
274
+ }
275
+
276
+ /**
277
+ * Returns the slash amount voted for each target validator by a single vote of a round.
278
+ * @param index - Position of the vote within the round, from 0 (inclusive) to the round's vote count (exclusive)
279
+ */
280
+ public async getVoteAt(round: bigint, index: bigint): Promise<SlashVoteTarget[]> {
281
+ const [validators, vote, slashAmounts] = await Promise.all([
282
+ this.getSlashTargetValidators(round),
283
+ this.contract.read.getVotes([round, index]),
284
+ this.getSlashingAmounts(),
285
+ ]);
286
+ return decodeVote(vote, validators, slashAmounts);
287
+ }
288
+
239
289
  /** Returns the last vote emitted for a given round */
240
290
  public async getLastVote(round: bigint) {
241
291
  const { voteCount } = await this.getRound(round);
242
- const validators = (await this.contract.simulate.getSlashTargetCommittees([round])).result.flat();
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);
292
+ return await this.getVoteAt(round, voteCount - 1n);
252
293
  }
253
294
 
254
295
  /**
255
296
  * Listen for VoteCast events
256
- * @param callback - Callback function to handle vote cast events
297
+ * @param callback - Callback receiving the round, the vote's index within the round as accepted by `getVoteAt`,
298
+ * and the proposer that cast it
257
299
  * @returns Unwatch function
258
300
  */
259
- public listenToVoteCast(callback: (args: { round: bigint; proposer: string }) => void): () => void {
301
+ public listenToVoteCast(
302
+ callback: (args: { round: bigint; voteIndex: bigint; proposer: string }) => void,
303
+ ): () => void {
260
304
  return this.contract.watchEvent.VoteCast(
261
305
  {},
262
306
  {
263
307
  onLogs: logs => {
264
308
  for (const log of logs) {
265
- const { round, proposer } = log.args;
266
- if (round !== undefined && proposer) {
267
- callback({ round, proposer });
309
+ const { round, voteIndex, proposer } = log.args;
310
+ if (round !== undefined && voteIndex !== undefined && proposer) {
311
+ callback({ round, voteIndex, proposer });
268
312
  }
269
313
  }
270
314
  },
@@ -296,6 +340,23 @@ export class TallySlashingProposerContract {
296
340
  }
297
341
  }
298
342
 
343
+ /**
344
+ * A validator targeted by a slashing vote, with the amount voted. The position is the validator's index in the
345
+ * round's flattened slash target committees — the unit the contract tallies quorum by. A validator sitting in
346
+ * several of the round's committees holds several positions, each with its own tally.
347
+ */
348
+ export type SlashVoteTarget = { validator: EthAddress; slashAmount: bigint; position: number };
349
+
350
+ function decodeVote(vote: Hex, validators: EthAddress[], slashAmounts: [bigint, bigint, bigint]): SlashVoteTarget[] {
351
+ return decodeSlashConsensusVotes(hexToBuffer(vote))
352
+ .map((units, position) => ({
353
+ validator: validators[position],
354
+ slashAmount: slashAmounts[units - 1] ?? 0n,
355
+ position,
356
+ }))
357
+ .filter(v => v.slashAmount > 0n);
358
+ }
359
+
299
360
  /**
300
361
  * Decodes a Buffer containing slash votes back into an array of numbers.
301
362
  * Each vote is represented as a 2-bit value (0, 1, 2, or 3) representing slashing units.
@@ -5,12 +5,12 @@ import { jsonStringify } from '@aztec/foundation/json-rpc';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
6
  import { promiseWithResolvers } from '@aztec/foundation/promise';
7
7
  import type { Fr } from '@aztec/foundation/schemas';
8
- import { fileURLToPath } from '@aztec/foundation/url';
9
8
 
10
9
  import { bn254 } from '@noble/curves/bn254';
11
10
  import type { Abi, Narrow } from 'abitype';
12
11
  import { spawn } from 'child_process';
13
12
  import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs';
13
+ import { createRequire } from 'node:module';
14
14
  import { tmpdir } from 'os';
15
15
  import { dirname, join, resolve } from 'path';
16
16
  import readline from 'readline';
@@ -19,14 +19,17 @@ 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 { L1ContractsConfig } from './config.js';
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
 
28
29
  const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
29
30
 
31
+ const require = createRequire(import.meta.url);
32
+
30
33
  const JSON_DEPLOY_RESULT_PREFIX = 'JSON DEPLOY RESULT:';
31
34
 
32
35
  /**
@@ -93,11 +96,16 @@ function runProcess<T>(
93
96
 
94
97
  // Covers an edge where where we may have a cached BlobLib that is not meant for production.
95
98
  // Despite the profile apparently sometimes cached code remains (so says Lasse after his ignition-monorepo arc).
96
- async function maybeForgeForceProductionBuild(l1ContractsPath: string, script: string, chainId: number) {
99
+ async function maybeForgeForceProductionBuild(
100
+ forgeBin: string,
101
+ l1ContractsPath: string,
102
+ script: string,
103
+ chainId: number,
104
+ ) {
97
105
  if (chainId === mainnet.id) {
98
106
  logger.info(`Recompiling ${script} with production profile for mainnet deployment`);
99
107
  logger.info('This may take a minute but ensures production BlobLib is used.');
100
- await runProcess('forge', ['build', script, '--force'], { FOUNDRY_PROFILE: 'production' }, l1ContractsPath);
108
+ await runProcess(forgeBin, ['build', script, '--force'], { FOUNDRY_PROFILE: 'production' }, l1ContractsPath);
101
109
  }
102
110
  }
103
111
 
@@ -123,15 +131,13 @@ export interface ValidatorJson {
123
131
  }
124
132
 
125
133
  /**
126
- * Gets the path to the l1-contracts foundry artifacts directory.
127
- * These are copied from l1-contracts to yarn-project/l1-artifacts/l1-contracts
128
- * during build to make yarn-project self-contained.
134
+ * Gets the path to the l1-contracts foundry artifacts directory bundled inside @aztec/l1-artifacts.
135
+ * Resolved through the package (its "." export -> dest/index.js) so it works whether the package is
136
+ * linked via portal (monorepo) or installed under node_modules (published npm) — resolution follows
137
+ * the symlink in the portal case. The bundled foundry subtree sits alongside dest/, at <pkg>/l1-contracts.
129
138
  */
130
139
  export function getL1ContractsPath(): string {
131
- const currentDir = dirname(fileURLToPath(import.meta.url));
132
- // Go up from yarn-project/ethereum/dest to yarn-project, then to l1-artifacts/l1-contracts
133
- const l1ContractsPath = resolve(currentDir, '..', '..', 'l1-artifacts', 'l1-contracts');
134
- return l1ContractsPath;
140
+ return resolve(dirname(require.resolve('@aztec/l1-artifacts')), '..', 'l1-contracts');
135
141
  }
136
142
 
137
143
  // Cached deployment directory
@@ -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,13 +592,18 @@ 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
- AZTEC_SLASHER_FLAVOR: args.slasherFlavor,
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(),
592
599
  AZTEC_SLASH_AMOUNT_SMALL: args.slashAmountSmall.toString(),
593
600
  AZTEC_SLASH_AMOUNT_MEDIUM: args.slashAmountMedium.toString(),
594
601
  AZTEC_SLASH_AMOUNT_LARGE: args.slashAmountLarge.toString(),
602
+ AZTEC_ENTRY_QUEUE_BOOTSTRAP_VALIDATOR_SET_SIZE: args.entryQueueBootstrapValidatorSetSize.toString(),
603
+ AZTEC_ENTRY_QUEUE_BOOTSTRAP_FLUSH_SIZE: args.entryQueueBootstrapFlushSize.toString(),
604
+ AZTEC_ENTRY_QUEUE_FLUSH_SIZE_MIN: args.entryQueueFlushSizeMin.toString(),
605
+ AZTEC_ENTRY_QUEUE_FLUSH_SIZE_QUOTIENT: args.entryQueueFlushSizeQuotient.toString(),
606
+ AZTEC_ENTRY_QUEUE_MAX_FLUSH_SIZE: args.entryQueueMaxFlushSize.toString(),
595
607
  } as const;
596
608
  }
597
609
 
@@ -615,12 +627,15 @@ export const deployRollupForUpgrade = async (
615
627
  // Use foundry-artifacts from l1-artifacts package
616
628
  const l1ContractsPath = prepareL1ContractsForDeployment();
617
629
 
630
+ const forgeBin = resolveFoundryBinary('forge');
618
631
  const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
619
- await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
632
+ await maybeForgeForceProductionBuild(forgeBin, l1ContractsPath, FORGE_SCRIPT, chainId);
620
633
 
621
634
  const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
622
635
  const forgeArgs = [FORGE_SCRIPT, '--sig', 'run()', '--private-key', privateKey, '--rpc-url', rpcUrl];
623
636
  const forgeEnv = {
637
+ // Resolved forge binary picked up by forge_broadcast.js, so it works without forge on PATH.
638
+ FORGE_BIN: forgeBin,
624
639
  FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
625
640
  // Env vars required by l1-contracts/script/deploy/RollupConfiguration.sol.
626
641
  REGISTRY_ADDRESS: registryAddress.toString(),
@@ -645,6 +660,5 @@ export const deployRollupForUpgrade = async (
645
660
 
646
661
  return {
647
662
  rollup,
648
- slashFactoryAddress: result.slashFactoryAddress,
649
663
  };
650
664
  };
@@ -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
+ }
@@ -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
- TallySlasherDeploymentExtLib: {
111
- name: 'TallySlasherDeploymentExtLib',
112
- contractAbi: TallySlasherDeploymentExtLibAbi,
113
- contractBytecode: TallySlasherDeploymentExtLibBytecode as Hex,
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 EmpireSlashingProposerArtifact = {
179
- name: 'EmpireSlashingProposer',
180
- contractAbi: EmpireSlashingProposerAbi,
181
- contractBytecode: EmpireSlashingProposerBytecode as Hex,
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 = {