@aztec/ethereum 3.0.3 → 4.0.0-devnet.1-patch.0

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 (129) hide show
  1. package/dest/client.js +6 -2
  2. package/dest/config.d.ts +17 -27
  3. package/dest/config.d.ts.map +1 -1
  4. package/dest/config.js +51 -54
  5. package/dest/contracts/empire_base.d.ts +2 -1
  6. package/dest/contracts/empire_base.d.ts.map +1 -1
  7. package/dest/contracts/empire_slashing_proposer.d.ts +2 -1
  8. package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
  9. package/dest/contracts/empire_slashing_proposer.js +22 -15
  10. package/dest/contracts/fee_asset_handler.d.ts +6 -5
  11. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  12. package/dest/contracts/fee_asset_handler.js +11 -9
  13. package/dest/contracts/governance.d.ts +3 -1
  14. package/dest/contracts/governance.d.ts.map +1 -1
  15. package/dest/contracts/governance.js +11 -1
  16. package/dest/contracts/governance_proposer.d.ts +2 -1
  17. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  18. package/dest/contracts/governance_proposer.js +395 -9
  19. package/dest/contracts/inbox.d.ts +22 -1
  20. package/dest/contracts/inbox.d.ts.map +1 -1
  21. package/dest/contracts/inbox.js +36 -1
  22. package/dest/contracts/index.d.ts +3 -1
  23. package/dest/contracts/index.d.ts.map +1 -1
  24. package/dest/contracts/index.js +2 -0
  25. package/dest/contracts/log.d.ts +13 -0
  26. package/dest/contracts/log.d.ts.map +1 -0
  27. package/dest/contracts/log.js +1 -0
  28. package/dest/contracts/multicall.d.ts +1 -1
  29. package/dest/contracts/multicall.d.ts.map +1 -1
  30. package/dest/contracts/multicall.js +2 -1
  31. package/dest/contracts/outbox.d.ts +41 -0
  32. package/dest/contracts/outbox.d.ts.map +1 -0
  33. package/dest/contracts/outbox.js +86 -0
  34. package/dest/contracts/rollup.d.ts +162 -81
  35. package/dest/contracts/rollup.d.ts.map +1 -1
  36. package/dest/contracts/rollup.js +691 -132
  37. package/dest/contracts/tally_slashing_proposer.d.ts +1 -1
  38. package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
  39. package/dest/contracts/tally_slashing_proposer.js +8 -1
  40. package/dest/deploy_aztec_l1_contracts.d.ts +17 -2
  41. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  42. package/dest/deploy_aztec_l1_contracts.js +87 -19
  43. package/dest/generated/l1-contracts-defaults.d.ts +30 -0
  44. package/dest/generated/l1-contracts-defaults.d.ts.map +1 -0
  45. package/dest/generated/l1-contracts-defaults.js +30 -0
  46. package/dest/l1_artifacts.d.ts +4964 -1573
  47. package/dest/l1_artifacts.d.ts.map +1 -1
  48. package/dest/l1_contract_addresses.d.ts +1 -1
  49. package/dest/l1_contract_addresses.d.ts.map +1 -1
  50. package/dest/l1_contract_addresses.js +3 -3
  51. package/dest/l1_tx_utils/constants.d.ts +8 -2
  52. package/dest/l1_tx_utils/constants.d.ts.map +1 -1
  53. package/dest/l1_tx_utils/constants.js +27 -2
  54. package/dest/l1_tx_utils/fee-strategies/index.d.ts +10 -0
  55. package/dest/l1_tx_utils/fee-strategies/index.d.ts.map +1 -0
  56. package/dest/l1_tx_utils/fee-strategies/index.js +12 -0
  57. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +8 -0
  58. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -0
  59. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +129 -0
  60. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts +23 -0
  61. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts.map +1 -0
  62. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +191 -0
  63. package/dest/l1_tx_utils/fee-strategies/types.d.ts +51 -0
  64. package/dest/l1_tx_utils/fee-strategies/types.d.ts.map +1 -0
  65. package/dest/l1_tx_utils/fee-strategies/types.js +3 -0
  66. package/dest/l1_tx_utils/index.d.ts +3 -1
  67. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  68. package/dest/l1_tx_utils/index.js +2 -0
  69. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +233 -0
  70. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -0
  71. package/dest/l1_tx_utils/l1_fee_analyzer.js +506 -0
  72. package/dest/l1_tx_utils/l1_tx_utils.js +6 -6
  73. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +4 -15
  74. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  75. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +35 -143
  76. package/dest/publisher_manager.d.ts +3 -2
  77. package/dest/publisher_manager.d.ts.map +1 -1
  78. package/dest/publisher_manager.js +2 -2
  79. package/dest/queries.d.ts +2 -2
  80. package/dest/queries.d.ts.map +1 -1
  81. package/dest/queries.js +12 -4
  82. package/dest/test/chain_monitor.js +1 -2
  83. package/dest/test/eth_cheat_codes.d.ts +13 -1
  84. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  85. package/dest/test/eth_cheat_codes.js +3 -1
  86. package/dest/test/rollup_cheat_codes.d.ts +5 -2
  87. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  88. package/dest/test/rollup_cheat_codes.js +22 -3
  89. package/dest/test/start_anvil.js +1 -1
  90. package/dest/test/tx_delayer.js +1 -1
  91. package/dest/utils.d.ts +15 -2
  92. package/dest/utils.d.ts.map +1 -1
  93. package/dest/utils.js +64 -0
  94. package/package.json +9 -7
  95. package/src/client.ts +2 -2
  96. package/src/config.ts +62 -53
  97. package/src/contracts/README.md +157 -0
  98. package/src/contracts/empire_base.ts +1 -1
  99. package/src/contracts/empire_slashing_proposer.ts +22 -28
  100. package/src/contracts/fee_asset_handler.ts +10 -7
  101. package/src/contracts/governance.ts +10 -1
  102. package/src/contracts/governance_proposer.ts +10 -2
  103. package/src/contracts/inbox.ts +53 -1
  104. package/src/contracts/index.ts +2 -0
  105. package/src/contracts/log.ts +13 -0
  106. package/src/contracts/multicall.ts +5 -2
  107. package/src/contracts/outbox.ts +98 -0
  108. package/src/contracts/rollup.ts +364 -100
  109. package/src/contracts/tally_slashing_proposer.ts +5 -1
  110. package/src/deploy_aztec_l1_contracts.ts +94 -18
  111. package/src/generated/l1-contracts-defaults.ts +32 -0
  112. package/src/l1_contract_addresses.ts +22 -20
  113. package/src/l1_tx_utils/constants.ts +13 -2
  114. package/src/l1_tx_utils/fee-strategies/index.ts +22 -0
  115. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +163 -0
  116. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +245 -0
  117. package/src/l1_tx_utils/fee-strategies/types.ts +56 -0
  118. package/src/l1_tx_utils/index.ts +2 -0
  119. package/src/l1_tx_utils/l1_fee_analyzer.ts +803 -0
  120. package/src/l1_tx_utils/l1_tx_utils.ts +6 -6
  121. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +48 -186
  122. package/src/publisher_manager.ts +4 -2
  123. package/src/queries.ts +11 -3
  124. package/src/test/chain_monitor.ts +1 -1
  125. package/src/test/eth_cheat_codes.ts +1 -1
  126. package/src/test/rollup_cheat_codes.ts +22 -4
  127. package/src/test/start_anvil.ts +1 -1
  128. package/src/test/tx_delayer.ts +1 -1
  129. package/src/utils.ts +82 -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,10 +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 { dirname, resolve } from 'path';
