@aztec-labs/ethereum 6.0.0-nightly.20260829

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 (272) hide show
  1. package/README.md +3 -0
  2. package/dest/account.d.ts +2 -0
  3. package/dest/account.d.ts.map +1 -0
  4. package/dest/account.js +4 -0
  5. package/dest/chain.d.ts +25 -0
  6. package/dest/chain.d.ts.map +1 -0
  7. package/dest/chain.js +53 -0
  8. package/dest/client.d.ts +42 -0
  9. package/dest/client.d.ts.map +1 -0
  10. package/dest/client.js +104 -0
  11. package/dest/config.d.ts +103 -0
  12. package/dest/config.d.ts.map +1 -0
  13. package/dest/config.js +233 -0
  14. package/dest/constants.d.ts +4 -0
  15. package/dest/constants.d.ts.map +1 -0
  16. package/dest/constants.js +2 -0
  17. package/dest/contracts/chain_state_override.d.ts +81 -0
  18. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  19. package/dest/contracts/chain_state_override.js +139 -0
  20. package/dest/contracts/empire_base.d.ts +30 -0
  21. package/dest/contracts/empire_base.d.ts.map +1 -0
  22. package/dest/contracts/empire_base.js +84 -0
  23. package/dest/contracts/errors.d.ts +7 -0
  24. package/dest/contracts/errors.d.ts.map +1 -0
  25. package/dest/contracts/errors.js +12 -0
  26. package/dest/contracts/fee_asset_handler.d.ts +20 -0
  27. package/dest/contracts/fee_asset_handler.d.ts.map +1 -0
  28. package/dest/contracts/fee_asset_handler.js +59 -0
  29. package/dest/contracts/fee_asset_price_oracle.d.ts +105 -0
  30. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  31. package/dest/contracts/fee_asset_price_oracle.js +656 -0
  32. package/dest/contracts/fee_juice.d.ts +14 -0
  33. package/dest/contracts/fee_juice.d.ts.map +1 -0
  34. package/dest/contracts/fee_juice.js +59 -0
  35. package/dest/contracts/governance.d.ts +192 -0
  36. package/dest/contracts/governance.d.ts.map +1 -0
  37. package/dest/contracts/governance.js +471 -0
  38. package/dest/contracts/governance_proposer.d.ts +63 -0
  39. package/dest/contracts/governance_proposer.d.ts.map +1 -0
  40. package/dest/contracts/governance_proposer.js +536 -0
  41. package/dest/contracts/gse.d.ts +38 -0
  42. package/dest/contracts/gse.d.ts.map +1 -0
  43. package/dest/contracts/gse.js +81 -0
  44. package/dest/contracts/inbox.d.ts +94 -0
  45. package/dest/contracts/inbox.d.ts.map +1 -0
  46. package/dest/contracts/inbox.js +141 -0
  47. package/dest/contracts/index.d.ts +18 -0
  48. package/dest/contracts/index.d.ts.map +1 -0
  49. package/dest/contracts/index.js +17 -0
  50. package/dest/contracts/log.d.ts +13 -0
  51. package/dest/contracts/log.d.ts.map +1 -0
  52. package/dest/contracts/log.js +1 -0
  53. package/dest/contracts/multicall.d.ts +138 -0
  54. package/dest/contracts/multicall.d.ts.map +1 -0
  55. package/dest/contracts/multicall.js +266 -0
  56. package/dest/contracts/outbox.d.ts +64 -0
  57. package/dest/contracts/outbox.d.ts.map +1 -0
  58. package/dest/contracts/outbox.js +113 -0
  59. package/dest/contracts/registry.d.ts +31 -0
  60. package/dest/contracts/registry.d.ts.map +1 -0
  61. package/dest/contracts/registry.js +142 -0
  62. package/dest/contracts/rollup.d.ts +5836 -0
  63. package/dest/contracts/rollup.d.ts.map +1 -0
  64. package/dest/contracts/rollup.js +1612 -0
  65. package/dest/contracts/slasher_contract.d.ts +44 -0
  66. package/dest/contracts/slasher_contract.d.ts.map +1 -0
  67. package/dest/contracts/slasher_contract.js +75 -0
  68. package/dest/contracts/slashing_proposer.d.ts +174 -0
  69. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  70. package/dest/contracts/slashing_proposer.js +755 -0
  71. package/dest/contracts/utils.d.ts +3 -0
  72. package/dest/contracts/utils.d.ts.map +1 -0
  73. package/dest/contracts/utils.js +11 -0
  74. package/dest/contracts/watch_event.d.ts +36 -0
  75. package/dest/contracts/watch_event.d.ts.map +1 -0
  76. package/dest/contracts/watch_event.js +66 -0
  77. package/dest/deploy_aztec_l1_contracts.d.ts +267 -0
  78. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -0
  79. package/dest/deploy_aztec_l1_contracts.js +424 -0
  80. package/dest/deploy_l1_contract.d.ts +68 -0
  81. package/dest/deploy_l1_contract.d.ts.map +1 -0
  82. package/dest/deploy_l1_contract.js +312 -0
  83. package/dest/eth-signer/eth-signer.d.ts +21 -0
  84. package/dest/eth-signer/eth-signer.d.ts.map +1 -0
  85. package/dest/eth-signer/eth-signer.js +5 -0
  86. package/dest/eth-signer/index.d.ts +2 -0
  87. package/dest/eth-signer/index.d.ts.map +1 -0
  88. package/dest/eth-signer/index.js +1 -0
  89. package/dest/forwarder_proxy.d.ts +32 -0
  90. package/dest/forwarder_proxy.d.ts.map +1 -0
  91. package/dest/forwarder_proxy.js +93 -0
  92. package/dest/foundry_binary.d.ts +13 -0
  93. package/dest/foundry_binary.d.ts.map +1 -0
  94. package/dest/foundry_binary.js +52 -0
  95. package/dest/l1_artifacts.d.ts +81896 -0
  96. package/dest/l1_artifacts.d.ts.map +1 -0
  97. package/dest/l1_artifacts.js +151 -0
  98. package/dest/l1_contract_addresses.d.ts +73 -0
  99. package/dest/l1_contract_addresses.d.ts.map +1 -0
  100. package/dest/l1_contract_addresses.js +120 -0
  101. package/dest/l1_reader.d.ts +18 -0
  102. package/dest/l1_reader.d.ts.map +1 -0
  103. package/dest/l1_reader.js +35 -0
  104. package/dest/l1_tx_utils/config.d.ts +65 -0
  105. package/dest/l1_tx_utils/config.d.ts.map +1 -0
  106. package/dest/l1_tx_utils/config.js +109 -0
  107. package/dest/l1_tx_utils/constants.d.ts +12 -0
  108. package/dest/l1_tx_utils/constants.d.ts.map +1 -0
  109. package/dest/l1_tx_utils/constants.js +39 -0
  110. package/dest/l1_tx_utils/factory.d.ts +32 -0
  111. package/dest/l1_tx_utils/factory.d.ts.map +1 -0
  112. package/dest/l1_tx_utils/factory.js +22 -0
  113. package/dest/l1_tx_utils/fee-strategies/index.d.ts +10 -0
  114. package/dest/l1_tx_utils/fee-strategies/index.d.ts.map +1 -0
  115. package/dest/l1_tx_utils/fee-strategies/index.js +12 -0
  116. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +8 -0
  117. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -0
  118. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +129 -0
  119. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts +23 -0
  120. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.d.ts.map +1 -0
  121. package/dest/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.js +191 -0
  122. package/dest/l1_tx_utils/fee-strategies/types.d.ts +51 -0
  123. package/dest/l1_tx_utils/fee-strategies/types.d.ts.map +1 -0
  124. package/dest/l1_tx_utils/fee-strategies/types.js +3 -0
  125. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +41 -0
  126. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -0
  127. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +42 -0
  128. package/dest/l1_tx_utils/index-blobs.d.ts +3 -0
  129. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -0
  130. package/dest/l1_tx_utils/index-blobs.js +2 -0
  131. package/dest/l1_tx_utils/index.d.ts +13 -0
  132. package/dest/l1_tx_utils/index.d.ts.map +1 -0
  133. package/dest/l1_tx_utils/index.js +13 -0
  134. package/dest/l1_tx_utils/interfaces.d.ts +76 -0
  135. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -0
  136. package/dest/l1_tx_utils/interfaces.js +4 -0
  137. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +252 -0
  138. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -0
  139. package/dest/l1_tx_utils/l1_fee_analyzer.js +568 -0
  140. package/dest/l1_tx_utils/l1_tx_utils.d.ts +113 -0
  141. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -0
  142. package/dest/l1_tx_utils/l1_tx_utils.js +720 -0
  143. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +83 -0
  144. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -0
  145. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +357 -0
  146. package/dest/l1_tx_utils/signer.d.ts +4 -0
  147. package/dest/l1_tx_utils/signer.d.ts.map +1 -0
  148. package/dest/l1_tx_utils/signer.js +16 -0
  149. package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
  150. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  151. package/dest/l1_tx_utils/tx_delayer.js +223 -0
  152. package/dest/l1_tx_utils/types.d.ts +93 -0
  153. package/dest/l1_tx_utils/types.d.ts.map +1 -0
  154. package/dest/l1_tx_utils/types.js +36 -0
  155. package/dest/l1_tx_utils/utils.d.ts +4 -0
  156. package/dest/l1_tx_utils/utils.d.ts.map +1 -0
  157. package/dest/l1_tx_utils/utils.js +14 -0
  158. package/dest/l1_types.d.ts +6 -0
  159. package/dest/l1_types.d.ts.map +1 -0
  160. package/dest/l1_types.js +1 -0
  161. package/dest/publisher_manager.d.ts +42 -0
  162. package/dest/publisher_manager.d.ts.map +1 -0
  163. package/dest/publisher_manager.js +199 -0
  164. package/dest/queries.d.ts +25 -0
  165. package/dest/queries.d.ts.map +1 -0
  166. package/dest/queries.js +144 -0
  167. package/dest/test/blob_kzg_warmup.d.ts +19 -0
  168. package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
  169. package/dest/test/blob_kzg_warmup.js +64 -0
  170. package/dest/test/chain_monitor.d.ts +126 -0
  171. package/dest/test/chain_monitor.d.ts.map +1 -0
  172. package/dest/test/chain_monitor.js +314 -0
  173. package/dest/test/eth_cheat_codes.d.ts +260 -0
  174. package/dest/test/eth_cheat_codes.d.ts.map +1 -0
  175. package/dest/test/eth_cheat_codes.js +608 -0
  176. package/dest/test/eth_cheat_codes_with_state.d.ts +18 -0
  177. package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -0
  178. package/dest/test/eth_cheat_codes_with_state.js +34 -0
  179. package/dest/test/index.d.ts +8 -0
  180. package/dest/test/index.d.ts.map +1 -0
  181. package/dest/test/index.js +7 -0
  182. package/dest/test/recording_rpc_server.d.ts +18 -0
  183. package/dest/test/recording_rpc_server.d.ts.map +1 -0
  184. package/dest/test/recording_rpc_server.js +68 -0
  185. package/dest/test/rollup_cheat_codes.d.ts +142 -0
  186. package/dest/test/rollup_cheat_codes.d.ts.map +1 -0
  187. package/dest/test/rollup_cheat_codes.js +338 -0
  188. package/dest/test/start_anvil.d.ts +40 -0
  189. package/dest/test/start_anvil.d.ts.map +1 -0
  190. package/dest/test/start_anvil.js +210 -0
  191. package/dest/test/upgrade_utils.d.ts +12 -0
  192. package/dest/test/upgrade_utils.d.ts.map +1 -0
  193. package/dest/test/upgrade_utils.js +111 -0
  194. package/dest/types.d.ts +68 -0
  195. package/dest/types.d.ts.map +1 -0
  196. package/dest/types.js +3 -0
  197. package/dest/utils.d.ts +45 -0
  198. package/dest/utils.d.ts.map +1 -0
  199. package/dest/utils.js +260 -0
  200. package/dest/zkPassportVerifierAddress.d.ts +15 -0
  201. package/dest/zkPassportVerifierAddress.d.ts.map +1 -0
  202. package/dest/zkPassportVerifierAddress.js +11 -0
  203. package/package.json +122 -0
  204. package/src/account.ts +5 -0
  205. package/src/chain.ts +71 -0
  206. package/src/client.ts +165 -0
  207. package/src/config.ts +342 -0
  208. package/src/constants.ts +4 -0
  209. package/src/contracts/README.md +157 -0
  210. package/src/contracts/chain_state_override.ts +203 -0
  211. package/src/contracts/empire_base.ts +89 -0
  212. package/src/contracts/errors.ts +13 -0
  213. package/src/contracts/fee_asset_handler.ts +66 -0
  214. package/src/contracts/fee_asset_price_oracle.ts +284 -0
  215. package/src/contracts/fee_juice.ts +57 -0
  216. package/src/contracts/governance.ts +597 -0
  217. package/src/contracts/governance_proposer.ts +181 -0
  218. package/src/contracts/gse.ts +105 -0
  219. package/src/contracts/inbox.ts +223 -0
  220. package/src/contracts/index.ts +17 -0
  221. package/src/contracts/log.ts +13 -0
  222. package/src/contracts/multicall.ts +298 -0
  223. package/src/contracts/outbox.ts +132 -0
  224. package/src/contracts/registry.ts +161 -0
  225. package/src/contracts/rollup.ts +1502 -0
  226. package/src/contracts/slasher_contract.ts +89 -0
  227. package/src/contracts/slashing_proposer.ts +397 -0
  228. package/src/contracts/utils.ts +14 -0
  229. package/src/contracts/watch_event.ts +109 -0
  230. package/src/deploy_aztec_l1_contracts.ts +663 -0
  231. package/src/deploy_l1_contract.ts +361 -0
  232. package/src/eth-signer/eth-signer.ts +24 -0
  233. package/src/eth-signer/index.ts +1 -0
  234. package/src/forwarder_proxy.ts +107 -0
  235. package/src/foundry_binary.ts +57 -0
  236. package/src/l1_artifacts.ts +231 -0
  237. package/src/l1_contract_addresses.ts +149 -0
  238. package/src/l1_reader.ts +57 -0
  239. package/src/l1_tx_utils/README.md +177 -0
  240. package/src/l1_tx_utils/config.ts +181 -0
  241. package/src/l1_tx_utils/constants.ts +29 -0
  242. package/src/l1_tx_utils/factory.ts +64 -0
  243. package/src/l1_tx_utils/fee-strategies/index.ts +22 -0
  244. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +162 -0
  245. package/src/l1_tx_utils/fee-strategies/p75_competitive_blob_txs_only.ts +244 -0
  246. package/src/l1_tx_utils/fee-strategies/types.ts +55 -0
  247. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +107 -0
  248. package/src/l1_tx_utils/index-blobs.ts +2 -0
  249. package/src/l1_tx_utils/index.ts +15 -0
  250. package/src/l1_tx_utils/interfaces.ts +86 -0
  251. package/src/l1_tx_utils/l1_fee_analyzer.ts +874 -0
  252. package/src/l1_tx_utils/l1_tx_utils.ts +840 -0
  253. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +459 -0
  254. package/src/l1_tx_utils/signer.ts +27 -0
  255. package/src/l1_tx_utils/tx_delayer.ts +302 -0
  256. package/src/l1_tx_utils/types.ts +116 -0
  257. package/src/l1_tx_utils/utils.ts +15 -0
  258. package/src/l1_types.ts +6 -0
  259. package/src/publisher_manager.ts +239 -0
  260. package/src/queries.ts +189 -0
  261. package/src/test/blob_kzg_warmup.ts +65 -0
  262. package/src/test/chain_monitor.ts +389 -0
  263. package/src/test/eth_cheat_codes.ts +624 -0
  264. package/src/test/eth_cheat_codes_with_state.ts +38 -0
  265. package/src/test/index.ts +7 -0
  266. package/src/test/recording_rpc_server.ts +69 -0
  267. package/src/test/rollup_cheat_codes.ts +398 -0
  268. package/src/test/start_anvil.ts +255 -0
  269. package/src/test/upgrade_utils.ts +109 -0
  270. package/src/types.ts +97 -0
  271. package/src/utils.ts +344 -0
  272. package/src/zkPassportVerifierAddress.ts +15 -0
