@aztec/ethereum 0.0.1-commit.d3ec352c → 0.0.1-commit.fcb71a6

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 (138) hide show
  1. package/dest/client.js +6 -2
  2. package/dest/config.d.ts +6 -42
  3. package/dest/config.d.ts.map +1 -1
  4. package/dest/config.js +9 -327
  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 +9 -0
  10. package/dest/contracts/governance_proposer.d.ts +2 -1
  11. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  12. package/dest/contracts/governance_proposer.js +9 -0
  13. package/dest/contracts/inbox.d.ts +7 -3
  14. package/dest/contracts/inbox.d.ts.map +1 -1
  15. package/dest/contracts/inbox.js +4 -0
  16. package/dest/contracts/rollup.d.ts +19 -3
  17. package/dest/contracts/rollup.d.ts.map +1 -1
  18. package/dest/contracts/rollup.js +14 -0
  19. package/dest/contracts/tally_slashing_proposer.d.ts +3 -2
  20. package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
  21. package/dest/contracts/tally_slashing_proposer.js +1 -1
  22. package/dest/deploy_aztec_l1_contracts.d.ts +247 -0
  23. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -0
  24. package/dest/deploy_aztec_l1_contracts.js +336 -0
  25. package/dest/deploy_l1_contract.d.ts +68 -0
  26. package/dest/deploy_l1_contract.d.ts.map +1 -0
  27. package/dest/deploy_l1_contract.js +312 -0
  28. package/dest/forwarder_proxy.d.ts +32 -0
  29. package/dest/forwarder_proxy.d.ts.map +1 -0
  30. package/dest/forwarder_proxy.js +93 -0
  31. package/dest/l1_artifacts.d.ts +136 -98
  32. package/dest/l1_artifacts.d.ts.map +1 -1
  33. package/dest/l1_contract_addresses.d.ts +1 -1
  34. package/dest/l1_contract_addresses.d.ts.map +1 -1
  35. package/dest/l1_contract_addresses.js +3 -3
  36. package/dest/l1_reader.d.ts +3 -1
  37. package/dest/l1_reader.d.ts.map +1 -1
  38. package/dest/l1_reader.js +6 -0
  39. package/dest/l1_tx_utils/config.d.ts +3 -3
  40. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  41. package/dest/l1_tx_utils/config.js +17 -3
  42. package/dest/l1_tx_utils/constants.d.ts +7 -1
  43. package/dest/l1_tx_utils/constants.d.ts.map +1 -1
  44. package/dest/l1_tx_utils/constants.js +25 -0
  45. package/dest/l1_tx_utils/fee-strategies/index.d.ts +9 -0
  46. package/dest/l1_tx_utils/fee-strategies/index.d.ts.map +1 -0
  47. package/dest/l1_tx_utils/fee-strategies/index.js +11 -0
  48. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +18 -0
  49. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -0
  50. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +111 -0
  51. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts +32 -0
  52. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts.map +1 -0
  53. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +173 -0
  54. package/dest/l1_tx_utils/fee-strategies/types.d.ts +64 -0
  55. package/dest/l1_tx_utils/fee-strategies/types.d.ts.map +1 -0
  56. package/dest/l1_tx_utils/fee-strategies/types.js +24 -0
  57. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +41 -0
  58. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -0
  59. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +48 -0
  60. package/dest/l1_tx_utils/index-blobs.d.ts +3 -0
  61. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -0
  62. package/dest/l1_tx_utils/index-blobs.js +2 -0
  63. package/dest/l1_tx_utils/index.d.ts +3 -1
  64. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  65. package/dest/l1_tx_utils/index.js +2 -0
  66. package/dest/l1_tx_utils/interfaces.d.ts +2 -2
  67. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -1
  68. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +233 -0
  69. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -0
  70. package/dest/l1_tx_utils/l1_fee_analyzer.js +506 -0
  71. package/dest/l1_tx_utils/l1_tx_utils.d.ts +1 -1
  72. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  73. package/dest/l1_tx_utils/l1_tx_utils.js +17 -4
  74. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +4 -11
  75. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  76. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +68 -138
  77. package/dest/queries.d.ts +1 -1
  78. package/dest/queries.d.ts.map +1 -1
  79. package/dest/queries.js +6 -1
  80. package/dest/test/chain_monitor.d.ts +2 -2
  81. package/dest/test/chain_monitor.d.ts.map +1 -1
  82. package/dest/test/eth_cheat_codes.js +4 -2
  83. package/dest/test/rollup_cheat_codes.d.ts +2 -2
  84. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  85. package/dest/test/rollup_cheat_codes.js +8 -3
  86. package/dest/test/start_anvil.d.ts +3 -1
  87. package/dest/test/start_anvil.d.ts.map +1 -1
  88. package/dest/test/tx_delayer.d.ts +1 -1
  89. package/dest/test/tx_delayer.d.ts.map +1 -1
  90. package/dest/test/tx_delayer.js +4 -3
  91. package/dest/types.d.ts +57 -2
  92. package/dest/types.d.ts.map +1 -1
  93. package/dest/utils.d.ts +15 -3
  94. package/dest/utils.d.ts.map +1 -1
  95. package/dest/utils.js +18 -0
  96. package/package.json +28 -10
  97. package/src/client.ts +2 -2
  98. package/src/config.ts +10 -406
  99. package/src/contracts/empire_base.ts +1 -1
  100. package/src/contracts/empire_slashing_proposer.ts +6 -1
  101. package/src/contracts/governance_proposer.ts +6 -1
  102. package/src/contracts/inbox.ts +7 -2
  103. package/src/contracts/rollup.ts +18 -2
  104. package/src/contracts/tally_slashing_proposer.ts +3 -1
  105. package/src/deploy_aztec_l1_contracts.ts +557 -0
  106. package/src/deploy_l1_contract.ts +362 -0
  107. package/src/forwarder_proxy.ts +108 -0
  108. package/src/l1_contract_addresses.ts +22 -20
  109. package/src/l1_reader.ts +8 -0
  110. package/src/l1_tx_utils/config.ts +24 -6
  111. package/src/l1_tx_utils/constants.ts +11 -0
  112. package/src/l1_tx_utils/fee-strategies/index.ts +22 -0
  113. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +159 -0
  114. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +241 -0
  115. package/src/l1_tx_utils/fee-strategies/types.ts +88 -0
  116. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +119 -0
  117. package/src/l1_tx_utils/index-blobs.ts +2 -0
  118. package/src/l1_tx_utils/index.ts +2 -0
  119. package/src/l1_tx_utils/interfaces.ts +1 -1
  120. package/src/l1_tx_utils/l1_fee_analyzer.ts +804 -0
  121. package/src/l1_tx_utils/l1_tx_utils.ts +24 -4
  122. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +76 -176
  123. package/src/queries.ts +6 -0
  124. package/src/test/chain_monitor.ts +2 -1
  125. package/src/test/eth_cheat_codes.ts +2 -2
  126. package/src/test/rollup_cheat_codes.ts +4 -3
  127. package/src/test/start_anvil.ts +2 -0
  128. package/src/test/tx_delayer.ts +5 -3
  129. package/src/types.ts +62 -0
  130. package/src/utils.ts +30 -1
  131. package/dest/deploy_l1_contracts.d.ts +0 -673
  132. package/dest/deploy_l1_contracts.d.ts.map +0 -1
  133. package/dest/deploy_l1_contracts.js +0 -1491
  134. package/dest/index.d.ts +0 -18
  135. package/dest/index.d.ts.map +0 -1
  136. package/dest/index.js +0 -17
  137. package/src/deploy_l1_contracts.ts +0 -1869
  138. package/src/index.ts +0 -17