13
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs';
14
+ import { tmpdir } from 'os';
15
+ import { dirname, join, resolve } from 'path';
14
16
  import readline from 'readline';
15
17
  import type { Hex } from 'viem';
16
- import { foundry, mainnet, sepolia } from 'viem/chains';
18
+ import { mainnet, sepolia } from 'viem/chains';
17
19
 
18
20
  import { createEthereumChain, isAnvilTestChain } from './chain.js';
19
21
  import { createExtendedL1Client } from './client.js';
@@ -107,17 +109,80 @@ export interface ValidatorJson {
107
109
  }
108
110
 
109
111
  /**
110
- * Gets the path to the l1-contracts directory.
112
+ * Gets the path to the l1-contracts foundry artifacts directory.
113
+ * These are copied from l1-contracts to yarn-project/l1-artifacts/l1-contracts
114
+ * during build to make yarn-project self-contained.
111
115
  */
112
116
  export function getL1ContractsPath(): string {
113
- // Try to find l1-contracts relative to this file
114
117
  const currentDir = dirname(fileURLToPath(import.meta.url));
115
-
116
- // Go up from yarn-project/ethereum/src to yarn-project, then to repo root, then to l1-contracts
117
- const l1ContractsPath = resolve(currentDir, '..', '..', '..', 'l1-contracts');
118
+ // Go up from yarn-project/ethereum/dest to yarn-project, then to l1-artifacts/l1-contracts
119
+ const l1ContractsPath = resolve(currentDir, '..', '..', 'l1-artifacts', 'l1-contracts');
118
120
  return l1ContractsPath;
119
121
  }