@@ -0,0 +1,459 @@
1
+ import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
2
+
3
+ import { getKeys, merge, pick, times } from '@aztec-labs/foundation/collection';
4
+ import type { EthAddress } from '@aztec-labs/foundation/eth-address';
5
+ import { type Logger, createLogger } from '@aztec-labs/foundation/log';
6
+ import { makeBackoff, retry } from '@aztec-labs/foundation/retry';
7
+ import { DateProvider } from '@aztec-labs/foundation/timer';
8
+ import { getErrorCause } from '@aztec-labs/foundation/types';
9
+ import pickBy from 'lodash.pickby';
10
+ import {
11
+ type Abi,
12
+ type Account,
13
+ type BaseError,
14
+ type BlockOverrides,
15
+ type ContractFunctionExecutionError,
16
+ type GetCodeReturnType,
17
+ type Hex,
18
+ MethodNotFoundRpcError,
19
+ MethodNotSupportedRpcError,
20
+ RpcRequestError,
21
+ type StateOverride,
22
+ decodeErrorResult,
23
+ formatGwei,
24
+ getContractError,
25
+ hexToBytes,
26
+ } from 'viem';
27
+
28
+ import { getL1RpcErrorCode } from '../client.js';
29
+ import type { ViemClient } from '../types.js';
30
+ import { type L1TxUtilsConfig, defaultL1TxUtilsConfig, l1TxUtilsConfigMappings } from './config.js';
31
+ import {
32
+ BLOCK_TIME_MS,
33
+ MAX_L1_TX_LIMIT,
34
+ MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE,
35
+ MIN_REPLACEMENT_BUMP_PERCENTAGE,
36
+ WEI_CONST,
37
+ } from './constants.js';
38
+ import { P75AllTxsPriorityFeeStrategy, type PriorityFeeStrategy } from './fee-strategies/index.js';
39
+ import type { FeesPerGas, L1BlobInputs, L1TxRequest, TransactionStats } from './types.js';
40
+ import { getCalldataGasUsage, tryGetCustomErrorNameContractFunction } from './utils.js';
41
+
42
+ // Change this to the current strategy we want to use
43
+ const CurrentStrategy: PriorityFeeStrategy = P75AllTxsPriorityFeeStrategy;
44
+
45
+ /** Error code returned by eth_simulateV1 for `insufficient funds for gas * price + value`. */
46
+ const INSUFFICIENT_FUNDS_RPC_ERROR_CODE = -38014;
47
+
48
+ /**
49
+ * Returns true when an RPC transport error reports the node's upfront funds check
50
+ * (sender balance >= gasLimit * maxFeePerGas) rejecting the request. Matches on the message as well as on the
51
+ * error code, since RPC gateways differ in how they wrap node errors. Apply this only to transport errors: an
52
+ * execution revert string can mention insufficient funds without being this kind of rejection.
53
+ */
54
+ function isInsufficientFundsRpcError(err: unknown): boolean {
55
+ if (getL1RpcErrorCode(err) === INSUFFICIENT_FUNDS_RPC_ERROR_CODE) {
56
+ return true;
57
+ }
58
+ const messages = [err, getErrorCause(err, RpcRequestError)].map(e => (e instanceof Error ? e.message : '')).join(' ');
59
+ return /insufficient funds/i.test(messages);
60
+ }
61
+
62
+ export class ReadOnlyL1TxUtils {
63
+ public config: Required<L1TxUtilsConfig>;
64
+ protected interrupted = false;
65
+
66
+ constructor(
67
+ public client: ViemClient,
68
+ protected logger: Logger = createLogger('ethereum:readonly-l1-utils'),
69
+ public readonly dateProvider: DateProvider,
70
+ config?: Partial<L1TxUtilsConfig>,
71
+ protected debugMaxGasLimit: boolean = false,
72
+ ) {
73
+ this.config = merge(defaultL1TxUtilsConfig, pick(config || {}, ...getKeys(l1TxUtilsConfigMappings)));
74
+ }
75
+
76
+ public interrupt() {
77
+ this.interrupted = true;
78
+ }
79
+
80
+ public restart() {
81
+ this.interrupted = false;
82
+ }
83
+
84
+ public getBlock() {
85
+ return this.client.getBlock();
86
+ }
87
+
88
+ public getBlockNumber() {
89
+ return this.client.getBlockNumber();
90
+ }
91
+
92
+ public getCode(address: EthAddress): Promise<GetCodeReturnType> {
93
+ return this.client.getCode({ address: address.toString() });
94
+ }
95
+
96
+ /**
97
+ * Gets the EIP-1559 fees per gas to send with, derived from the current network fees and bounded by the config
98
+ */
99
+ public async getFeesPerGas(
100
+ gasConfigOverrides?: L1TxUtilsConfig,
101
+ isBlobTx: boolean = false,
102
+ attempt: number = 0,
103
+ previousFeesPerGas?: typeof attempt extends 0 ? never : FeesPerGas,
104
+ ): Promise<FeesPerGas> {
105
+ const gasConfig = merge(this.config, gasConfigOverrides);
106
+
107
+ // Execute strategy - it handles all RPC calls internally and returns everything we need
108
+ const strategyResult = await retry(
109
+ () =>
110
+ CurrentStrategy.execute(this.client, {
111
+ gasConfig,
112
+ isBlobTx,
113
+ logger: this.logger,
114
+ }),
115
+ 'Executing priority fee strategy',
116
+ makeBackoff(times(2, () => 0)),
117
+ this.logger,
118
+ true,
119
+ );
120
+
121
+ const { latestBlock, blobBaseFee, priorityFee: strategyPriorityFee } = strategyResult;
122
+
123
+ // Extract base fee from latest block
124
+ const baseFee = latestBlock.baseFeePerGas ?? 0n;
125
+
126
+ // Handle blob base fee
127
+ if (isBlobTx && blobBaseFee === undefined) {
128
+ this.logger?.warn('Failed to get L1 blob base fee', attempt);
129
+ }
130
+
131
+ let priorityFee = strategyPriorityFee;
132
+
133
+ // Apply minimum priority fee floor if configured
134
+ if (gasConfig.minimumPriorityFeePerGas) {
135
+ const minimumPriorityFee = BigInt(Math.trunc(gasConfig.minimumPriorityFeePerGas * Number(WEI_CONST)));
136
+ if (priorityFee < minimumPriorityFee) {
137
+ this.logger?.debug('Applying minimum priority fee floor', {
138
+ calculatedPriorityFee: formatGwei(priorityFee),
139
+ minimumPriorityFeePerGas: gasConfig.minimumPriorityFeePerGas,
140
+ appliedFee: formatGwei(minimumPriorityFee),
141
+ });
142
+ priorityFee = minimumPriorityFee;
143
+ }
144
+ }
145
+ let maxFeePerGas = baseFee;
146
+
147
+ let maxFeePerBlobGas = blobBaseFee ?? 0n;
148
+
149
+ // Bump base fee so it's valid for next blocks if it stalls
150
+ const numBlocks = Math.ceil(gasConfig.stallTimeMs! / BLOCK_TIME_MS);
151
+ for (let i = 0; i < numBlocks; i++) {
152
+ // each block can go up 12.5% from previous baseFee
153
+ // ceil, (a+b-1)/b, to avoid truncation at small values (e.g. 1 wei blob base fee)
154
+ maxFeePerGas = (maxFeePerGas * (1_000n + 125n) + 999n) / 1_000n;
155
+ // same for blob gas fee
156
+ maxFeePerBlobGas = (maxFeePerBlobGas * (1_000n + 125n) + 999n) / 1_000n;
157
+ }
158
+
159
+ if (attempt > 0) {
160
+ const configBump =
161
+ gasConfig.priorityFeeRetryBumpPercentage ?? defaultL1TxUtilsConfig.priorityFeeRetryBumpPercentage!;
162
+
163
+ // if this is a blob tx, we have to use the blob bump percentage
164
+ const minBumpPercentage = isBlobTx ? MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE : MIN_REPLACEMENT_BUMP_PERCENTAGE;
165
+
166
+ const bumpPercentage = configBump > minBumpPercentage ? configBump : minBumpPercentage;
167
+ // Calculate minimum required fees based on previous attempt
168
+ // multiply by 100 & divide by 100 to maintain some precision
169
+ const minPriorityFee =
170
+ (previousFeesPerGas!.maxPriorityFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
171
+ const minMaxFee = (previousFeesPerGas!.maxFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
172
+
173
+ // Apply bump percentage to competitive fee
174
+ const competitivePriorityFee = (priorityFee * (100_00n + BigInt(configBump * 1_00))) / 100_00n;
175
+
176
+ this.logger?.debug(`Speed-up attempt ${attempt}: using competitive fee strategy`, {
177
+ networkEstimate: formatGwei(priorityFee),
178
+ competitiveFee: formatGwei(competitivePriorityFee),
179
+ minRequired: formatGwei(minPriorityFee),
180
+ bumpPercentage: configBump,
181
+ });
182
+
183
+ // Use maximum between competitive fee and minimum required bump
184
+ const finalPriorityFee = competitivePriorityFee > minPriorityFee ? competitivePriorityFee : minPriorityFee;
185
+ const feeSource = finalPriorityFee === competitivePriorityFee ? 'competitive' : 'minimum-bump';
186
+
187
+ priorityFee = finalPriorityFee;
188
+ // Add the final priority fee to maxFeePerGas
189
+ maxFeePerGas += finalPriorityFee;
190
+ maxFeePerGas = maxFeePerGas > minMaxFee ? maxFeePerGas : minMaxFee;
191
+
192
+ this.logger?.debug(`Speed-up fee decision: using ${feeSource} fee`, {
193
+ finalPriorityFee: formatGwei(finalPriorityFee),
194
+ });
195
+ } else {
196
+ // First attempt: apply configured bump percentage to competitive fee
197
+ // multiply by 100 & divide by 100 to maintain some precision
198
+ priorityFee = (priorityFee * (100_00n + BigInt((gasConfig.priorityFeeBumpPercentage || 0) * 1_00))) / 100_00n;
199
+ this.logger?.debug('Initial transaction: using competitive fee from market analysis', {
200
+ networkEstimate: formatGwei(priorityFee),
201
+ });
202
+ maxFeePerGas += priorityFee;
203
+ }
204
+
205
+ // maxGwei and maxBlobGwei are hard limits
206
+ const effectiveMaxGwei = BigInt(Math.trunc(gasConfig.maxGwei! * Number(WEI_CONST)));
207
+ const effectiveMaxBlobGwei = BigInt(Math.trunc(gasConfig.maxBlobGwei! * Number(WEI_CONST)));
208
+
209
+ // Ensure we don't exceed maxGwei
210
+ if (effectiveMaxGwei > 0n) {
211
+ maxFeePerGas = maxFeePerGas > effectiveMaxGwei ? effectiveMaxGwei : maxFeePerGas;
212
+ }
213
+
214
+ // Ensure we don't exceed maxBlobGwei
215
+ if (maxFeePerBlobGas && effectiveMaxBlobGwei > 0n) {
216
+ maxFeePerBlobGas = maxFeePerBlobGas > effectiveMaxBlobGwei ? effectiveMaxBlobGwei : maxFeePerBlobGas;
217
+ }
218
+
219
+ // Ensure priority fee doesn't exceed max fee
220
+ const maxPriorityFeePerGas = priorityFee > maxFeePerGas ? maxFeePerGas : priorityFee;
221
+
222
+ if (attempt > 0 && previousFeesPerGas?.maxFeePerBlobGas) {
223
+ const bumpPercentage =
224
+ gasConfig.priorityFeeRetryBumpPercentage! > MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE
225
+ ? gasConfig.priorityFeeRetryBumpPercentage!
226
+ : MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE;
227
+
228
+ // calculate min blob fee based on previous attempt
229
+ const minBlobFee = (previousFeesPerGas.maxFeePerBlobGas * (100_00n + BigInt(bumpPercentage * 1_00))) / 100_00n;
230
+
231
+ // use max between current network values and min required values
232
+ maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee;
233
+ }
234
+
235
+ this.logger?.trace(
236
+ `Computed L1 gas price max fee ${formatGwei(maxFeePerGas)} and max priority fee ${formatGwei(maxPriorityFeePerGas)}`,
237
+ {
238
+ attempt,
239
+ baseFee: formatGwei(baseFee),
240
+ maxFeePerGas: formatGwei(maxFeePerGas),
241
+ maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
242
+ blobBaseFee: formatGwei(blobBaseFee ?? 0n),
243
+ maxFeePerBlobGas: formatGwei(maxFeePerBlobGas),
244
+ },
245
+ );
246
+
247
+ return {
248
+ maxFeePerGas,
249
+ maxPriorityFeePerGas,
250
+ ...(maxFeePerBlobGas && { maxFeePerBlobGas: maxFeePerBlobGas }),
251
+ };
252
+ }
253
+
254
+ /**
255
+ * Estimates gas and adds buffer
256
+ */
257
+ public async estimateGas(
258
+ account: Account | Hex,
259
+ request: L1TxRequest,
260
+ _gasConfig?: L1TxUtilsConfig,
261
+ _blobInputs?: L1BlobInputs,
262
+ ): Promise<bigint> {
263
+ const gasConfig = { ...this.config, ..._gasConfig };
264
+ let initialEstimate = 0n;
265
+ if (_blobInputs) {
266
+ // @note requests with blobs also require maxFeePerBlobGas to be set.
267
+ // Use 2x buffer for maxFeePerBlobGas to avoid stale fees and to pass EIP-4844 validation (even if it is a gas estimation call).
268
+ // 1. maxFeePerBlobGas >= blobBaseFee
269
+ // 2. account balance >= gas * maxFeePerGas + maxFeePerBlobGas * blobCount + value
270
+ const feesPerGas = await this.getFeesPerGas(gasConfig, true, 0);
271
+ initialEstimate = await this.client.estimateGas({
272
+ account,
273
+ ...request,
274
+ ..._blobInputs,
275
+ maxFeePerBlobGas: feesPerGas.maxFeePerBlobGas! * 2n,
276
+ gas: MAX_L1_TX_LIMIT,
277
+ blockTag: 'latest',
278
+ });
279
+
280
+ this.logger?.trace(`Estimated gas for blob tx: ${initialEstimate}`);
281
+ } else {
282
+ initialEstimate = await this.client.estimateGas({
283
+ account,
284
+ ...request,
285
+ gas: MAX_L1_TX_LIMIT,
286
+ blockTag: 'latest',
287
+ });
288
+ this.logger?.trace(`Estimated gas for non-blob tx: ${initialEstimate}`);
289
+ }
290
+
291
+ // Add buffer based on either fixed amount or percentage
292
+ const withBuffer = this.bumpGasLimit(initialEstimate, gasConfig);
293
+
294
+ return withBuffer;
295
+ }
296
+
297
+ async getTransactionStats(txHash: string): Promise<TransactionStats | undefined> {
298
+ const tx = await this.client.getTransaction({ hash: txHash as Hex });
299
+ if (!tx) {
300
+ return undefined;
301
+ }
302
+ const calldata = hexToBytes(tx.input);
303
+ return {
304
+ sender: tx.from.toString(),
305
+ transactionHash: tx.hash,
306
+ calldataSize: calldata.length,
307
+ calldataGas: getCalldataGasUsage(calldata),
308
+ };
309
+ }
310
+
311
+ public async tryGetErrorFromRevertedTx(
312
+ data: Hex,
313
+ args: {
314
+ args: readonly any[];
315
+ functionName: string;
316
+ abi: Abi;
317
+ address: Hex;
318
+ },
319
+ blobInputs: (L1BlobInputs & { maxFeePerBlobGas: bigint }) | undefined,
320
+ stateOverride: StateOverride = [],
321
+ ) {
322
+ try {
323
+ await this.client.simulateContract({
324
+ ...args,
325
+ account: this.client.account,
326
+ stateOverride,
327
+ });
328
+ this.logger?.trace('Simulated blob tx', { blobInputs });
329
+ // If the above passes, we have a blob error. We cannot simulate blob txs, and failed txs no longer throw errors.
330
+ // Strangely, the only way to throw the revert reason as an error and provide blobs is prepareTransactionRequest.
331
+ // See: https://github.com/wevm/viem/issues/2075
332
+ // This throws a EstimateGasExecutionError with the custom error information:
333
+ const request = blobInputs
334
+ ? {
335
+ account: this.client.account,
336
+ to: args.address,
337
+ data,
338
+ blobs: blobInputs.blobs,
339
+ kzg: blobInputs.kzg,
340
+ maxFeePerBlobGas: blobInputs.maxFeePerBlobGas,
341
+ }
342
+ : {
343
+ account: this.client.account,
344
+ to: args.address,
345
+ data,
346
+ };
347
+ this.logger?.trace('Preparing tx', { request });
348
+ await this.client.prepareTransactionRequest(request);
349
+ this.logger?.trace('Prepared tx');
350
+ return undefined;
351
+ } catch (simulationErr: any) {
352
+ // If we don't have a ContractFunctionExecutionError, we have a blob related error => use getContractError to get the error msg.
353
+ const contractErr =
354
+ simulationErr.name === 'ContractFunctionExecutionError'
355
+ ? simulationErr
356
+ : getContractError(simulationErr as BaseError, {
357
+ args: [],
358
+ abi: args.abi,
359
+ functionName: args.functionName,
360
+ address: args.address,
361
+ });
362
+ if (contractErr.name === 'ContractFunctionExecutionError') {
363
+ const execErr = contractErr as ContractFunctionExecutionError;
364
+ return tryGetCustomErrorNameContractFunction(execErr);
365
+ }
366
+ this.logger?.error(`Error getting error from simulation`, simulationErr);
367
+ }
368
+ }
369
+
370
+ public async simulate(
371
+ request: L1TxRequest & { gas?: bigint; from?: Hex },
372
+ blockOverrides: BlockOverrides<bigint, number> = {},
373
+ stateOverrides: StateOverride = [],
374
+ abi: Abi = RollupAbi,
375
+ _gasConfig?: L1TxUtilsConfig & { fallbackGasEstimate?: bigint },
376
+ ): Promise<{ gasUsed: bigint; result: `0x${string}` }> {
377
+ const gasConfig = { ...this.config, ..._gasConfig };
378
+
379
+ const call: any = {
380
+ to: request.to!,
381
+ data: request.data,
382
+ ...(request.from && { from: request.from }),
383
+ };
384
+
385
+ return await this._simulate(call, blockOverrides, stateOverrides, gasConfig, abi);
386
+ }
387
+
388
+ protected async _simulate(
389
+ call: any,
390
+ blockOverrides: BlockOverrides<bigint, number> = {},
391
+ stateOverrides: StateOverride = [],
392
+ gasConfig: L1TxUtilsConfig & { fallbackGasEstimate?: bigint },
393
+ abi: Abi,
394
+ ) {
395
+ const simulateBlocks = () =>
396
+ this.client.simulateBlocks({
397
+ validation: false,
398
+ blocks: [
399
+ {
400
+ blockOverrides,
401
+ stateOverrides,
402
+ calls: [call],
403
+ },
404
+ ],
405
+ });
406
+
407
+ // Only the request itself is wrapped, so that transport-level rejections are never confused with an
408
+ // execution revert (whose decoded revert string could well mention insufficient funds too).
409
+ let result: Awaited<ReturnType<typeof simulateBlocks>>;
410
+ try {
411
+ result = await simulateBlocks();
412
+ } catch (err) {
413
+ if (getErrorCause(err, MethodNotFoundRpcError) || getErrorCause(err, MethodNotSupportedRpcError)) {
414
+ if (gasConfig.fallbackGasEstimate) {
415
+ this.logger?.warn(
416
+ `Node does not support eth_simulateV1 API. Using fallback gas estimate: ${gasConfig.fallbackGasEstimate}`,
417
+ );
418
+ return { gasUsed: gasConfig.fallbackGasEstimate, result: '0x' as `0x${string}` };
419
+ }
420
+ this.logger?.error('Node does not support eth_simulateV1 API');
421
+ }
422
+ if (isInsufficientFundsRpcError(err)) {
423
+ const rpcError = getErrorCause(err, RpcRequestError);
424
+ const providerMessage = rpcError?.details ?? (err instanceof Error ? err.message : String(err));
425
+ const gas = call?.gas !== undefined ? `, gas ${call.gas}` : '';
426
+ throw new Error(
427
+ `L1 node rejected the eth_simulateV1 request with an insufficient funds error before executing it ` +
428
+ `(sender ${call?.from ?? 'unknown'}${gas}): ${providerMessage}`,
429
+ { cause: err },
430
+ );
431
+ }
432
+ throw err;
433
+ }
434
+
435
+ if (result[0].calls[0].status === 'failure') {
436
+ this.logger?.error('L1 transaction simulation failed', result[0].calls[0].error);
437
+ const decodedError = decodeErrorResult({ abi, data: result[0].calls[0].data });
438
+
439
+ throw new Error(
440
+ `L1 transaction simulation failed with error ${decodedError.errorName}(${decodedError.args?.join(',')})`,
441
+ );
442
+ }
443
+ this.logger?.debug(`L1 transaction simulation succeeded`, { ...result[0].calls[0] });
444
+ return { gasUsed: result[0].gasUsed, result: result[0].calls[0].data as `0x${string}` };
445
+ }
446
+
447
+ public bumpGasLimit(gasLimit: bigint, _gasConfig?: L1TxUtilsConfig): bigint {
448
+ const gasConfig = { ...this.config, ..._gasConfig };
449
+ const bumpedGasLimit = gasLimit + (gasLimit * BigInt((gasConfig?.gasLimitBufferPercentage || 0) * 1_00)) / 100_00n;
450
+
451
+ const cleanGasConfig = pickBy(gasConfig, (_, key) => key in l1TxUtilsConfigMappings);
452
+ this.logger?.trace(`Bumping gas limit from ${gasLimit} to ${bumpedGasLimit}`, {
453
+ gasLimit,
454
+ gasConfig: cleanGasConfig,
455
+ bumpedGasLimit,
456
+ });
457
+ return bumpedGasLimit;
458
+ }
459
+ }
@@ -0,0 +1,27 @@
1
+ import { EthAddress } from '@aztec-labs/foundation/eth-address';
2
+ import type { ViemTransactionSignature } from '@aztec-labs/foundation/eth-signature';
3
+ import { type TransactionSerializable, type WalletClient, parseTransaction } from 'viem';
4
+
5
+ import type { SigningCallback } from './types.js';
6
+
7
+ export function createViemSigner(client: WalletClient) {
8
+ const signer: SigningCallback = async (
9
+ tx: TransactionSerializable,
10
+ _address: EthAddress,
11
+ ): Promise<ViemTransactionSignature> => {
12
+ const signedTx = await client.signTransaction(tx as any);
13
+
14
+ const parsed = parseTransaction(signedTx);
15
+
16
+ if (!parsed.r || !parsed.s || (parsed.yParity !== 0 && parsed.yParity !== 1)) {
17
+ throw new Error('Failed to extract signature from viem signed transaction');
18
+ }
19
+
20
+ return {
21
+ r: parsed.r,
22
+ s: parsed.s,
23
+ yParity: parsed.yParity,
24
+ };
25
+ };
26
+ return signer;
27
+ }