@aztec/ethereum 0.0.1-commit.6d3c34e → 0.0.1-commit.7cf39cb55

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 (87) hide show
  1. package/dest/config.d.ts +15 -28
  2. package/dest/config.d.ts.map +1 -1
  3. package/dest/config.js +47 -56
  4. package/dest/contracts/empire_slashing_proposer.d.ts +1 -1
  5. package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
  6. package/dest/contracts/empire_slashing_proposer.js +13 -15
  7. package/dest/contracts/fee_asset_handler.d.ts +1 -1
  8. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  9. package/dest/contracts/fee_asset_handler.js +2 -0
  10. package/dest/contracts/governance.d.ts +3 -1
  11. package/dest/contracts/governance.d.ts.map +1 -1
  12. package/dest/contracts/governance.js +11 -1
  13. package/dest/contracts/governance_proposer.d.ts +1 -1
  14. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  15. package/dest/contracts/governance_proposer.js +4 -1
  16. package/dest/contracts/inbox.d.ts +18 -1
  17. package/dest/contracts/inbox.d.ts.map +1 -1
  18. package/dest/contracts/inbox.js +32 -1
  19. package/dest/contracts/index.d.ts +2 -1
  20. package/dest/contracts/index.d.ts.map +1 -1
  21. package/dest/contracts/index.js +1 -0
  22. package/dest/contracts/log.d.ts +13 -0
  23. package/dest/contracts/log.d.ts.map +1 -0
  24. package/dest/contracts/log.js +1 -0
  25. package/dest/contracts/multicall.d.ts +1 -1
  26. package/dest/contracts/multicall.d.ts.map +1 -1
  27. package/dest/contracts/multicall.js +2 -1
  28. package/dest/contracts/rollup.d.ts +35 -3
  29. package/dest/contracts/rollup.d.ts.map +1 -1
  30. package/dest/contracts/rollup.js +76 -6
  31. package/dest/contracts/tally_slashing_proposer.d.ts +1 -1
  32. package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
  33. package/dest/contracts/tally_slashing_proposer.js +8 -1
  34. package/dest/deploy_aztec_l1_contracts.d.ts +6 -1
  35. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  36. package/dest/deploy_aztec_l1_contracts.js +56 -28
  37. package/dest/generated/l1-contracts-defaults.d.ts +30 -0
  38. package/dest/generated/l1-contracts-defaults.d.ts.map +1 -0
  39. package/dest/generated/l1-contracts-defaults.js +30 -0
  40. package/dest/l1_artifacts.d.ts +1394 -471
  41. package/dest/l1_artifacts.d.ts.map +1 -1
  42. package/dest/l1_tx_utils/constants.d.ts +1 -1
  43. package/dest/l1_tx_utils/constants.js +2 -2
  44. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +1 -1
  45. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +1 -1
  46. package/dest/l1_tx_utils/l1_tx_utils.js +6 -6
  47. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +3 -3
  48. package/dest/publisher_manager.d.ts +3 -2
  49. package/dest/publisher_manager.d.ts.map +1 -1
  50. package/dest/publisher_manager.js +2 -2
  51. package/dest/queries.d.ts +2 -2
  52. package/dest/queries.d.ts.map +1 -1
  53. package/dest/queries.js +4 -1
  54. package/dest/test/eth_cheat_codes.d.ts +13 -1
  55. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  56. package/dest/test/rollup_cheat_codes.d.ts +4 -2
  57. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  58. package/dest/test/rollup_cheat_codes.js +10 -1
  59. package/dest/test/start_anvil.js +1 -1
  60. package/dest/utils.d.ts +2 -1
  61. package/dest/utils.d.ts.map +1 -1
  62. package/dest/utils.js +46 -0
  63. package/package.json +8 -7
  64. package/src/config.ts +56 -55
  65. package/src/contracts/README.md +157 -0
  66. package/src/contracts/empire_slashing_proposer.ts +16 -27
  67. package/src/contracts/fee_asset_handler.ts +2 -0
  68. package/src/contracts/governance.ts +10 -1
  69. package/src/contracts/governance_proposer.ts +4 -1
  70. package/src/contracts/inbox.ts +48 -1
  71. package/src/contracts/index.ts +1 -0
  72. package/src/contracts/log.ts +13 -0
  73. package/src/contracts/multicall.ts +5 -2
  74. package/src/contracts/rollup.ts +111 -11
  75. package/src/contracts/tally_slashing_proposer.ts +5 -1
  76. package/src/deploy_aztec_l1_contracts.ts +61 -33
  77. package/src/generated/l1-contracts-defaults.ts +32 -0
  78. package/src/l1_tx_utils/constants.ts +2 -2
  79. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +1 -1
  80. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +1 -1
  81. package/src/l1_tx_utils/l1_tx_utils.ts +6 -6
  82. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +3 -3
  83. package/src/publisher_manager.ts +4 -2
  84. package/src/queries.ts +3 -1
  85. package/src/test/rollup_cheat_codes.ts +11 -2
  86. package/src/test/start_anvil.ts +1 -1
  87. package/src/utils.ts +53 -0