@@ -130,12 +130,32 @@ export class L1TxUtils extends ReadOnlyL1TxUtils {
130
130
  return;
131
131
  }
132
132
 
133
- // Convert loaded states (which have id) to the txs format
134
- this.txs = loadedStates;
135
- this.logger.info(`Rehydrated ${loadedStates.length} tx states for account ${account}`);
133
+ // Clean up excess states if we have more than MAX_L1_TX_STATES
134
+ if (loadedStates.length > MAX_L1_TX_STATES) {
135
+ this.logger.warn(
136
+ `Found ${loadedStates.length} tx states for account ${account}, pruning to most recent ${MAX_L1_TX_STATES}`,
137
+ );
138
+
139
+ // Keep only the most recent MAX_L1_TX_STATES
140
+ const statesToKeep = loadedStates.slice(-MAX_L1_TX_STATES);
141
+ const statesToDelete = loadedStates.slice(0, -MAX_L1_TX_STATES);
142
+
143
+ // Batch delete old states in a transaction for efficiency
144
+ const idsToDelete = statesToDelete.map(s => s.id);
145
+ await this.store.deleteState(account, ...idsToDelete);
146
+
147
+ this.txs = statesToKeep;
148
+ this.logger.info(
149
+ `Cleaned up ${statesToDelete.length} old tx states, kept ${statesToKeep.length} for account ${account}`,
150
+ );
151
+ } else {
152
+ // Convert loaded states (which have id) to the txs format
153
+ this.txs = loadedStates;
154
+ this.logger.info(`Rehydrated ${loadedStates.length} tx states for account ${account}`);
155
+ }
136
156
 
137
157
  // Find all pending states and resume monitoring
138
- const pendingStates = loadedStates.filter(state => !TerminalTxUtilsState.includes(state.status));
158
+ const pendingStates = this.txs.filter(state => !TerminalTxUtilsState.includes(state.status));
139
159
  if (pendingStates.length === 0) {
140
160
  return;
141
161
  }