120
122
 
123
+ // Cached deployment directory
124
+ let preparedDeployDir: string | undefined;
125
+
126
+ function cleanupDeployDir() {
127
+ if (preparedDeployDir) {
128
+ try {
129
+ rmSync(preparedDeployDir, { recursive: true, force: true });
130
+ } catch {
131
+ // ignore cleanup errors
132
+ }
133
+ preparedDeployDir = undefined;
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Prepares a temp directory for forge deployment.
139
+ * Copies all artifacts with preserved timestamps (required for forge cache validity).
140
+ * A fresh broadcast/ directory is created for deployment outputs.
141
+ */
142
+ export function prepareL1ContractsForDeployment(): string {
143
+ if (preparedDeployDir && existsSync(preparedDeployDir)) {
144
+ logger.verbose(`Using cached deployment directory: ${preparedDeployDir}`);
145
+ return preparedDeployDir;
146
+ }
147
+
148
+ const basePath = getL1ContractsPath();
149
+ logger.verbose(`Preparing L1 contracts from: ${basePath}`);
150
+ const tempDir = mkdtempSync(join(tmpdir(), '.foundry-deploy-'));
151
+ logger.verbose(`Created temp directory for deployment: ${tempDir}`);
152
+ preparedDeployDir = tempDir;
153
+ process.on('exit', cleanupDeployDir);
154
+
155
+ // Copy all dirs with preserved timestamps (required for forge cache validity)
156
+ const copyOpts = { recursive: true, preserveTimestamps: true };
157
+ cpSync(join(basePath, 'out'), join(tempDir, 'out'), copyOpts);
158
+ cpSync(join(basePath, 'lib'), join(tempDir, 'lib'), copyOpts);
159
+ cpSync(join(basePath, 'cache'), join(tempDir, 'cache'), copyOpts);
160
+ cpSync(join(basePath, 'src'), join(tempDir, 'src'), copyOpts);
161
+ cpSync(join(basePath, 'script'), join(tempDir, 'script'), copyOpts);
162
+ cpSync(join(basePath, 'generated'), join(tempDir, 'generated'), copyOpts);
163
+ // Kludge: copy test/ to appease forge cache which references test/shouting.t.sol
164
+ cpSync(join(basePath, 'test'), join(tempDir, 'test'), copyOpts);
165
+ cpSync(join(basePath, 'foundry.lock'), join(tempDir, 'foundry.lock'));
166
+
167
+ // Update foundry.toml to use absolute path to solc binary (avoids copying to noexec tmpfs)
168
+ const foundryTomlPath = join(basePath, 'foundry.toml');
169
+ let foundryToml = readFileSync(foundryTomlPath, 'utf-8');
170
+ const solcPathMatch = foundryToml.match(/solc\s*=\s*"\.\/solc-([^"]+)"/);
171
+ // Did we find a hardcoded solc path that we need to make absolute?
172
+ // This code path happens in CI currently as we bundle solc there to avoid race conditions when
173
+ // downloading solc.
174
+ if (solcPathMatch) {
175
+ const solcVersion = solcPathMatch[1];
176
+ const absoluteSolcPath = join(basePath, `solc-${solcVersion}`);
177
+ foundryToml = foundryToml.replace(/solc\s*=\s*"\.\/solc-[^"]+"/, `solc = "${absoluteSolcPath}"`);
178
+ logger.verbose(`Updated solc path in foundry.toml to: ${absoluteSolcPath}`);
179
+ }
180
+ writeFileSync(join(tempDir, 'foundry.toml'), foundryToml);
181
+
182
+ mkdirSync(join(tempDir, 'broadcast'));
183
+ return tempDir;
184
+ }
185
+
121
186
  /**
122
187
  * Computes the validator data for passing to Solidity.
123
188
  * Only computes the G2 public key (which requires scalar multiplication on G2, not available in EVM).
@@ -211,7 +276,6 @@ export async function deployAztecL1Contracts(
211
276
  'Initial validator funding requires minting tokens, which is not possible with an external token.',
212
277
  );
213
278
  }
214
- const currentDir = dirname(fileURLToPath(import.meta.url));
215
279
  const chain = createEthereumChain([rpcUrl], chainId);
216
280
 
217
281
  const l1Client = createExtendedL1Client([rpcUrl], privateKey, chain.chainInfo);
@@ -240,17 +304,26 @@ export async function deployAztecL1Contracts(
240
304
  }
241
305
  }
242
306
 
243
- // Relative location of l1-contracts in monorepo or docker image.
244
- const l1ContractsPath = resolve(currentDir, '..', '..', '..', 'l1-contracts');
307
+ // Use foundry-artifacts from l1-artifacts package
308
+ const l1ContractsPath = prepareL1ContractsForDeployment();
245
309
 
246
310
  const FORGE_SCRIPT = 'script/deploy/DeployAztecL1Contracts.s.sol';
247
311
  await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
248
312
 
249
- const shouldVerify = chainId === mainnet.id || chainId === sepolia.id;
313
+ // Verify contracts on Etherscan when on mainnet/sepolia and ETHERSCAN_API_KEY is available.
314
+ const isVerifiableChain = chainId === mainnet.id || chainId === sepolia.id;
315
+ const shouldVerify = isVerifiableChain && !!process.env.ETHERSCAN_API_KEY;
316
+
317
+ if (isVerifiableChain && !process.env.ETHERSCAN_API_KEY) {
318
+ logger.warn(
319
+ `Deploying to chain ${chainId} (${chainId === mainnet.id ? 'mainnet' : 'sepolia'}) without ETHERSCAN_API_KEY. ` +
320
+ `Contracts will NOT be verified on Etherscan. Set ETHERSCAN_API_KEY environment variable to enable verification.`,
321
+ );
322
+ }
323
+
250
324
  // From heuristic testing. More caused issues with anvil.
251
- const MAGIC_ANVIL_BATCH_SIZE = 12;
325
+ const MAGIC_ANVIL_BATCH_SIZE = 8;
252
326
  // Anvil seems to stall with unbounded batch size. Otherwise no max batch size is desirable.
253
- // On sepolia and mainnet, we verify on etherscan (if etherscan API key is in env)
254
327
  const forgeArgs = [
255
328
  'script',
256
329
  FORGE_SCRIPT,
@@ -261,7 +334,8 @@ export async function deployAztecL1Contracts(
261
334
  '--rpc-url',
262
335
  rpcUrl,
263
336
  '--broadcast',
264
- ...(chainId === foundry.id ? ['--batch-size', MAGIC_ANVIL_BATCH_SIZE.toString()] : []),
337
+ '--batch-size',
338
+ MAGIC_ANVIL_BATCH_SIZE.toString(),
265
339
  ...(shouldVerify ? ['--verify'] : []),
266
340
  ];
267
341
  const forgeEnv = {
@@ -438,6 +512,7 @@ export function getDeployAztecL1ContractsEnvVars(args: DeployAztecL1ContractsArg
438
512
  AZTEC_EJECTION_THRESHOLD: args.ejectionThreshold?.toString(),
439
513
  AZTEC_GOVERNANCE_PROPOSER_ROUND_SIZE: args.governanceProposerRoundSize?.toString(),
440
514
  AZTEC_GOVERNANCE_PROPOSER_QUORUM: args.governanceProposerQuorum?.toString(),
515
+ AZTEC_GOVERNANCE_VOTING_DURATION: args.governanceVotingDuration?.toString(),
441
516
  ZKPASSPORT_DOMAIN: args.zkPassportArgs?.zkPassportDomain,
442
517
  ZKPASSPORT_SCOPE: args.zkPassportArgs?.zkPassportScope,
443
518
  } as const;
@@ -468,14 +543,17 @@ export function getDeployRollupForUpgradeEnvVars(
468
543
  AZTEC_TARGET_COMMITTEE_SIZE: args.aztecTargetCommitteeSize.toString(),
469
544
  AZTEC_LAG_IN_EPOCHS_FOR_VALIDATOR_SET: args.lagInEpochsForValidatorSet.toString(),
470
545
  AZTEC_LAG_IN_EPOCHS_FOR_RANDAO: args.lagInEpochsForRandao.toString(),
546
+ AZTEC_INBOX_LAG: args.inboxLag?.toString(),
471
547
  AZTEC_PROOF_SUBMISSION_EPOCHS: args.aztecProofSubmissionEpochs.toString(),
472
548
  AZTEC_LOCAL_EJECTION_THRESHOLD: args.localEjectionThreshold.toString(),
473
549
  AZTEC_SLASHING_LIFETIME_IN_ROUNDS: args.slashingLifetimeInRounds.toString(),
550
+ AZTEC_SLASHING_EXECUTION_DELAY_IN_ROUNDS: args.slashingExecutionDelayInRounds.toString(),
474
551
  AZTEC_SLASHING_VETOER: args.slashingVetoer.toString(),
475
552
  AZTEC_SLASHING_DISABLE_DURATION: args.slashingDisableDuration.toString(),
476
553
  AZTEC_MANA_TARGET: args.manaTarget.toString(),
477
554
  AZTEC_EXIT_DELAY_SECONDS: args.exitDelaySeconds.toString(),
478
555
  AZTEC_PROVING_COST_PER_MANA: args.provingCostPerMana.toString(),
556
+ AZTEC_INITIAL_ETH_PER_FEE_ASSET: args.initialEthPerFeeAsset.toString(),
479
557
  AZTEC_SLASHER_FLAVOR: args.slasherFlavor,
480
558
  AZTEC_SLASHING_ROUND_SIZE_IN_EPOCHS: args.slashingRoundSizeInEpochs.toString(),
481
559
  AZTEC_SLASHING_QUORUM: args.slashingQuorum?.toString(),
@@ -503,10 +581,8 @@ export const deployRollupForUpgrade = async (
503
581
  | 'zkPassportArgs'
504
582
  >,
505
583
  ) => {
506
- const currentDir = dirname(fileURLToPath(import.meta.url));
507
-
508
- // Relative location of l1-contracts in monorepo or docker image.
509
- const l1ContractsPath = resolve(currentDir, '..', '..', '..', 'l1-contracts');
584
+ // Use foundry-artifacts from l1-artifacts package
585
+ const l1ContractsPath = prepareL1ContractsForDeployment();
510
586
 
511
587
  const FORGE_SCRIPT = 'script/deploy/DeployRollupForUpgrade.s.sol';
512
588
  await maybeForgeForceProductionBuild(l1ContractsPath, FORGE_SCRIPT, chainId);
@@ -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;
@@ -1,6 +1,6 @@
1
1
  import type { ConfigMappingsType } from '@aztec/foundation/config';
2
2
  import { EthAddress } from '@aztec/foundation/eth-address';
3
- import { type ZodFor, schemas } from '@aztec/foundation/schemas';
3
+ import { schemas, zodFor } from '@aztec/foundation/schemas';
4
4
 
5
5
  import { z } from 'zod';
6
6
 
@@ -35,25 +35,27 @@ export type L1ContractAddresses = {
35
35
  dateGatedRelayerAddress?: EthAddress | undefined;
36
36
  };
37
37
 
38
- export const L1ContractAddressesSchema = z.object({
39
- rollupAddress: schemas.EthAddress,
40
- registryAddress: schemas.EthAddress,
41
- inboxAddress: schemas.EthAddress,
42
- outboxAddress: schemas.EthAddress,
43
- feeJuiceAddress: schemas.EthAddress,
44
- stakingAssetAddress: schemas.EthAddress,
45
- feeJuicePortalAddress: schemas.EthAddress,
46
- coinIssuerAddress: schemas.EthAddress,
47
- rewardDistributorAddress: schemas.EthAddress,
48
- governanceProposerAddress: schemas.EthAddress,
49
- governanceAddress: schemas.EthAddress,
50
- slashFactoryAddress: schemas.EthAddress.optional(),
51
- feeAssetHandlerAddress: schemas.EthAddress.optional(),
52
- stakingAssetHandlerAddress: schemas.EthAddress.optional(),
53
- zkPassportVerifierAddress: schemas.EthAddress.optional(),
54
- gseAddress: schemas.EthAddress.optional(),
55
- dateGatedRelayerAddress: schemas.EthAddress.optional(),
56
- }) satisfies ZodFor<L1ContractAddresses>;
38
+ export const L1ContractAddressesSchema = zodFor<L1ContractAddresses>()(
39
+ z.object({
40
+ rollupAddress: schemas.EthAddress,
41
+ registryAddress: schemas.EthAddress,
42
+ inboxAddress: schemas.EthAddress,
43
+ outboxAddress: schemas.EthAddress,
44
+ feeJuiceAddress: schemas.EthAddress,
45
+ stakingAssetAddress: schemas.EthAddress,
46
+ feeJuicePortalAddress: schemas.EthAddress,
47
+ coinIssuerAddress: schemas.EthAddress,
48
+ rewardDistributorAddress: schemas.EthAddress,
49
+ governanceProposerAddress: schemas.EthAddress,
50
+ governanceAddress: schemas.EthAddress,
51
+ slashFactoryAddress: schemas.EthAddress.optional(),
52
+ feeAssetHandlerAddress: schemas.EthAddress.optional(),
53
+ stakingAssetHandlerAddress: schemas.EthAddress.optional(),
54
+ zkPassportVerifierAddress: schemas.EthAddress.optional(),
55
+ gseAddress: schemas.EthAddress.optional(),
56
+ dateGatedRelayerAddress: schemas.EthAddress.optional(),
57
+ }),
58
+ );
57
59
 
58
60
  const parseEnv = (val: string) => EthAddress.fromString(val);
59
61
 
@@ -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
@@ -16,3 +16,14 @@ export const MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE = 100;
16
16
 
17
17
  // Avg ethereum block time is ~12s
18
18
  export const BLOCK_TIME_MS = 12_000;
19
+
20
+ // Gas per blob (EIP-4844)
21
+ export const GAS_PER_BLOB = 131072n;
22
+
23
+ // Blob capacity schedule based on Ethereum upgrades
24
+ export const BLOB_CAPACITY_SCHEDULE = [
25
+ { timestamp: 1734357600, target: 14, max: 21 }, // BPO2: Dec 17, 2025
26
+ { timestamp: 1733752800, target: 10, max: 15 }, // BPO1: Dec 9, 2025
27
+ { timestamp: 1733234400, target: 6, max: 9 }, // Fusaka: Dec 3, 2025
28
+ { timestamp: 0, target: 6, max: 9 }, // Pectra/earlier
29
+ ];
@@ -0,0 +1,22 @@
1
+ import { P75AllTxsPriorityFeeStrategy } from './p75_competitive.js';
2
+ import { P75BlobTxsOnlyPriorityFeeStrategy } from './p75_competitive_blob_txs_only.js';
3
+ import type { PriorityFeeStrategy } from './types.js';
4
+
5
+ export {
6
+ HISTORICAL_BLOCK_COUNT,
7
+ type PriorityFeeStrategy,
8
+ type PriorityFeeStrategyContext,
9
+ type PriorityFeeStrategyResult,
10
+ } from './types.js';
11
+
12
+ export { P75AllTxsPriorityFeeStrategy } from './p75_competitive.js';
13
+ export { P75BlobTxsOnlyPriorityFeeStrategy } from './p75_competitive_blob_txs_only.js';
14
+
15
+ /**
16
+ * Default list of priority fee strategies to analyze.
17
+ * Add more strategies here for comparison.
18
+ */
19
+ export const DEFAULT_PRIORITY_FEE_STRATEGIES: PriorityFeeStrategy[] = [
20
+ P75AllTxsPriorityFeeStrategy,
21
+ P75BlobTxsOnlyPriorityFeeStrategy,
22
+ ];
@@ -0,0 +1,163 @@
1
+ import { median } from '@aztec/foundation/collection';
2
+
3
+ import { formatGwei } from 'viem';
4
+
5
+ import type { ViemClient } from '../../types.js';
6
+ import { calculatePercentile } from '../../utils.js';
7
+ import { WEI_CONST } from '../constants.js';
8
+ import {
9
+ HISTORICAL_BLOCK_COUNT,
10
+ type PriorityFeeStrategy,
11
+ type PriorityFeeStrategyContext,
12
+ type PriorityFeeStrategyResult,
13
+ } from './types.js';
14
+
15
+ /**
16
+ * Our current competitive priority fee strategy.
17
+ * Analyzes p75 of pending transactions and 5-block fee history to determine a competitive priority fee.
18
+ * Falls back to network estimate if data is unavailable.
19
+ */
20
+ export const P75AllTxsPriorityFeeStrategy: PriorityFeeStrategy = {
21
+ name: 'Competitive (P75 + History) - CURRENT',
22
+ id: 'p75_pending_txs_and_history_all_txs',
23
+
24
+ async execute(client: ViemClient, context: PriorityFeeStrategyContext): Promise<PriorityFeeStrategyResult> {
25
+ const { isBlobTx, logger } = context;
26
+
27
+ // Fire all RPC calls in parallel
28
+ const [latestBlockResult, blobBaseFeeResult, networkEstimateResult, pendingBlockResult, feeHistoryResult] =
29
+ await Promise.allSettled([
30
+ client.getBlock({ blockTag: 'latest' }),
31
+ isBlobTx ? client.getBlobBaseFee() : Promise.resolve(undefined),
32
+ client.estimateMaxPriorityFeePerGas().catch(() => 0n),
33
+ client.getBlock({ blockTag: 'pending', includeTransactions: true }).catch(() => null),
34
+ client
35
+ .getFeeHistory({
36
+ blockCount: HISTORICAL_BLOCK_COUNT,
37
+ rewardPercentiles: [75],
38
+ blockTag: 'latest',
39
+ })
40
+ .catch(() => null),
41
+ ]);
42
+
43
+ // Extract latest block
44
+ if (latestBlockResult.status === 'rejected') {
45
+ throw new Error(`Failed to get latest block: ${latestBlockResult.reason}`);
46
+ }
47
+ const latestBlock = latestBlockResult.value;
48
+
49
+ // Extract blob base fee (only for blob txs)
50
+ let blobBaseFee: bigint | undefined;
51
+ if (isBlobTx) {
52
+ if (blobBaseFeeResult.status === 'fulfilled' && typeof blobBaseFeeResult.value === 'bigint') {
53
+ blobBaseFee = blobBaseFeeResult.value;
54
+ } else {
55
+ logger?.warn('Failed to get L1 blob base fee');
56
+ }
57
+ }
58
+
59
+ // Extract network estimate
60
+ const networkEstimate =
61
+ networkEstimateResult.status === 'fulfilled' && typeof networkEstimateResult.value === 'bigint'
62
+ ? networkEstimateResult.value
63
+ : 0n;
64
+
65
+ let competitiveFee = networkEstimate;
66
+ const debugInfo: Record<string, string | number> = {
67
+ networkEstimateGwei: formatGwei(networkEstimate),
68
+ };
69
+
70
+ // Extract pending block
71
+ const pendingBlock = pendingBlockResult.status === 'fulfilled' ? pendingBlockResult.value : null;
72
+
73
+ // Analyze pending block transactions
74
+ if (pendingBlock?.transactions && pendingBlock.transactions.length > 0) {
75
+ const pendingFees = pendingBlock.transactions
76
+ .map(tx => {
77
+ if (typeof tx === 'string') {
78
+ return 0n;
79
+ }
80
+ return tx.maxPriorityFeePerGas || 0n;
81
+ })
82
+ .filter((fee: bigint) => fee > 0n);
83
+
84
+ if (pendingFees.length > 0) {
85
+ // Use 75th percentile of pending fees to be competitive
86
+ const pendingCompetitiveFee = calculatePercentile(pendingFees, 75);
87
+
88
+ if (pendingCompetitiveFee > competitiveFee) {
89
+ competitiveFee = pendingCompetitiveFee;
90
+ }
91
+
92
+ debugInfo.pendingTxCount = pendingFees.length;
93
+ debugInfo.pendingP75Gwei = formatGwei(pendingCompetitiveFee);
94
+
95
+ logger?.debug('Analyzed pending transactions for competitive pricing', {
96
+ pendingTxCount: pendingFees.length,
97
+ pendingP75: formatGwei(pendingCompetitiveFee),
98
+ });
99
+ }
100
+ }
101
+
102
+ // Extract fee history
103
+ const feeHistory = feeHistoryResult.status === 'fulfilled' ? feeHistoryResult.value : null;
104
+
105
+ // Analyze fee history
106
+ if (feeHistory?.reward && feeHistory.reward.length > 0) {
107
+ // Extract 75th percentile fees from each block
108
+ const percentile75Fees = feeHistory.reward.map(rewards => rewards[0] || 0n).filter(fee => fee > 0n);
109
+
110
+ if (percentile75Fees.length > 0) {
111
+ // Calculate median of the 75th percentile fees across blocks
112
+ const medianHistoricalFee = median(percentile75Fees) ?? 0n;
113
+
114
+ // Debug: Log suspicious fees from history
115
+ if (medianHistoricalFee > 100n * WEI_CONST) {
116
+ logger?.warn('Suspicious high fee in history', {
117
+ historicalMedian: formatGwei(medianHistoricalFee),
118
+ allP75Fees: percentile75Fees.map(f => formatGwei(f)),
119
+ });
120
+ }
121
+
122
+ if (medianHistoricalFee > competitiveFee) {
123
+ competitiveFee = medianHistoricalFee;
124
+ }
125
+
126
+ debugInfo.historicalMedianGwei = formatGwei(medianHistoricalFee);
127
+
128
+ logger?.debug('Analyzed fee history for competitive pricing', {
129
+ historicalMedian: formatGwei(medianHistoricalFee),
130
+ });
131
+ }
132
+ }
133
+
134
+ // Sanity check: cap competitive fee at 100x network estimate to avoid using unrealistic fees
135
+ const maxReasonableFee = networkEstimate * 100n;
136
+ if (competitiveFee > maxReasonableFee && networkEstimate > 0n) {
137
+ logger?.debug('Competitive fee exceeds sanity cap, using capped value', {
138
+ competitiveFee: formatGwei(competitiveFee),
139
+ networkEstimate: formatGwei(networkEstimate),
140
+ cappedTo: formatGwei(maxReasonableFee),
141
+ });
142
+ competitiveFee = maxReasonableFee;
143
+ debugInfo.cappedToGwei = formatGwei(maxReasonableFee);
144
+ }
145
+
146
+ // Log final decision
147
+ if (competitiveFee > networkEstimate) {
148
+ logger?.debug('Using competitive fee from market analysis', {
149
+ networkEstimate: formatGwei(networkEstimate),
150
+ competitive: formatGwei(competitiveFee),
151
+ });
152
+ }
153
+
154
+ debugInfo.finalFeeGwei = formatGwei(competitiveFee);
155
+
156
+ return {
157
+ priorityFee: competitiveFee,
158
+ latestBlock,
159
+ blobBaseFee,
160
+ debugInfo,
161
+ };
162
+ },
163
+ };