@@ -1,11 +1,12 @@
1
1
  import type { L1TxRequest } from '@aztec/ethereum/l1-tx-utils';
2
2
  import type { ViemClient } from '@aztec/ethereum/types';
3
- import { tryExtractEvent } from '@aztec/ethereum/utils';
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
6
  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
+ import { SlasherAbi } from '@aztec/l1-artifacts/SlasherAbi';
9
10
  import { TallySlashingProposerAbi } from '@aztec/l1-artifacts/TallySlashingProposerAbi';
10
11
 
11
12
  import {
@@ -160,6 +161,7 @@ export class TallySlashingProposerContract {
160
161
 
161
162
  return {
162
163
  to: this.contract.address,
164
+ abi: TallySlashingProposerAbi,
163
165
  data: encodeFunctionData({
164
166
  abi: TallySlashingProposerAbi,
165
167
  functionName: 'vote',
@@ -207,6 +209,7 @@ export class TallySlashingProposerContract {
207
209
  public buildVoteRequestWithSignature(votes: Hex, signature: { v: number; r: Hex; s: Hex }): L1TxRequest {
208
210
  return {
209
211
  to: this.contract.address,
212
+ abi: TallySlashingProposerAbi,
210
213
  data: encodeFunctionData({
211
214
  abi: TallySlashingProposerAbi,
212
215
  functionName: 'vote',
@@ -224,6 +227,7 @@ export class TallySlashingProposerContract {
224
227
  public buildExecuteRoundRequest(round: bigint, committees: EthAddress[][]): L1TxRequest {
225
228
  return {
226
229
  to: this.contract.address,
230
+ abi: mergeAbis([TallySlashingProposerAbi, SlasherAbi]),
227
231
  data: encodeFunctionData({
228
232
  abi: TallySlashingProposerAbi,
229
233
  functionName: 'executeRound',
@@ -10,12 +10,12 @@ import { fileURLToPath } from '@aztec/foundation/url';
10
10
  import { bn254 } from '@noble/curves/bn254';
11
11
  import type { Abi, Narrow } from 'abitype';
12
12
  import { spawn } from 'child_process';
13
- import { cpSync, existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync } from 'fs';
13
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs';
14
14
  import { tmpdir } from 'os';
15
15
  import { dirname, join, resolve } from 'path';
16
16
  import readline from 'readline';
17
17
  import type { Hex } from 'viem';
18
- import { foundry, mainnet, sepolia } from 'viem/chains';
18
+ import { mainnet, sepolia } from 'viem/chains';
19
19
 
20
20
  import { createEthereumChain, isAnvilTestChain } from './chain.js';
21
21
  import { createExtendedL1Client } from './client.js';
@@ -31,9 +31,9 @@ const logger = createLogger('ethereum:deploy_aztec_l1_contracts');
31
31
  const JSON_DEPLOY_RESULT_PREFIX = 'JSON DEPLOY RESULT:';
32
32
 
33
33
  /**
34
- * Runs a process with the given command, arguments, and environment.
35
- * If the process outputs a line starting with JSON_DEPLOY_RESULT_PREFIX,
36
- * the JSON is parsed and returned.
34
+ * Runs a process and parses JSON deploy results from stdout.
35
+ * Lines starting with JSON_DEPLOY_RESULT_PREFIX are parsed and returned.
36
+ * All other stdout goes to logger.info, stderr goes to logger.warn.
37
37
  */
38
38
  function runProcess<T>(
39
39
  command: string,
@@ -49,26 +49,41 @@ function runProcess<T>(
49
49
  });
50
50
 
51
51
  let result: T | undefined;
52
+ let parseError: Error | undefined;
53
+ let settled = false;
52
54
 
53
55
  readline.createInterface({ input: proc.stdout }).on('line', line => {
54
56
  const trimmedLine = line.trim();
55
57
  if (trimmedLine.startsWith(JSON_DEPLOY_RESULT_PREFIX)) {
56
58
  const jsonStr = trimmedLine.slice(JSON_DEPLOY_RESULT_PREFIX.length).trim();
57
- // TODO(AD): should this be a zod parse?
58
- result = JSON.parse(jsonStr);
59
+ try {
60
+ result = JSON.parse(jsonStr);
61
+ } catch {
62
+ parseError = new Error(`Failed to parse deploy result JSON: ${jsonStr.slice(0, 200)}`);
63
+ }
59
64
  } else {
60
65
  logger.info(line);
61
66
  }
62
67
  });
63
- readline.createInterface({ input: proc.stderr }).on('line', logger.error.bind(logger));
68
+ readline.createInterface({ input: proc.stderr }).on('line', logger.warn.bind(logger));
64
69
 
65
70
  proc.on('error', error => {
71
+ if (settled) {
72
+ return;
73
+ }
74
+ settled = true;
66
75
  reject(new Error(`Failed to spawn ${command}: ${error.message}`));
67
76
  });
68
77
 
69
78
  proc.on('close', code => {
79
+ if (settled) {
80
+ return;
81
+ }
82
+ settled = true;
70
83
  if (code !== 0) {
71
- reject(new Error(`${command} exited with code ${code}. See logs for details.\n`));
84
+ reject(new Error(`${command} exited with code ${code}`));
85
+ } else if (parseError) {
86
+ reject(parseError);
72
87
  } else {
73
88
  resolve(result);
74
89
  }
@@ -141,11 +156,14 @@ function cleanupDeployDir() {
141
156
  */
142
157
  export function prepareL1ContractsForDeployment(): string {
143
158
  if (preparedDeployDir && existsSync(preparedDeployDir)) {
159
+ logger.verbose(`Using cached deployment directory: ${preparedDeployDir}`);
144
160
  return preparedDeployDir;
145
161
  }
146
162
 
147
163
  const basePath = getL1ContractsPath();
164
+ logger.verbose(`Preparing L1 contracts from: ${basePath}`);
148
165
  const tempDir = mkdtempSync(join(tmpdir(), '.foundry-deploy-'));
166
+ logger.verbose(`Created temp directory for deployment: ${tempDir}`);
149
167
  preparedDeployDir = tempDir;
150
168
  process.on('exit', cleanupDeployDir);
151
169
 
@@ -157,13 +175,24 @@ export function prepareL1ContractsForDeployment(): string {
157
175
  cpSync(join(basePath, 'src'), join(tempDir, 'src'), copyOpts);
158
176
  cpSync(join(basePath, 'script'), join(tempDir, 'script'), copyOpts);
159
177
  cpSync(join(basePath, 'generated'), join(tempDir, 'generated'), copyOpts);
160
- cpSync(join(basePath, 'foundry.toml'), join(tempDir, 'foundry.toml'));
178
+ // Kludge: copy test/ to appease forge cache which references test/shouting.t.sol
179
+ cpSync(join(basePath, 'test'), join(tempDir, 'test'), copyOpts);
161
180
  cpSync(join(basePath, 'foundry.lock'), join(tempDir, 'foundry.lock'));
162
- for (const file of readdirSync(basePath)) {
163
- if (file.startsWith('solc-')) {
164
- cpSync(join(basePath, file), join(tempDir, file));
165
- }
181
+
182
+ // Update foundry.toml to use absolute path to solc binary (avoids copying to noexec tmpfs)
183
+ const foundryTomlPath = join(basePath, 'foundry.toml');
184
+ let foundryToml = readFileSync(foundryTomlPath, 'utf-8');
185
+ const solcPathMatch = foundryToml.match(/solc\s*=\s*"\.\/solc-([^"]+)"/);
186
+ // Did we find a hardcoded solc path that we need to make absolute?
187
+ // This code path happens in CI currently as we bundle solc there to avoid race conditions when
188
+ // downloading solc.
189
+ if (solcPathMatch) {
190
+ const solcVersion = solcPathMatch[1];
191
+ const absoluteSolcPath = join(basePath, `solc-${solcVersion}`);
192
+ foundryToml = foundryToml.replace(/solc\s*=\s*"\.\/solc-[^"]+"/, `solc = "${absoluteSolcPath}"`);
193
+ logger.verbose(`Updated solc path in foundry.toml to: ${absoluteSolcPath}`);
166
194
  }
195
+ writeFileSync(join(tempDir, 'foundry.toml'), foundryToml);
167
196
 
168
197
  mkdirSync(join(tempDir, 'broadcast'));
169
198
  return tempDir;
@@ -307,11 +336,8 @@ export async function deployAztecL1Contracts(
307
336
  );
308
337
  }
309
338
 
310
- // From heuristic testing. More caused issues with anvil.
311
- const MAGIC_ANVIL_BATCH_SIZE = 12;
312
- // Anvil seems to stall with unbounded batch size. Otherwise no max batch size is desirable.
339
+ const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
313
340
  const forgeArgs = [
314
- 'script',
315
341
  FORGE_SCRIPT,
316
342
  '--sig',
317
343
  'run()',
@@ -319,8 +345,6 @@ export async function deployAztecL1Contracts(
319
345
  privateKey,
320
346
  '--rpc-url',
321
347
  rpcUrl,
322
- '--broadcast',
323
- ...(chainId === foundry.id ? ['--batch-size', MAGIC_ANVIL_BATCH_SIZE.toString()] : []),
324
348
  ...(shouldVerify ? ['--verify'] : []),
325
349
  ];
326
350
  const forgeEnv = {
@@ -329,7 +353,12 @@ export async function deployAztecL1Contracts(
329
353
  FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
330
354
  ...getDeployAztecL1ContractsEnvVars(args),
331
355
  };
332
- const result = await runProcess<ForgeL1ContractsDeployResult>('forge', forgeArgs, forgeEnv, l1ContractsPath);
356
+ const result = await runProcess<ForgeL1ContractsDeployResult>(
357
+ process.execPath,
358
+ [scriptPath, ...forgeArgs],
359
+ forgeEnv,
360
+ l1ContractsPath,
361
+ );
333
362
  if (!result) {
334
363
  throw new Error('Forge script did not output deployment result');
335
364
  }
@@ -497,6 +526,7 @@ export function getDeployAztecL1ContractsEnvVars(args: DeployAztecL1ContractsArg
497
526
  AZTEC_EJECTION_THRESHOLD: args.ejectionThreshold?.toString(),
498
527
  AZTEC_GOVERNANCE_PROPOSER_ROUND_SIZE: args.governanceProposerRoundSize?.toString(),
499
528
  AZTEC_GOVERNANCE_PROPOSER_QUORUM: args.governanceProposerQuorum?.toString(),
529
+ AZTEC_GOVERNANCE_VOTING_DURATION: args.governanceVotingDuration?.toString(),
500
530
  ZKPASSPORT_DOMAIN: args.zkPassportArgs?.zkPassportDomain,
501
531
  ZKPASSPORT_SCOPE: args.zkPassportArgs?.zkPassportScope,
502
532
  } as const;
@@ -531,11 +561,13 @@ export function getDeployRollupForUpgradeEnvVars(
531
561
  AZTEC_PROOF_SUBMISSION_EPOCHS: args.aztecProofSubmissionEpochs.toString(),
532
562
  AZTEC_LOCAL_EJECTION_THRESHOLD: args.localEjectionThreshold.toString(),
533
563
  AZTEC_SLASHING_LIFETIME_IN_ROUNDS: args.slashingLifetimeInRounds.toString(),
564
+ AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS: args.slashingExecutionDelayInRounds.toString(),
534
565
  AZTEC_SLASHING_VETOER: args.slashingVetoer.toString(),
535
566
  AZTEC_SLASHING_DISABLE_DURATION: args.slashingDisableDuration.toString(),
536
567
  AZTEC_MANA_TARGET: args.manaTarget.toString(),
537
568
  AZTEC_EXIT_DELAY_SECONDS: args.exitDelaySeconds.toString(),
538
569
  AZTEC_PROVING_COST_PER_MANA: args.provingCostPerMana.toString(),
570
+ AZTEC_INITIAL_ETH_PER_FEE_ASSET: args.initialEthPerFeeAsset.toString(),
539
571
  AZTEC_SLASHER_FLAVOR: args.slasherFlavor,
540
572
  AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: args.slashingRoundSizeInEpochs.toString(),
541
573
  AZTEC_SLASHING_QUORUM: args.slashingQuorum?.toString(),
@@ -569,17 +601,8 @@ export const deployRollupForUpgrade = async (
569
601
  const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
570
602
  await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
571
603
 
572
- const forgeArgs = [
573
- 'script',
574
- FORGE_SCRIPT,
575
- '--sig',
576
- 'run()',
577
- '--private-key',
578
- privateKey,
579
- '--rpc-url',
580
- rpcUrl,
581
- '--broadcast',
582
- ];
604
+ const scriptPath = join(getL1ContractsPath(), 'scripts', 'forge_broadcast.js');
605
+ const forgeArgs = [FORGE_SCRIPT, '--sig', 'run()', '--private-key', privateKey, '--rpc-url', rpcUrl];
583
606
  const forgeEnv = {
584
607
  FOUNDRY_PROFILE: chainId === mainnet.id ? 'production' : undefined,
585
608
  // Env vars required by l1-contracts/script/deploy/RollupConfiguration.sol.
@@ -588,7 +611,12 @@ export const deployRollupForUpgrade = async (
588
611
  ...getDeployRollupForUpgradeEnvVars(args),
589
612
  };
590
613
 
591
- const result = await runProcess<ForgeRollupUpgradeResult>('forge', forgeArgs, forgeEnv, l1ContractsPath);
614
+ const result = await runProcess<ForgeRollupUpgradeResult>(
615
+ process.execPath,
616
+ [scriptPath, ...forgeArgs],
617
+ forgeEnv,
618
+ l1ContractsPath,
619
+ );
592
620
  if (!result) {
593
621
  throw new Error('Forge script did not output deployment result');
594
622
  }
@@ -0,0 +1,32 @@
1
+ // Auto-generated from spartan/environments/network-defaults.yml
2
+ // Do not edit manually - run yarn generate to regenerate
3
+
4
+ /** Default L1 contracts configuration values from network-defaults.yml */
5
+ export const l1ContractsDefaultEnv = {
6
+ ETHEREUM_SLOT_DURATION: 12,
7
+ AZTEC_SLOT_DURATION: 36,
8
+ AZTEC_EPOCH_DURATION: 32,
9
+ AZTEC_TARGET_COMMITTEE_SIZE: 48,
10
+ AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET: 2,
11
+ AZTEC_LAG_IN_EPOCHS_FOR_RANDAO: 2,
12
+ AZTEC_ACTIVATION_THRESHOLD: 100000000000000000000,
13
+ AZTEC_EJECTION_THRESHOLD: 50000000000000000000,
14
+ AZTEC_LOCAL_EJECTION_THRESHOLD: 98000000000000000000,
15
+ AZTEC_EXIT_DELAY_SECONDS: 172800,
16
+ AZTEC_INBOX_LAG: 1,
17
+ AZTEC_PROOF_SUBMISSION_EPOCHS: 1,
18
+ AZTEC_MANA_TARGET: 100000000,
19
+ AZTEC_PROVING_COST_PER_MANA: 100,
20
+ AZTEC_INITIAL_ETH_PER_FEE_ASSET: 10000000,
21
+ AZTEC_SLASHER_FLAVOR: 'tally',
22
+ AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: 4,
23
+ AZTEC_SLASHING_LIFETIME_IN_ROUNDS: 5,
24
+ AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS: 0,
25
+ AZTEC_SLASHING_OFFSET_IN_ROUNDS: 2,
26
+ AZTEC_SLASHING_VETOER: '0x0000000000000000000000000000000000000000',
27
+ AZTEC_SLASHING_DISABLE_DURATION: 432000,
28
+ AZTEC_SLASH_AMOUNT_SMALL: 10000000000000000000,
29
+ AZTEC_SLASH_AMOUNT_MEDIUM: 20000000000000000000,
30
+ AZTEC_SLASH_AMOUNT_LARGE: 50000000000000000000,
31
+ AZTEC_GOVERNANCE_PROPOSER_ROUND_SIZE: 300,
32
+ } as const;
@@ -3,8 +3,8 @@
3
3
  // 1_000_000_000_000_000_000 Wei = 1 ETH
4
4
  export const WEI_CONST = 1_000_000_000n;
5
5
 
6
- // @note using this large gas limit to avoid the issue of `gas limit too low` when estimating gas in reth
7
- export const LARGE_GAS_LIMIT = 12_000_000n;
6
+ // EIP-7825: protocol-level cap on tx gas limit (2^24). Clients reject above this.
7
+ export const MAX_L1_TX_LIMIT = 16_777_216n;
8
8
 
9
9
  // setting a minimum bump percentage to 10% due to geth's implementation
10
10
  // https://github.com/ethereum/go-ethereum/blob/e3d61e6db028c412f74bc4d4c7e117a9e29d0de0/core/txpool/legacypool/list.go#L298
@@ -134,7 +134,7 @@ export const P75AllTxsPriorityFeeStrategy: PriorityFeeStrategy = {
134
134
  // Sanity check: cap competitive fee at 100x network estimate to avoid using unrealistic fees
135
135
  const maxReasonableFee = networkEstimate * 100n;
136
136
  if (competitiveFee > maxReasonableFee && networkEstimate > 0n) {
137
- logger?.warn('Competitive fee exceeds sanity cap, using capped value', {
137
+ logger?.debug('Competitive fee exceeds sanity cap, using capped value', {
138
138
  competitiveFee: formatGwei(competitiveFee),
139
139
  networkEstimate: formatGwei(networkEstimate),
140
140
  cappedTo: formatGwei(maxReasonableFee),
@@ -207,7 +207,7 @@ export const P75BlobTxsOnlyPriorityFeeStrategy: PriorityFeeStrategy = {
207
207
 
208
208
  // Debug: Log suspicious fees from history
209
209
  if (medianHistoricalFee > 100n * WEI_CONST) {
210
- logger?.warn('Suspicious high fee in history', {
210
+ logger?.debug('Suspicious high fee in history', {
211
211
  historicalMedian: formatGwei(medianHistoricalFee),
212
212
  allP75Fees: percentile75Fees.map(f => formatGwei(f)),
213
213
  });
@@ -27,7 +27,7 @@ import { jsonRpc } from 'viem/nonce';
27
27
  import type { ViemClient } from '../types.js';
28
28
  import { formatViemError } from '../utils.js';
29
29
  import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from './config.js';
30
- import { LARGE_GAS_LIMIT } from './constants.js';
30
+ import { MAX_L1_TX_LIMIT } from './constants.js';
31
31
  import type { IL1TxMetrics, IL1TxStore } from './interfaces.js';
32
32
  import { ReadOnlyL1TxUtils } from './readonly_l1_tx_utils.js';
33
33
  import {
@@ -207,7 +207,7 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
207
207
 
208
208
  let gasLimit: bigint;
209
209
  if (this.debugMaxGasLimit) {
210
- gasLimit = LARGE_GAS_LIMIT;
210
+ gasLimit = MAX_L1_TX_LIMIT;
211
211
  } else if (gasConfig.gasLimit) {
212
212
  gasLimit = gasConfig.gasLimit;
213
213
  } else {
@@ -283,7 +283,7 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
283
283
  return { txHash, state: l1TxState };
284
284
  } catch (err: any) {
285
285
  const viemError = formatViemError(err, request.abi);
286
- this.logger.error(`Failed to send L1 transaction`, viemError, {
286
+ this.logger.error(`Failed to send L1 transaction: ${viemError.message}`, viemError, {
287
287
  request: pick(request, 'to', 'value'),
288
288
  });
289
289
  throw viemError;
@@ -631,12 +631,12 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
631
631
  from: request.from ?? this.getSenderAddress().toString(),
632
632
  maxFeePerGas: gasPrice.maxFeePerGas,
633
633
  maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
634
- gas: request.gas ?? LARGE_GAS_LIMIT,
634
+ gas: request.gas ?? MAX_L1_TX_LIMIT,
635
635
  };
636
636
 
637
637
  if (!request.gas && !gasConfig.ignoreBlockGasLimit) {
638
- // LARGE_GAS_LIMIT is set as call.gas, increase block gasLimit
639
- blockOverrides.gasLimit = LARGE_GAS_LIMIT * 2n;
638
+ // MAX_L1_TX_LIMIT is set as call.gas, ensure block gasLimit is sufficient
639
+ blockOverrides.gasLimit = MAX_L1_TX_LIMIT;
640
640
  }
641
641
 
642
642
  return this._simulate(call, blockOverrides, stateOverrides, gasConfig, abi);
@@ -27,7 +27,7 @@ import type { ViemClient } from '../types.js';
27
27
  import { type L1TxUtilsConfig, defaultL1TxUtilsConfig, l1TxUtilsConfigMappings } from './config.js';
28
28
  import {
29
29
  BLOCK_TIME_MS,
30
- LARGE_GAS_LIMIT,
30
+ MAX_L1_TX_LIMIT,
31
31
  MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE,
32
32
  MIN_REPLACEMENT_BUMP_PERCENTAGE,
33
33
  WEI_CONST,
@@ -249,7 +249,7 @@ export class ReadOnlyL1TxUtils {
249
249
  ...request,
250
250
  ..._blobInputs,
251
251
  maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
252
- gas: LARGE_GAS_LIMIT,
252
+ gas: MAX_L1_TX_LIMIT,
253
253
  blockTag: 'latest',
254
254
  });
255
255
 
@@ -258,7 +258,7 @@ export class ReadOnlyL1TxUtils {
258
258
  initialEstimate = await this.client.estimateGas({
259
259
  account,
260
260
  ...request,
261
- gas: LARGE_GAS_LIMIT,
261
+ gas: MAX_L1_TX_LIMIT,
262
262
  blockTag: 'latest',
263
263
  });
264
264
  this.logger?.trace(`Estimated gas for non-blob tx: ${initialEstimate}`);
@@ -1,5 +1,5 @@
1
1
  import { pick } from '@aztec/foundation/collection';
2
- import { createLogger } from '@aztec/foundation/log';
2
+ import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
3
3
 
4
4
  import { L1TxUtils, TxUtilsState } from './l1_tx_utils/index.js';
5
5
 
@@ -28,13 +28,15 @@ const busyStates: TxUtilsState[] = [
28
28
  export type PublisherFilter<UtilsType extends L1TxUtils> = (utils: UtilsType) => boolean;
29
29
 
30
30
  export class PublisherManager<UtilsType extends L1TxUtils = L1TxUtils> {
31
- private log = createLogger('publisher:manager');
31
+ private log: Logger;
32
32
  private config: { publisherAllowInvalidStates?: boolean };
33
33
 
34
34
  constructor(
35
35
  private publishers: UtilsType[],
36
36
  config: { publisherAllowInvalidStates?: boolean },
37
+ bindings?: LoggerBindings,
37
38
  ) {
39
+ this.log = createLogger('publisher:manager', bindings);
38
40
  this.log.info(`PublisherManager initialized with ${publishers.length} publishers.`);
39
41
  this.publishers = publishers;
40
42
  this.config = pick(config, 'publisherAllowInvalidStates');
package/src/queries.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { EthAddress } from '@aztec/foundation/eth-address';
2
2
 
3
- import type { L1ContractsConfig } from './config.js';
3
+ import { DefaultL1ContractsConfig, type L1ContractsConfig } from './config.js';
4
4
  import { ReadOnlyGovernanceContract } from './contracts/governance.js';
5
5
  import { GovernanceProposerContract } from './contracts/governance_proposer.js';
6
6
  import { InboxContract } from './contracts/inbox.js';
@@ -99,6 +99,7 @@ export async function getL1ContractsConfig(
99
99
  inboxLag: Number(inboxLag),
100
100
  governanceProposerQuorum: Number(governanceProposerQuorum),
101
101
  governanceProposerRoundSize: Number(governanceProposerRoundSize),
102
+ governanceVotingDuration: DefaultL1ContractsConfig.governanceVotingDuration,
102
103
  activationThreshold,
103
104
  ejectionThreshold,
104
105
  localEjectionThreshold,
@@ -118,5 +119,6 @@ export async function getL1ContractsConfig(
118
119
  slashAmountSmall: slashingAmounts[0],
119
120
  slashAmountMedium: slashingAmounts[1],
120
121
  slashAmountLarge: slashingAmounts[2],
122
+ initialEthPerFeeAsset: DefaultL1ContractsConfig.initialEthPerFeeAsset,
121
123
  };
122
124
  }
@@ -56,6 +56,15 @@ export class RollupCheatCodes {
56
56
  return SlotNumber.fromBigInt(await this.rollup.read.getSlotAt([ts]));
57
57
  }
58
58
 
59
+ /** Returns the number of seconds until the start of the given slot based on L1 block timestamp. */
60
+ public async getSecondsUntilSlot(slot: SlotNumber): Promise<number> {
61
+ const [currentTimestamp, targetTimestamp] = await Promise.all([
62
+ this.client.getBlock().then(b => BigInt(b.timestamp)),
63
+ this.rollup.read.getTimestampForSlot([BigInt(slot)]),
64
+ ]);
65
+ return Math.max(0, Number(targetTimestamp - currentTimestamp));
66
+ }
67
+
59
68
  /** Returns the current epoch */
60
69
  public async getEpoch(): Promise<EpochNumber> {
61
70
  const slotNumber = await this.getSlot();
@@ -124,7 +133,7 @@ export class RollupCheatCodes {
124
133
  } = {},
125
134
  ) {
126
135
  const { epochDuration: slotsInEpoch } = await this.getConfig();
127
- const slotNumber = SlotNumber(epoch * Number(slotsInEpoch));
136
+ const slotNumber = SlotNumber(Number(epoch) * Number(slotsInEpoch));
128
137
  const timestamp = (await this.rollup.read.getTimestampForSlot([BigInt(slotNumber)])) + BigInt(opts.offset ?? 0);
129
138
  try {
130
139
  await this.ethCheatCodes.warp(Number(timestamp), { ...opts, silent: true, resetBlockInterval: true });
@@ -176,7 +185,7 @@ export class RollupCheatCodes {
176
185
  * Marks the specified checkpoint (or latest if none) as proven
177
186
  * @param maybeCheckpointNumber - The checkpoint number to mark as proven (defaults to latest pending)
178
187
  */
179
- public markAsProven(maybeCheckpointNumber?: number | bigint) {
188
+ public markAsProven(maybeCheckpointNumber?: CheckpointNumber) {
180
189
  return this.ethCheatCodes.execWithPausedAnvil(async () => {
181
190
  const tipsBefore = await this.getTips();
182
191
  const { pending, proven } = tipsBefore;
@@ -33,7 +33,7 @@ export async function startAnvil(
33
33
  const anvil = createAnvil({
34
34
  anvilBinary,
35
35
  host: '127.0.0.1',
36
- port: opts.port ?? 8545,
36
+ port: opts.port ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : 8545),
37
37
  blockTime: opts.l1BlockTime,
38
38
  stopTimeout: 1000,
39
39
  accounts: opts.accounts ?? 20,
package/src/utils.ts CHANGED
@@ -4,6 +4,7 @@ import { ErrorsAbi } from '@aztec/l1-artifacts/ErrorsAbi';
4
4
 
5
5
  import {
6
6
  type Abi,
7
+ type AbiItem,
7
8
  BaseError,
8
9
  type ContractEventName,
9
10
  ContractFunctionRevertedError,
@@ -14,6 +15,7 @@ import {
14
15
  decodeErrorResult,
15
16
  decodeEventLog,
16
17
  } from 'viem';
18
+ import { formatAbiItem, formatAbiParams } from 'viem/utils';
17
19
 
18
20
  export interface L2Claim {
19
21
  claimSecret: Fr;
@@ -93,6 +95,57 @@ export function prettyLogViemErrorMsg(err: any) {
93
95
  return err?.message ?? err;
94
96
  }
95
97
 
98
+ export function mergeAbis(abis: Abi[]): Abi {
99
+ let merged: Abi = [];
100
+ const seen = new Set<string>();
101
+
102
+ for (const abi of abis) {
103
+ for (const item of abi) {
104
+ const key = getAbiItemKey(item);
105
+ if (!seen.has(key)) {
106
+ seen.add(key);
107
+ merged = [...merged, item];
108
+ }
109
+ }
110
+ }
111
+
112
+ return merged;
113
+ }
114
+
115
+ function getAbiItemKey(item: AbiItem): string {
116
+ if (item.type === 'function') {
117
+ const signature = formatAbiItem(item);
118
+ const outputs = formatAbiParams(item.outputs);
119
+ const stateMutability = typeof item.stateMutability === 'string' ? item.stateMutability : '';
120
+ return `function:${signature}:${outputs}:${stateMutability}`;
121
+ }
122
+
123
+ if (item.type === 'event') {
124
+ const signature = formatAbiItem(item);
125
+ const indexed = (item.inputs ?? []).map(input => ((input as { indexed?: boolean }).indexed ? '1' : '0')).join('');
126
+ const anonymous = item.anonymous ? 'anonymous' : 'not-anonymous';
127
+ return `event:${signature}:${indexed}:${anonymous}`;
128
+ }
129
+
130
+ if (item.type === 'error') {
131
+ const signature = formatAbiItem(item);
132
+ return `error:${signature}`;
133
+ }
134
+
135
+ if (item.type === 'constructor') {
136
+ const inputs = formatAbiParams(item.inputs);
137
+ const stateMutability = typeof item.stateMutability === 'string' ? item.stateMutability : '';
138
+ return `constructor::${inputs}:${stateMutability}`;
139
+ }
140
+
141
+ if (item.type === 'fallback' || item.type === 'receive') {
142
+ const stateMutability = typeof item.stateMutability === 'string' ? item.stateMutability : '';
143
+ return `${item.type}:::${stateMutability}`;
144
+ }
145
+
146
+ return 'unknown';
147
+ }
148
+
96
149
  function getNestedErrorData(error: unknown): string | undefined {
97
150
  // If nothing, bail
98
151
  if (!error) {