@@ -1,4 +1,5 @@
1
- import { getKeys, median, merge, pick, times } from '@aztec/foundation/collection';
1
+ import { getKeys, merge, pick, times } from '@aztec/foundation/collection';
2
+ import type { EthAddress } from '@aztec/foundation/eth-address';
2
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
4
  import { makeBackoff, retry } from '@aztec/foundation/retry';
4
5
  import { DateProvider } from '@aztec/foundation/timer';
@@ -11,6 +12,7 @@ import {
11
12
  type BaseError,
12
13
  type BlockOverrides,
13
14
  type ContractFunctionExecutionError,
15
+ type GetCodeReturnType,
14
16
  type Hex,
15
17
  MethodNotFoundRpcError,
16
18
  MethodNotSupportedRpcError,
@@ -30,10 +32,12 @@ import {
30
32
  MIN_REPLACEMENT_BUMP_PERCENTAGE,
31
33
  WEI_CONST,
32
34
  } from './constants.js';
35
+ import { P75AllTxsPriorityFeeStrategy, type PriorityFeeStrategy } from './fee-strategies/index.js';
33
36
  import type { GasPrice, L1BlobInputs, L1TxRequest, TransactionStats } from './types.js';
34
37
  import { getCalldataGasUsage, tryGetCustomErrorNameContractFunction } from './utils.js';
35
38
 
36
- const HISTORICAL_BLOCK_COUNT = 5;
39
+ // Change this to the current strategy we want to use
40
+ const CurrentStrategy: PriorityFeeStrategy = P75AllTxsPriorityFeeStrategy;
37
41
 
38
42
  export class ReadOnlyL1TxUtils {
39
43
  public config: Required<L1TxUtilsConfig>;
@@ -65,122 +69,8 @@ export class ReadOnlyL1TxUtils {
65
69
  return this.client.getBlockNumber();
66
70
  }
67
71
 
68
- /**
69
- * Analyzes pending transactions and recent fee history to determine a competitive priority fee.
70
- * Falls back to network estimate if data is unavailable or fails.
71
- * @param networkEstimateResult - Result from estimateMaxPriorityFeePerGas RPC call
72
- * @param pendingBlockResult - Result from getBlock with pending tag RPC call
73
- * @param feeHistoryResult - Result from getFeeHistory RPC call
74
- * @returns A competitive priority fee based on pending txs and recent block history
75
- */
76
- protected getCompetitivePriorityFee(
77
- networkEstimateResult: PromiseSettledResult<bigint | null>,
78
- pendingBlockResult: PromiseSettledResult<Awaited<ReturnType<ViemClient['getBlock']>> | null>,
79
- feeHistoryResult: PromiseSettledResult<Awaited<ReturnType<ViemClient['getFeeHistory']>> | null>,
80
- ): bigint {
81
- const networkEstimate =
82
- networkEstimateResult.status === 'fulfilled' && typeof networkEstimateResult.value === 'bigint'
83
- ? networkEstimateResult.value
84
- : 0n;
85
- let competitiveFee = networkEstimate;
86
-
87
- if (
88
- pendingBlockResult.status === 'fulfilled' &&
89
- pendingBlockResult.value !== null &&
90
- pendingBlockResult.value.transactions &&
91
- pendingBlockResult.value.transactions.length > 0
92
- ) {
93
- const pendingBlock = pendingBlockResult.value;
94
- // Extract priority fees from pending transactions
95
- const pendingFees = pendingBlock.transactions
96
- .map(tx => {
97
- // Transaction can be just a hash string, so we need to check if it's an object
98
- if (typeof tx === 'string') {
99
- return 0n;
100
- }
101
- const fee = tx.maxPriorityFeePerGas || 0n;
102
- // Debug: Log suspicious fees
103
- if (fee > 100n * WEI_CONST) {
104
- this.logger?.warn('Suspicious high priority fee in pending tx', {
105
- txHash: tx.hash,
106
- maxPriorityFeePerGas: formatGwei(fee),
107
- maxFeePerGas: formatGwei(tx.maxFeePerGas || 0n),
108
- maxFeePerBlobGas: tx.maxFeePerBlobGas ? formatGwei(tx.maxFeePerBlobGas) : 'N/A',
109
- });
110
- }
111
- return fee;
112
- })
113
- .filter((fee: bigint) => fee > 0n);
114
-
115
- if (pendingFees.length > 0) {
116
- // Use 75th percentile of pending fees to be competitive
117
- const sortedPendingFees = [...pendingFees].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
118
- const percentile75Index = Math.floor((sortedPendingFees.length - 1) * 0.75);
119
- const pendingCompetitiveFee = sortedPendingFees[percentile75Index];
120
-
121
- if (pendingCompetitiveFee > competitiveFee) {
122
- competitiveFee = pendingCompetitiveFee;
123
- }
124
-
125
- this.logger?.debug('Analyzed pending transactions for competitive pricing', {
126
- pendingTxCount: pendingFees.length,
127
- pendingP75: formatGwei(pendingCompetitiveFee),
128
- });
129
- }
130
- }
131
- if (
132
- feeHistoryResult.status === 'fulfilled' &&
133
- feeHistoryResult.value !== null &&
134
- feeHistoryResult.value.reward &&
135
- feeHistoryResult.value.reward.length > 0
136
- ) {
137
- const feeHistory = feeHistoryResult.value;
138
- // Extract 75th percentile fees from each block
139
- const percentile75Fees = feeHistory.reward!.map(rewards => rewards[0] || 0n).filter(fee => fee > 0n);
140
-
141
- if (percentile75Fees.length > 0) {
142
- // Calculate median of the 75th percentile fees across blocks
143
- const medianHistoricalFee = median(percentile75Fees) ?? 0n;
144
-
145
- // Debug: Log suspicious fees from history
146
- if (medianHistoricalFee > 100n * WEI_CONST) {
147
- this.logger?.warn('Suspicious high fee in history', {
148
- historicalMedian: formatGwei(medianHistoricalFee),
149
- allP75Fees: percentile75Fees.map(f => formatGwei(f)),
150
- });
151
- }
152
-
153
- if (medianHistoricalFee > competitiveFee) {
154
- competitiveFee = medianHistoricalFee;
155
- }
156
-
157
- this.logger?.debug('Analyzed fee history for competitive pricing', {
158
- historicalMedian: formatGwei(medianHistoricalFee),
159
- });
160
- }
161
- }
162
-
163
- // Sanity check: cap competitive fee at 100x network estimate to avoid using unrealistic fees
164
- // (e.g., Anvil returns inflated historical fees that don't reflect actual network conditions)
165
- const maxReasonableFee = networkEstimate * 100n;
166
- if (competitiveFee > maxReasonableFee) {
167
- this.logger?.warn('Competitive fee exceeds sanity cap, using capped value', {
168
- competitiveFee: formatGwei(competitiveFee),
169
- networkEstimate: formatGwei(networkEstimate),
170
- cappedTo: formatGwei(maxReasonableFee),
171
- });
172
- competitiveFee = maxReasonableFee;
173
- }
174
-
175
- // Log final decision
176
- if (competitiveFee > networkEstimate) {
177
- this.logger?.debug('Using competitive fee from market analysis', {
178
- networkEstimate: formatGwei(networkEstimate),
179
- competitive: formatGwei(competitiveFee),
180
- });
181
- }
182
-
183
- return competitiveFee;
72
+ public getCode(address: EthAddress): Promise<GetCodeReturnType> {
73
+ return this.client.getCode({ address: address.toString() });
184
74
  }
185
75
 
186
76
  /**
@@ -195,37 +85,31 @@ export class ReadOnlyL1TxUtils {
195
85
  const gasConfig = merge(this.config, gasConfigOverrides);
196
86
 
197
87
  // Make all RPC calls in parallel upfront with retry logic
88
+ // First 2 calls are necessary to complete
198
89
  const latestBlockPromise = this.tryTwice(
199
90
  () => this.client.getBlock({ blockTag: 'latest' }),
200
91
  'Getting latest block',
201
92
  );
202
- const networkEstimatePromise = gasConfig.fixedPriorityFeePerGas
203
- ? null
204
- : this.tryTwice(() => this.client.estimateMaxPriorityFeePerGas(), 'Estimating max priority fee per gas');
205
- const pendingBlockPromise = gasConfig.fixedPriorityFeePerGas
206
- ? null
207
- : this.tryTwice(
208
- () => this.client.getBlock({ blockTag: 'pending', includeTransactions: true }),
209
- 'Getting pending block',
210
- );
211
- const feeHistoryPromise = gasConfig.fixedPriorityFeePerGas
212
- ? null
213
- : this.tryTwice(
214
- () => this.client.getFeeHistory({ blockCount: HISTORICAL_BLOCK_COUNT, rewardPercentiles: [75] }),
215
- 'Getting fee history',
216
- );
217
- const blobBaseFeePromise = isBlobTx
218
- ? this.tryTwice(() => this.client.getBlobBaseFee(), 'Getting blob base fee')
219
- : null;
220
-
221
- const [latestBlockResult, networkEstimateResult, pendingBlockResult, feeHistoryResult, blobBaseFeeResult] =
222
- await Promise.allSettled([
223
- latestBlockPromise,
224
- networkEstimatePromise ?? Promise.resolve(0n),
225
- pendingBlockPromise ?? Promise.resolve(null),
226
- feeHistoryPromise ?? Promise.resolve(null),
227
- blobBaseFeePromise ?? Promise.resolve(0n),
228
- ]);
93
+
94
+ let blobBaseFeePromise = null;
95
+ if (isBlobTx) {
96
+ blobBaseFeePromise = this.tryTwice(() => this.client.getBlobBaseFee(), 'Getting blob base fee');
97
+ }
98
+
99
+ // Get strategy promises for priority fee calculation
100
+ const strategyPromises = CurrentStrategy.getRequiredPromises(this.client, { isBlobTx });
101
+ const strategyPromiseKeys = [];
102
+ const strategyPromisesArr = [];
103
+ for (const [key, promise] of Object.entries(strategyPromises)) {
104
+ strategyPromiseKeys.push(key);
105
+ strategyPromisesArr.push(this.tryTwice(() => promise, `Getting strategy data for ${key}`));
106
+ }
107
+
108
+ const [latestBlockResult, blobBaseFeeResult, ...strategyResults] = await Promise.allSettled([
109
+ latestBlockPromise,
110
+ blobBaseFeePromise ?? Promise.resolve(0n),
111
+ ...strategyPromisesArr,
112
+ ]);
229
113
 
230
114
  // Extract results
231
115
  const baseFee =
@@ -244,14 +128,31 @@ export class ReadOnlyL1TxUtils {
244
128
  }
245
129
 
246
130
  let priorityFee: bigint;
247
- if (gasConfig.fixedPriorityFeePerGas) {
248
- this.logger?.debug('Using fixed priority fee per L1 gas', {
249
- fixedPriorityFeePerGas: gasConfig.fixedPriorityFeePerGas,
250
- });
251
- priorityFee = BigInt(Math.trunc(gasConfig.fixedPriorityFeePerGas * Number(WEI_CONST)));
252
- } else {
253
- // Get competitive priority fee (includes network estimate + analysis)
254
- priorityFee = this.getCompetitivePriorityFee(networkEstimateResult, pendingBlockResult, feeHistoryResult);
131
+ // Get competitive priority fee using strategy
132
+ // Reconstruct the results object with the same keys as the promises
133
+ const resultsObject: Record<string, PromiseSettledResult<unknown>> = {};
134
+ strategyPromiseKeys.forEach((key, index) => {
135
+ resultsObject[key] = strategyResults[index];
136
+ });
137
+
138
+ const result = CurrentStrategy.calculate(resultsObject as any, {
139
+ gasConfig,
140
+ isBlobTx,
141
+ logger: this.logger,
142
+ });
143
+ priorityFee = result.priorityFee;
144
+
145
+ // Apply minimum priority fee floor if configured
146
+ if (gasConfig.minimumPriorityFeePerGas) {
147
+ const minimumPriorityFee = BigInt(Math.trunc(gasConfig.minimumPriorityFeePerGas * Number(WEI_CONST)));
148
+ if (priorityFee < minimumPriorityFee) {
149
+ this.logger?.debug('Applying minimum priority fee floor', {
150
+ calculatedPriorityFee: formatGwei(priorityFee),
151
+ minimumPriorityFeePerGas: gasConfig.minimumPriorityFeePerGas,
152
+ appliedFee: formatGwei(minimumPriorityFee),
153
+ });
154
+ priorityFee = minimumPriorityFee;
155
+ }
255
156
  }
256
157
  let maxFeePerGas = baseFee;
257
158
 
@@ -280,18 +181,15 @@ export class ReadOnlyL1TxUtils {
280
181
  (previousGasPrice!.maxPriorityFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
281
182
  const minMaxFee = (previousGasPrice!.maxFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
282
183
 
283
- let competitivePriorityFee = priorityFee;
284
- if (!gasConfig.fixedPriorityFeePerGas) {
285
- // Apply bump percentage to competitive fee
286
- competitivePriorityFee = (priorityFee * (100_00n + BigInt(configBump * 1_00))) / 100_00n;
184
+ // Apply bump percentage to competitive fee
185
+ const competitivePriorityFee = (priorityFee * (100_00n + BigInt(configBump * 1_00))) / 100_00n;
287
186
 
288
- this.logger?.debug(`Speed-up attempt ${attempt}: using competitive fee strategy`, {
289
- networkEstimate: formatGwei(priorityFee),
290
- competitiveFee: formatGwei(competitivePriorityFee),
291
- minRequired: formatGwei(minPriorityFee),
292
- bumpPercentage: configBump,
293
- });
294
- }
187
+ this.logger?.debug(`Speed-up attempt ${attempt}: using competitive fee strategy`, {
188
+ networkEstimate: formatGwei(priorityFee),
189
+ competitiveFee: formatGwei(competitivePriorityFee),
190
+ minRequired: formatGwei(minPriorityFee),
191
+ bumpPercentage: configBump,
192
+ });
295
193
 
296
194
  // Use maximum between competitive fee and minimum required bump
297
195
  const finalPriorityFee = competitivePriorityFee > minPriorityFee ? competitivePriorityFee : minPriorityFee;
@@ -302,20 +200,16 @@ export class ReadOnlyL1TxUtils {
302
200
  maxFeePerGas += finalPriorityFee;
303
201
  maxFeePerGas = maxFeePerGas > minMaxFee ? maxFeePerGas : minMaxFee;
304
202
 
305
- if (!gasConfig.fixedPriorityFeePerGas) {
306
- this.logger?.debug(`Speed-up fee decision: using ${feeSource} fee`, {
307
- finalPriorityFee: formatGwei(finalPriorityFee),
308
- });
309
- }
203
+ this.logger?.debug(`Speed-up fee decision: using ${feeSource} fee`, {
204
+ finalPriorityFee: formatGwei(finalPriorityFee),
205
+ });
310
206
  } else {
311
207
  // First attempt: apply configured bump percentage to competitive fee
312
208
  // multiply by 100 & divide by 100 to maintain some precision
313
- if (!gasConfig.fixedPriorityFeePerGas) {
314
- priorityFee = (priorityFee * (100_00n + BigInt((gasConfig.priorityFeeBumpPercentage || 0) * 1_00))) / 100_00n;
315
- this.logger?.debug('Initial transaction: using competitive fee from market analysis', {
316
- networkEstimate: formatGwei(priorityFee),
317
- });
318
- }
209
+ priorityFee = (priorityFee * (100_00n + BigInt((gasConfig.priorityFeeBumpPercentage || 0) * 1_00))) / 100_00n;
210
+ this.logger?.debug('Initial transaction: using competitive fee from market analysis', {
211
+ networkEstimate: formatGwei(priorityFee),
212
+ });
319
213
  maxFeePerGas += priorityFee;
320
214
  }
321
215
 
@@ -388,11 +282,17 @@ export class ReadOnlyL1TxUtils {
388
282
  ..._blobInputs,
389
283
  maxFeePerBlobGas: gasPrice.maxFeePerBlobGas!,
390
284
  gas: LARGE_GAS_LIMIT,
285
+ blockTag: 'latest',
391
286
  });
392
287
 
393
288
  this.logger?.trace(`Estimated gas for blob tx: ${initialEstimate}`);
394
289
  } else {
395
- initialEstimate = await this.client.estimateGas({ account, ...request, gas: LARGE_GAS_LIMIT });
290
+ initialEstimate = await this.client.estimateGas({
291
+ account,
292
+ ...request,
293
+ gas: LARGE_GAS_LIMIT,
294
+ blockTag: 'latest',
295
+ });
396
296
  this.logger?.trace(`Estimated gas for non-blob tx: ${initialEstimate}`);
397
297
  }
398
298
 
package/src/queries.ts CHANGED
@@ -3,6 +3,7 @@ import { EthAddress } from '@aztec/foundation/eth-address';
3
3
  import type { L1ContractsConfig } from './config.js';
4
4
  import { ReadOnlyGovernanceContract } from './contracts/governance.js';
5
5
  import { GovernanceProposerContract } from './contracts/governance_proposer.js';
6
+ import { InboxContract } from './contracts/inbox.js';
6
7
  import { RollupContract } from './contracts/rollup.js';
7
8
  import type { ViemPublicClient } from './types.js';
8
9
 
@@ -25,6 +26,8 @@ export async function getL1ContractsConfig(
25
26
  const rollup = new RollupContract(publicClient, rollupAddress.toString());
26
27
  const slasherProposer = await rollup.getSlashingProposer();
27
28
  const slasher = await rollup.getSlasherContract();
29
+ const rollupAddresses = await rollup.getRollupAddresses();
30
+ const inboxContract = new InboxContract(publicClient, rollupAddresses.inboxAddress.toString());
28
31
 
29
32
  const [
30
33
  l1StartBlock,
@@ -35,6 +38,7 @@ export async function getL1ContractsConfig(
35
38
  aztecTargetCommitteeSize,
36
39
  lagInEpochsForValidatorSet,
37
40
  lagInEpochsForRandao,
41
+ inboxLag,
38
42
  activationThreshold,
39
43
  ejectionThreshold,
40
44
  localEjectionThreshold,
@@ -62,6 +66,7 @@ export async function getL1ContractsConfig(
62
66
  rollup.getTargetCommitteeSize(),
63
67
  rollup.getLagInEpochsForValidatorSet(),
64
68
  rollup.getLagInEpochsForRandao(),
69
+ inboxContract.getLag(),
65
70
  rollup.getActivationThreshold(),
66
71
  rollup.getEjectionThreshold(),
67
72
  rollup.getLocalEjectionThreshold(),
@@ -91,6 +96,7 @@ export async function getL1ContractsConfig(
91
96
  aztecTargetCommitteeSize: Number(aztecTargetCommitteeSize),
92
97
  lagInEpochsForValidatorSet: Number(lagInEpochsForValidatorSet),
93
98
  lagInEpochsForRandao: Number(lagInEpochsForRandao),
99
+ inboxLag: Number(inboxLag),
94
100
  governanceProposerQuorum: Number(governanceProposerQuorum),
95
101
  governanceProposerRoundSize: Number(governanceProposerRoundSize),
96
102
  activationThreshold,
@@ -1,4 +1,5 @@
1
- import { InboxContract, type RollupContract } from '@aztec/ethereum/contracts';
1
+ import type { RollupContract } from '@aztec/ethereum/contracts';
2
+ import { InboxContract } from '@aztec/ethereum/contracts';
2
3
  import { CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
3
4
  import { EthAddress } from '@aztec/foundation/eth-address';
4
5
  import { createLogger } from '@aztec/foundation/log';
@@ -1,5 +1,5 @@
1
1
  import { toBigIntBE, toHex } from '@aztec/foundation/bigint-buffer';
2
- import { keccak256 } from '@aztec/foundation/crypto';
2
+ import { keccak256 } from '@aztec/foundation/crypto/keccak';
3
3
  import { EthAddress } from '@aztec/foundation/eth-address';
4
4
  import { jsonStringify } from '@aztec/foundation/json-rpc';
5
5
  import { createLogger } from '@aztec/foundation/log';
@@ -35,7 +35,7 @@ export class EthCheatCodes {
35
35
  public chain: Chain = foundry,
36
36
  ) {
37
37
  this.publicClient = createPublicClient({
38
- transport: fallback(this.rpcUrls.map(url => http(url))),
38
+ transport: fallback(this.rpcUrls.map(url => http(url, { batch: false }))),
39
39
  chain: chain,
40
40
  });
41
41
  }
@@ -1,5 +1,6 @@
1
- import { RollupContract, type ViemPublicClient } from '@aztec/ethereum';
1
+ import { RollupContract } from '@aztec/ethereum/contracts';
2
2
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
3
+ import type { ViemPublicClient } from '@aztec/ethereum/types';
3
4
  import { CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
4
5
  import { EthAddress } from '@aztec/foundation/eth-address';
5
6
  import { createLogger } from '@aztec/foundation/log';
@@ -31,7 +32,7 @@ export class RollupCheatCodes {
31
32
  ) {
32
33
  this.client = createPublicClient({
33
34
  chain: ethCheatCodes.chain,
34
- transport: fallback(ethCheatCodes.rpcUrls.map(url => http(url))),
35
+ transport: fallback(ethCheatCodes.rpcUrls.map(url => http(url, { batch: false }))),
35
36
  });
36
37
  this.rollup = getContract({
37
38
  abi: RollupAbi,
@@ -127,7 +128,7 @@ export class RollupCheatCodes {
127
128
  const timestamp = (await this.rollup.read.getTimestampForSlot([BigInt(slotNumber)])) + BigInt(opts.offset ?? 0);
128
129
  try {
129
130
  await this.ethCheatCodes.warp(Number(timestamp), { ...opts, silent: true, resetBlockInterval: true });
130
- this.logger.warn(`Warped to epoch ${epoch}`);
131
+ this.logger.warn(`Warped to epoch ${epoch}`, { offset: opts.offset, timestamp });
131
132
  } catch (err) {
132
133
  this.logger.warn(`Warp to epoch ${epoch} failed: ${err}`);
133
134
  }
@@ -16,6 +16,8 @@ export async function startAnvil(
16
16
  captureMethodCalls?: boolean;
17
17
  accounts?: number;
18
18
  chainId?: number;
19
+ /** The hardfork to use - note: @viem/anvil types are out of date but 'cancun' and 'latest' work */
20
+ hardfork?: string;
19
21
  } = {},
20
22
  ): Promise<{ anvil: Anvil; methodCalls?: string[]; rpcUrl: string; stop: () => Promise<void> }> {
21
23
  const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh');
@@ -18,6 +18,8 @@ import {
18
18
 
19
19
  import { type ViemClient, isExtendedClient } from '../types.js';
20
20
 
21
+ const MAX_WAIT_TIME_SECONDS = 180;
22
+
21
23
  export function waitUntilBlock<T extends Client>(
22
24
  client: T,
23
25
  blockNumber: number | bigint,
@@ -36,7 +38,7 @@ export function waitUntilBlock<T extends Client>(
36
38
  return currentBlockNumber >= BigInt(blockNumber);
37
39
  },
38
40
  `Wait until L1 block ${blockNumber}`,
39
- timeout ?? 120,
41
+ timeout ?? MAX_WAIT_TIME_SECONDS,
40
42
  0.1,
41
43
  );
42
44
  }
@@ -66,7 +68,7 @@ export function waitUntilL1Timestamp<T extends Client>(
66
68
  return currentTs >= BigInt(timestamp);
67
69
  },
68
70
  `Wait until L1 timestamp ${timestamp}`,
69
- timeout ?? 120,
71
+ timeout ?? MAX_WAIT_TIME_SECONDS,
70
72
  0.1,
71
73
  );
72
74
  }
@@ -230,7 +232,7 @@ export function withDelayer<T extends ViemClient>(
230
232
  return Promise.resolve(txHash!);
231
233
  } else {
232
234
  const txHash = await client.sendRawTransaction(...args);
233
- logger.verbose(`Sent tx immediately ${txHash}`);
235
+ logger.debug(`Sent tx immediately ${txHash}`);
234
236
  delayer.sentTxHashes.push(txHash);
235
237
  return txHash;
236
238
  }
package/src/types.ts CHANGED
@@ -5,6 +5,7 @@ import type {
5
5
  Client,
6
6
  FallbackTransport,
7
7
  GetContractReturnType,
8
+ Hex,
8
9
  HttpTransport,
9
10
  PublicActions,
10
11
  PublicClient,
@@ -16,6 +17,67 @@ import type {
16
17
  /** Type for a viem public client */
17
18
  export type ViemPublicClient = PublicClient<FallbackTransport<HttpTransport[]>, Chain>;
18
19
 
20
+ export type PublicRpcDebugSchema = [
21
+ {
22
+ Method: 'debug_traceTransaction';
23
+ Parameters: [txHash: `0x${string}`, options: { tracer: 'callTracer' }];
24
+ ReturnType: DebugCallTrace;
25
+ },
26
+ {
27
+ Method: 'trace_transaction';
28
+ Parameters: [txHash: `0x${string}`];
29
+ ReturnType: TraceTransactionResponse[];
30
+ },
31
+ ];
32
+
33
+ /** Return type for a debug_traceTransaction call */
34
+ export type DebugCallTrace = {
35
+ from: Hex;
36
+ to?: Hex;
37
+ type: string;
38
+ input?: Hex;
39
+ output?: Hex;
40
+ gas?: Hex;
41
+ gasUsed?: Hex;
42
+ value?: Hex;
43
+ error?: string;
44
+ calls?: DebugCallTrace[];
45
+ };
46
+
47
+ /** Action object for a trace_transaction call */
48
+ export type TraceAction = {
49
+ from: Hex;
50
+ to?: Hex;
51
+ callType: string;
52
+ gas?: Hex;
53
+ input?: Hex;
54
+ value?: Hex;
55
+ };
56
+
57
+ /** Result object for a trace_transaction call */
58
+ export type TraceResult = {
59
+ gasUsed?: Hex;
60
+ output?: Hex;
61
+ };
62
+
63
+ /** Return type for a single trace in trace_transaction response */
64
+ export type TraceTransactionResponse = {
65
+ action: TraceAction;
66
+ result?: TraceResult;
67
+ error?: string;
68
+ subtraces: number;
69
+ traceAddress: number[];
70
+ type: string;
71
+ };
72
+
73
+ /** Type for a viem public client with support for debug methods */
74
+ export type ViemPublicDebugClient = PublicClient<
75
+ FallbackTransport<HttpTransport[]>,
76
+ Chain,
77
+ undefined,
78
+ [...PublicRpcSchema, ...PublicRpcDebugSchema]
79
+ >;
80
+
19
81
  export type ExtendedViemWalletClient = Client<
20
82
  FallbackTransport<readonly HttpTransport[]>,
21
83
  Chain,
package/src/utils.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Fr } from '@aztec/foundation/fields';
1
+ import type { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import type { Logger } from '@aztec/foundation/log';
3
3
  import { ErrorsAbi } from '@aztec/l1-artifacts/ErrorsAbi';
4
4
 
@@ -8,6 +8,7 @@ import {
8
8
  type ContractEventName,
9
9
  ContractFunctionRevertedError,
10
10
  type DecodeEventLogReturnType,
11
+ type FormattedTransaction,
11
12
  type Hex,
12
13
  type Log,
13
14
  decodeErrorResult,
@@ -233,3 +234,31 @@ export function tryGetCustomErrorName(err: any) {
233
234
  return undefined;
234
235
  }
235
236
  }
237
+
238
+ /**
239
+ * Type guard to check if a transaction is a blob transaction (EIP-4844).
240
+ * Blob transactions have maxFeePerBlobGas and blobVersionedHashes fields.
241
+ */
242
+ export function isBlobTransaction(tx: FormattedTransaction): tx is FormattedTransaction & {
243
+ maxFeePerBlobGas: bigint;
244
+ blobVersionedHashes: readonly Hex[];
245
+ } {
246
+ return (
247
+ 'maxFeePerBlobGas' in tx &&
248
+ tx.maxFeePerBlobGas !== undefined &&
249
+ 'blobVersionedHashes' in tx &&
250
+ tx.blobVersionedHashes !== undefined
251
+ );
252
+ }
253
+
254
+ /**
255
+ * Calculates a percentile from an array of bigints
256
+ */
257
+ export function calculatePercentile(values: bigint[], percentile: number): bigint {
258
+ if (values.length === 0) {
259
+ return 0n;
260
+ }
261
+ const sorted = [...values].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
262
+ const index = Math.ceil((sorted.length - 1) * (percentile / 100));
263
+ return sorted[index];
264
+ }