@aztec/ethereum 3.0.0-canary.a9708bd → 3.0.0-devnet.20251212

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 (195) hide show
  1. package/dest/account.d.ts +1 -1
  2. package/dest/chain.d.ts +1 -1
  3. package/dest/client.d.ts +2 -2
  4. package/dest/client.d.ts.map +1 -1
  5. package/dest/config.d.ts +16 -8
  6. package/dest/config.d.ts.map +1 -1
  7. package/dest/config.js +160 -62
  8. package/dest/constants.d.ts +1 -1
  9. package/dest/contracts/empire_base.d.ts +7 -6
  10. package/dest/contracts/empire_base.d.ts.map +1 -1
  11. package/dest/contracts/empire_base.js +1 -1
  12. package/dest/contracts/empire_slashing_proposer.d.ts +7 -6
  13. package/dest/contracts/empire_slashing_proposer.d.ts.map +1 -1
  14. package/dest/contracts/empire_slashing_proposer.js +9 -3
  15. package/dest/contracts/errors.d.ts +1 -1
  16. package/dest/contracts/errors.d.ts.map +1 -1
  17. package/dest/contracts/fee_asset_handler.d.ts +4 -4
  18. package/dest/contracts/fee_asset_handler.d.ts.map +1 -1
  19. package/dest/contracts/fee_juice.d.ts +1 -1
  20. package/dest/contracts/fee_juice.d.ts.map +1 -1
  21. package/dest/contracts/governance.d.ts +16 -16
  22. package/dest/contracts/governance.d.ts.map +1 -1
  23. package/dest/contracts/governance.js +7 -3
  24. package/dest/contracts/governance_proposer.d.ts +6 -6
  25. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  26. package/dest/contracts/governance_proposer.js +9 -4
  27. package/dest/contracts/gse.d.ts +1 -1
  28. package/dest/contracts/gse.d.ts.map +1 -1
  29. package/dest/contracts/inbox.d.ts +1 -1
  30. package/dest/contracts/inbox.d.ts.map +1 -1
  31. package/dest/contracts/index.d.ts +1 -1
  32. package/dest/contracts/multicall.d.ts +5 -7
  33. package/dest/contracts/multicall.d.ts.map +1 -1
  34. package/dest/contracts/multicall.js +6 -4
  35. package/dest/contracts/registry.d.ts +1 -1
  36. package/dest/contracts/registry.d.ts.map +1 -1
  37. package/dest/contracts/rollup.d.ts +83 -72
  38. package/dest/contracts/rollup.d.ts.map +1 -1
  39. package/dest/contracts/rollup.js +144 -139
  40. package/dest/contracts/slasher_contract.d.ts +11 -1
  41. package/dest/contracts/slasher_contract.d.ts.map +1 -1
  42. package/dest/contracts/slasher_contract.js +18 -0
  43. package/dest/contracts/tally_slashing_proposer.d.ts +30 -9
  44. package/dest/contracts/tally_slashing_proposer.d.ts.map +1 -1
  45. package/dest/contracts/tally_slashing_proposer.js +58 -8
  46. package/dest/contracts/utils.d.ts +1 -1
  47. package/dest/deploy_l1_contracts.d.ts +477 -15
  48. package/dest/deploy_l1_contracts.d.ts.map +1 -1
  49. package/dest/deploy_l1_contracts.js +610 -386
  50. package/dest/eth-signer/eth-signer.d.ts +1 -1
  51. package/dest/eth-signer/index.d.ts +1 -1
  52. package/dest/forwarder_proxy.d.ts +32 -0
  53. package/dest/forwarder_proxy.d.ts.map +1 -0
  54. package/dest/forwarder_proxy.js +93 -0
  55. package/dest/l1_artifacts.d.ts +14258 -6015
  56. package/dest/l1_artifacts.d.ts.map +1 -1
  57. package/dest/l1_artifacts.js +10 -5
  58. package/dest/l1_contract_addresses.d.ts +8 -4
  59. package/dest/l1_contract_addresses.d.ts.map +1 -1
  60. package/dest/l1_contract_addresses.js +16 -26
  61. package/dest/l1_reader.d.ts +4 -2
  62. package/dest/l1_reader.d.ts.map +1 -1
  63. package/dest/l1_reader.js +14 -8
  64. package/dest/l1_tx_utils/config.d.ts +59 -0
  65. package/dest/l1_tx_utils/config.d.ts.map +1 -0
  66. package/dest/l1_tx_utils/config.js +96 -0
  67. package/dest/l1_tx_utils/constants.d.ts +6 -0
  68. package/dest/l1_tx_utils/constants.d.ts.map +1 -0
  69. package/dest/l1_tx_utils/constants.js +14 -0
  70. package/dest/l1_tx_utils/factory.d.ts +24 -0
  71. package/dest/l1_tx_utils/factory.d.ts.map +1 -0
  72. package/dest/l1_tx_utils/factory.js +12 -0
  73. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +41 -0
  74. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -0
  75. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +48 -0
  76. package/dest/l1_tx_utils/index-blobs.d.ts +3 -0
  77. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -0
  78. package/dest/l1_tx_utils/index-blobs.js +2 -0
  79. package/dest/l1_tx_utils/index.d.ts +10 -0
  80. package/dest/l1_tx_utils/index.d.ts.map +1 -0
  81. package/dest/l1_tx_utils/index.js +10 -0
  82. package/dest/l1_tx_utils/interfaces.d.ts +76 -0
  83. package/dest/l1_tx_utils/interfaces.d.ts.map +1 -0
  84. package/dest/l1_tx_utils/interfaces.js +4 -0
  85. package/dest/l1_tx_utils/l1_tx_utils.d.ts +94 -0
  86. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -0
  87. package/dest/l1_tx_utils/l1_tx_utils.js +623 -0
  88. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +26 -0
  89. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +1 -0
  90. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +26 -0
  91. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +94 -0
  92. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -0
  93. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +431 -0
  94. package/dest/l1_tx_utils/signer.d.ts +4 -0
  95. package/dest/l1_tx_utils/signer.d.ts.map +1 -0
  96. package/dest/l1_tx_utils/signer.js +16 -0
  97. package/dest/l1_tx_utils/types.d.ts +67 -0
  98. package/dest/l1_tx_utils/types.d.ts.map +1 -0
  99. package/dest/l1_tx_utils/types.js +26 -0
  100. package/dest/l1_tx_utils/utils.d.ts +4 -0
  101. package/dest/l1_tx_utils/utils.d.ts.map +1 -0
  102. package/dest/l1_tx_utils/utils.js +14 -0
  103. package/dest/l1_types.d.ts +1 -1
  104. package/dest/publisher_manager.d.ts +8 -3
  105. package/dest/publisher_manager.d.ts.map +1 -1
  106. package/dest/publisher_manager.js +36 -8
  107. package/dest/queries.d.ts +1 -1
  108. package/dest/queries.d.ts.map +1 -1
  109. package/dest/queries.js +13 -12
  110. package/dest/test/chain_monitor.d.ts +33 -19
  111. package/dest/test/chain_monitor.d.ts.map +1 -1
  112. package/dest/test/chain_monitor.js +100 -33
  113. package/dest/test/delayed_tx_utils.d.ts +3 -3
  114. package/dest/test/delayed_tx_utils.d.ts.map +1 -1
  115. package/dest/test/delayed_tx_utils.js +2 -2
  116. package/dest/test/eth_cheat_codes.d.ts +36 -14
  117. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  118. package/dest/test/eth_cheat_codes.js +124 -31
  119. package/dest/test/eth_cheat_codes_with_state.d.ts +1 -1
  120. package/dest/test/eth_cheat_codes_with_state.d.ts.map +1 -1
  121. package/dest/test/index.d.ts +1 -1
  122. package/dest/test/rollup_cheat_codes.d.ts +23 -20
  123. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  124. package/dest/test/rollup_cheat_codes.js +79 -42
  125. package/dest/test/start_anvil.d.ts +2 -1
  126. package/dest/test/start_anvil.d.ts.map +1 -1
  127. package/dest/test/start_anvil.js +2 -1
  128. package/dest/test/tx_delayer.d.ts +1 -1
  129. package/dest/test/tx_delayer.d.ts.map +1 -1
  130. package/dest/test/tx_delayer.js +3 -2
  131. package/dest/test/upgrade_utils.d.ts +1 -1
  132. package/dest/test/upgrade_utils.d.ts.map +1 -1
  133. package/dest/test/upgrade_utils.js +3 -2
  134. package/dest/types.d.ts +57 -2
  135. package/dest/types.d.ts.map +1 -1
  136. package/dest/utils.d.ts +2 -2
  137. package/dest/utils.d.ts.map +1 -1
  138. package/dest/utils.js +10 -161
  139. package/dest/zkPassportVerifierAddress.d.ts +1 -1
  140. package/dest/zkPassportVerifierAddress.js +1 -1
  141. package/package.json +27 -13
  142. package/src/client.ts +1 -1
  143. package/src/config.ts +177 -65
  144. package/src/contracts/empire_base.ts +7 -6
  145. package/src/contracts/empire_slashing_proposer.ts +18 -8
  146. package/src/contracts/fee_asset_handler.ts +1 -1
  147. package/src/contracts/governance.ts +3 -3
  148. package/src/contracts/governance_proposer.ts +14 -9
  149. package/src/contracts/multicall.ts +12 -10
  150. package/src/contracts/rollup.ts +170 -171
  151. package/src/contracts/slasher_contract.ts +22 -0
  152. package/src/contracts/tally_slashing_proposer.ts +63 -12
  153. package/src/deploy_l1_contracts.ts +610 -337
  154. package/src/forwarder_proxy.ts +108 -0
  155. package/src/l1_artifacts.ts +14 -6
  156. package/src/l1_contract_addresses.ts +17 -26
  157. package/src/l1_reader.ts +17 -9
  158. package/src/l1_tx_utils/README.md +177 -0
  159. package/src/l1_tx_utils/config.ts +161 -0
  160. package/src/l1_tx_utils/constants.ts +18 -0
  161. package/src/l1_tx_utils/factory.ts +64 -0
  162. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +119 -0
  163. package/src/l1_tx_utils/index-blobs.ts +2 -0
  164. package/src/l1_tx_utils/index.ts +12 -0
  165. package/src/l1_tx_utils/interfaces.ts +86 -0
  166. package/src/l1_tx_utils/l1_tx_utils.ts +738 -0
  167. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +77 -0
  168. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +557 -0
  169. package/src/l1_tx_utils/signer.ts +28 -0
  170. package/src/l1_tx_utils/types.ts +85 -0
  171. package/src/l1_tx_utils/utils.ts +16 -0
  172. package/src/publisher_manager.ts +51 -9
  173. package/src/queries.ts +16 -8
  174. package/src/test/chain_monitor.ts +118 -36
  175. package/src/test/delayed_tx_utils.ts +2 -2
  176. package/src/test/eth_cheat_codes.ts +149 -30
  177. package/src/test/rollup_cheat_codes.ts +94 -52
  178. package/src/test/start_anvil.ts +2 -0
  179. package/src/test/tx_delayer.ts +4 -2
  180. package/src/test/upgrade_utils.ts +3 -2
  181. package/src/types.ts +62 -0
  182. package/src/utils.ts +12 -184
  183. package/src/zkPassportVerifierAddress.ts +1 -1
  184. package/dest/index.d.ts +0 -18
  185. package/dest/index.d.ts.map +0 -1
  186. package/dest/index.js +0 -17
  187. package/dest/l1_tx_utils.d.ts +0 -250
  188. package/dest/l1_tx_utils.d.ts.map +0 -1
  189. package/dest/l1_tx_utils.js +0 -826
  190. package/dest/l1_tx_utils_with_blobs.d.ts +0 -19
  191. package/dest/l1_tx_utils_with_blobs.d.ts.map +0 -1
  192. package/dest/l1_tx_utils_with_blobs.js +0 -85
  193. package/src/index.ts +0 -17
  194. package/src/l1_tx_utils.ts +0 -1105
  195. package/src/l1_tx_utils_with_blobs.ts +0 -144
@@ -0,0 +1,26 @@
1
+ import { Blob } from '@aztec/blob-lib';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import { L1TxUtils } from './l1_tx_utils.js';
4
+ import { createViemSigner } from './signer.js';
5
+ /** Extends L1TxUtils with the capability to cancel blobs. This needs to be a separate class so we don't require a dependency on blob-lib unnecessarily. */ export class L1TxUtilsWithBlobs extends L1TxUtils {
6
+ /** Makes empty blob inputs for the cancellation tx. */ makeEmptyBlobInputs(maxFeePerBlobGas) {
7
+ const blobData = new Uint8Array(131072).fill(0);
8
+ const kzg = Blob.getViemKzgInstance();
9
+ return {
10
+ blobs: [
11
+ blobData
12
+ ],
13
+ kzg,
14
+ maxFeePerBlobGas
15
+ };
16
+ }
17
+ }
18
+ export function createL1TxUtilsWithBlobsFromViemWallet(client, deps = {}, config = {}, debugMaxGasLimit = false) {
19
+ return new L1TxUtilsWithBlobs(client, EthAddress.fromString(client.account.address), createViemSigner(client), deps.logger, deps.dateProvider, config, debugMaxGasLimit, deps.store, deps.metrics);
20
+ }
21
+ export function createL1TxUtilsWithBlobsFromEthSigner(client, signer, deps = {}, config = {}, debugMaxGasLimit = false) {
22
+ const callback = async (transaction, _signingAddress)=>{
23
+ return (await signer.signTransaction(transaction)).toViemTransactionSignature();
24
+ };
25
+ return new L1TxUtilsWithBlobs(client, signer.address, callback, deps.logger, deps.dateProvider, config, debugMaxGasLimit, deps.store, deps.metrics);
26
+ }
@@ -0,0 +1,94 @@
1
+ import { type Logger } from '@aztec/foundation/log';
2
+ import { DateProvider } from '@aztec/foundation/timer';
3
+ import { type Abi, type Account, type BlockOverrides, type Hex, type StateOverride } from 'viem';
4
+ import type { ViemClient } from '../types.js';
5
+ import { type L1TxUtilsConfig } from './config.js';
6
+ import type { GasPrice, L1BlobInputs, L1TxRequest, TransactionStats } from './types.js';
7
+ export declare class ReadOnlyL1TxUtils {
8
+ client: ViemClient;
9
+ protected logger: Logger;
10
+ readonly dateProvider: DateProvider;
11
+ protected debugMaxGasLimit: boolean;
12
+ config: Required<L1TxUtilsConfig>;
13
+ protected interrupted: boolean;
14
+ constructor(client: ViemClient, logger: Logger | undefined, dateProvider: DateProvider, config?: Partial<L1TxUtilsConfig>, debugMaxGasLimit?: boolean);
15
+ interrupt(): void;
16
+ restart(): void;
17
+ getBlock(): Promise<{
18
+ baseFeePerGas: bigint | null;
19
+ blobGasUsed: bigint;
20
+ difficulty: bigint;
21
+ excessBlobGas: bigint;
22
+ extraData: `0x${string}`;
23
+ gasLimit: bigint;
24
+ gasUsed: bigint;
25
+ hash: `0x${string}`;
26
+ logsBloom: `0x${string}`;
27
+ miner: `0x${string}`;
28
+ mixHash: `0x${string}`;
29
+ nonce: `0x${string}`;
30
+ number: bigint;
31
+ parentBeaconBlockRoot?: `0x${string}` | undefined;
32
+ parentHash: `0x${string}`;
33
+ receiptsRoot: `0x${string}`;
34
+ sealFields: `0x${string}`[];
35
+ sha3Uncles: `0x${string}`;
36
+ size: bigint;
37
+ stateRoot: `0x${string}`;
38
+ timestamp: bigint;
39
+ totalDifficulty: bigint | null;
40
+ transactionsRoot: `0x${string}`;
41
+ uncles: `0x${string}`[];
42
+ withdrawals?: import("viem").Withdrawal[] | undefined;
43
+ withdrawalsRoot?: `0x${string}` | undefined;
44
+ transactions: `0x${string}`[];
45
+ }>;
46
+ getBlockNumber(): Promise<bigint>;
47
+ /**
48
+ * Analyzes pending transactions and recent fee history to determine a competitive priority fee.
49
+ * Falls back to network estimate if data is unavailable or fails.
50
+ * @param networkEstimateResult - Result from estimateMaxPriorityFeePerGas RPC call
51
+ * @param pendingBlockResult - Result from getBlock with pending tag RPC call
52
+ * @param feeHistoryResult - Result from getFeeHistory RPC call
53
+ * @returns A competitive priority fee based on pending txs and recent block history
54
+ */
55
+ protected getCompetitivePriorityFee(networkEstimateResult: PromiseSettledResult<bigint | null>, pendingBlockResult: PromiseSettledResult<Awaited<ReturnType<ViemClient['getBlock']>> | null>, feeHistoryResult: PromiseSettledResult<Awaited<ReturnType<ViemClient['getFeeHistory']>> | null>): bigint;
56
+ /**
57
+ * Gets the current gas price with bounds checking
58
+ */
59
+ getGasPrice(gasConfigOverrides?: L1TxUtilsConfig, isBlobTx?: boolean, attempt?: number, previousGasPrice?: typeof attempt extends 0 ? never : GasPrice): Promise<GasPrice>;
60
+ /**
61
+ * Estimates gas and adds buffer
62
+ */
63
+ estimateGas(account: Account | Hex, request: L1TxRequest, _gasConfig?: L1TxUtilsConfig, _blobInputs?: L1BlobInputs): Promise<bigint>;
64
+ getTransactionStats(txHash: string): Promise<TransactionStats | undefined>;
65
+ tryGetErrorFromRevertedTx(data: Hex, args: {
66
+ args: readonly any[];
67
+ functionName: string;
68
+ abi: Abi;
69
+ address: Hex;
70
+ }, blobInputs: (L1BlobInputs & {
71
+ maxFeePerBlobGas: bigint;
72
+ }) | undefined, stateOverride?: StateOverride): Promise<string | undefined>;
73
+ simulate(request: L1TxRequest & {
74
+ gas?: bigint;
75
+ from?: Hex;
76
+ }, blockOverrides?: BlockOverrides<bigint, number>, stateOverrides?: StateOverride, abi?: Abi, _gasConfig?: L1TxUtilsConfig & {
77
+ fallbackGasEstimate?: bigint;
78
+ }): Promise<{
79
+ gasUsed: bigint;
80
+ result: `0x${string}`;
81
+ }>;
82
+ protected _simulate(call: any, blockOverrides: BlockOverrides<bigint, number> | undefined, stateOverrides: StateOverride | undefined, gasConfig: L1TxUtilsConfig & {
83
+ fallbackGasEstimate?: bigint;
84
+ }, abi: Abi): Promise<{
85
+ gasUsed: bigint;
86
+ result: `0x${string}`;
87
+ }>;
88
+ bumpGasLimit(gasLimit: bigint, _gasConfig?: L1TxUtilsConfig): bigint;
89
+ /**
90
+ * Helper function to retry RPC calls twice
91
+ */
92
+ private tryTwice;
93
+ }
94
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhZG9ubHlfbDFfdHhfdXRpbHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sMV90eF91dGlscy9yZWFkb25seV9sMV90eF91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsS0FBSyxNQUFNLEVBQWdCLE1BQU0sdUJBQXVCLENBQUM7QUFFbEUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBSXZELE9BQU8sRUFDTCxLQUFLLEdBQUcsRUFDUixLQUFLLE9BQU8sRUFFWixLQUFLLGNBQWMsRUFFbkIsS0FBSyxHQUFHLEVBR1IsS0FBSyxhQUFhLEVBS25CLE1BQU0sTUFBTSxDQUFDO0FBRWQsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzlDLE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBbUQsTUFBTSxhQUFhLENBQUM7QUFRcEcsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFLeEYscUJBQWEsaUJBQWlCO0lBS25CLE1BQU0sRUFBRSxVQUFVO0lBQ3pCLFNBQVMsQ0FBQyxNQUFNLEVBQUUsTUFBTTthQUNSLFlBQVksRUFBRSxZQUFZO0lBRTFDLFNBQVMsQ0FBQyxnQkFBZ0IsRUFBRSxPQUFPO0lBUjlCLE1BQU0sRUFBRSxRQUFRLENBQUMsZUFBZSxDQUFDLENBQUM7SUFDekMsU0FBUyxDQUFDLFdBQVcsVUFBUztJQUU5QixZQUNTLE1BQU0sRUFBRSxVQUFVLEVBQ2YsTUFBTSxvQkFBcUQsRUFDckQsWUFBWSxFQUFFLFlBQVksRUFDMUMsTUFBTSxDQUFDLEVBQUUsT0FBTyxDQUFDLGVBQWUsQ0FBQyxFQUN2QixnQkFBZ0IsR0FBRSxPQUFlLEVBRzVDO0lBRU0sU0FBUyxTQUVmO0lBRU0sT0FBTyxTQUViO0lBRU0sUUFBUTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readonly_l1_tx_utils.d.ts","sourceRoot":"","sources":["../../src/l1_tx_utils/readonly_l1_tx_utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,431 @@
1
+ import { getKeys, median, merge, pick, times } from '@aztec/foundation/collection';
2
+ import { createLogger } from '@aztec/foundation/log';
3
+ import { makeBackoff, retry } from '@aztec/foundation/retry';
4
+ import { RollupAbi } from '@aztec/l1-artifacts/RollupAbi';
5
+ import pickBy from 'lodash.pickby';
6
+ import { MethodNotFoundRpcError, MethodNotSupportedRpcError, decodeErrorResult, formatGwei, getContractError, hexToBytes } from 'viem';
7
+ import { defaultL1TxUtilsConfig, l1TxUtilsConfigMappings } from './config.js';
8
+ import { BLOCK_TIME_MS, LARGE_GAS_LIMIT, MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE, MIN_REPLACEMENT_BUMP_PERCENTAGE, WEI_CONST } from './constants.js';
9
+ import { getCalldataGasUsage, tryGetCustomErrorNameContractFunction } from './utils.js';
10
+ const HISTORICAL_BLOCK_COUNT = 5;
11
+ export class ReadOnlyL1TxUtils {
12
+ client;
13
+ logger;
14
+ dateProvider;
15
+ debugMaxGasLimit;
16
+ config;
17
+ interrupted;
18
+ constructor(client, logger = createLogger('ethereum:readonly-l1-utils'), dateProvider, config, debugMaxGasLimit = false){
19
+ this.client = client;
20
+ this.logger = logger;
21
+ this.dateProvider = dateProvider;
22
+ this.debugMaxGasLimit = debugMaxGasLimit;
23
+ this.interrupted = false;
24
+ this.config = merge(defaultL1TxUtilsConfig, pick(config || {}, ...getKeys(l1TxUtilsConfigMappings)));
25
+ }
26
+ interrupt() {
27
+ this.interrupted = true;
28
+ }
29
+ restart() {
30
+ this.interrupted = false;
31
+ }
32
+ getBlock() {
33
+ return this.client.getBlock();
34
+ }
35
+ getBlockNumber() {
36
+ return this.client.getBlockNumber();
37
+ }
38
+ /**
39
+ * Analyzes pending transactions and recent fee history to determine a competitive priority fee.
40
+ * Falls back to network estimate if data is unavailable or fails.
41
+ * @param networkEstimateResult - Result from estimateMaxPriorityFeePerGas RPC call
42
+ * @param pendingBlockResult - Result from getBlock with pending tag RPC call
43
+ * @param feeHistoryResult - Result from getFeeHistory RPC call
44
+ * @returns A competitive priority fee based on pending txs and recent block history
45
+ */ getCompetitivePriorityFee(networkEstimateResult, pendingBlockResult, feeHistoryResult) {
46
+ const networkEstimate = networkEstimateResult.status === 'fulfilled' && typeof networkEstimateResult.value === 'bigint' ? networkEstimateResult.value : 0n;
47
+ let competitiveFee = networkEstimate;
48
+ if (pendingBlockResult.status === 'fulfilled' && pendingBlockResult.value !== null && pendingBlockResult.value.transactions && pendingBlockResult.value.transactions.length > 0) {
49
+ const pendingBlock = pendingBlockResult.value;
50
+ // Extract priority fees from pending transactions
51
+ const pendingFees = pendingBlock.transactions.map((tx)=>{
52
+ // Transaction can be just a hash string, so we need to check if it's an object
53
+ if (typeof tx === 'string') {
54
+ return 0n;
55
+ }
56
+ const fee = tx.maxPriorityFeePerGas || 0n;
57
+ // Debug: Log suspicious fees
58
+ if (fee > 100n * WEI_CONST) {
59
+ this.logger?.warn('Suspicious high priority fee in pending tx', {
60
+ txHash: tx.hash,
61
+ maxPriorityFeePerGas: formatGwei(fee),
62
+ maxFeePerGas: formatGwei(tx.maxFeePerGas || 0n),
63
+ maxFeePerBlobGas: tx.maxFeePerBlobGas ? formatGwei(tx.maxFeePerBlobGas) : 'N/A'
64
+ });
65
+ }
66
+ return fee;
67
+ }).filter((fee)=>fee > 0n);
68
+ if (pendingFees.length > 0) {
69
+ // Use 75th percentile of pending fees to be competitive
70
+ const sortedPendingFees = [
71
+ ...pendingFees
72
+ ].sort((a, b)=>a < b ? -1 : a > b ? 1 : 0);
73
+ const percentile75Index = Math.floor((sortedPendingFees.length - 1) * 0.75);
74
+ const pendingCompetitiveFee = sortedPendingFees[percentile75Index];
75
+ if (pendingCompetitiveFee > competitiveFee) {
76
+ competitiveFee = pendingCompetitiveFee;
77
+ }
78
+ this.logger?.debug('Analyzed pending transactions for competitive pricing', {
79
+ pendingTxCount: pendingFees.length,
80
+ pendingP75: formatGwei(pendingCompetitiveFee)
81
+ });
82
+ }
83
+ }
84
+ if (feeHistoryResult.status === 'fulfilled' && feeHistoryResult.value !== null && feeHistoryResult.value.reward && feeHistoryResult.value.reward.length > 0) {
85
+ const feeHistory = feeHistoryResult.value;
86
+ // Extract 75th percentile fees from each block
87
+ const percentile75Fees = feeHistory.reward.map((rewards)=>rewards[0] || 0n).filter((fee)=>fee > 0n);
88
+ if (percentile75Fees.length > 0) {
89
+ // Calculate median of the 75th percentile fees across blocks
90
+ const medianHistoricalFee = median(percentile75Fees) ?? 0n;
91
+ // Debug: Log suspicious fees from history
92
+ if (medianHistoricalFee > 100n * WEI_CONST) {
93
+ this.logger?.warn('Suspicious high fee in history', {
94
+ historicalMedian: formatGwei(medianHistoricalFee),
95
+ allP75Fees: percentile75Fees.map((f)=>formatGwei(f))
96
+ });
97
+ }
98
+ if (medianHistoricalFee > competitiveFee) {
99
+ competitiveFee = medianHistoricalFee;
100
+ }
101
+ this.logger?.debug('Analyzed fee history for competitive pricing', {
102
+ historicalMedian: formatGwei(medianHistoricalFee)
103
+ });
104
+ }
105
+ }
106
+ // Sanity check: cap competitive fee at 100x network estimate to avoid using unrealistic fees
107
+ // (e.g., Anvil returns inflated historical fees that don't reflect actual network conditions)
108
+ const maxReasonableFee = networkEstimate * 100n;
109
+ if (competitiveFee > maxReasonableFee) {
110
+ this.logger?.warn('Competitive fee exceeds sanity cap, using capped value', {
111
+ competitiveFee: formatGwei(competitiveFee),
112
+ networkEstimate: formatGwei(networkEstimate),
113
+ cappedTo: formatGwei(maxReasonableFee)
114
+ });
115
+ competitiveFee = maxReasonableFee;
116
+ }
117
+ // Log final decision
118
+ if (competitiveFee > networkEstimate) {
119
+ this.logger?.debug('Using competitive fee from market analysis', {
120
+ networkEstimate: formatGwei(networkEstimate),
121
+ competitive: formatGwei(competitiveFee)
122
+ });
123
+ }
124
+ return competitiveFee;
125
+ }
126
+ /**
127
+ * Gets the current gas price with bounds checking
128
+ */ async getGasPrice(gasConfigOverrides, isBlobTx = false, attempt = 0, previousGasPrice) {
129
+ const gasConfig = merge(this.config, gasConfigOverrides);
130
+ // Make all RPC calls in parallel upfront with retry logic
131
+ const latestBlockPromise = this.tryTwice(()=>this.client.getBlock({
132
+ blockTag: 'latest'
133
+ }), 'Getting latest block');
134
+ const networkEstimatePromise = this.tryTwice(()=>this.client.estimateMaxPriorityFeePerGas(), 'Estimating max priority fee per gas');
135
+ const pendingBlockPromise = this.tryTwice(()=>this.client.getBlock({
136
+ blockTag: 'pending',
137
+ includeTransactions: true
138
+ }), 'Getting pending block');
139
+ const feeHistoryPromise = this.tryTwice(()=>this.client.getFeeHistory({
140
+ blockCount: HISTORICAL_BLOCK_COUNT,
141
+ rewardPercentiles: [
142
+ 75
143
+ ]
144
+ }), 'Getting fee history');
145
+ const blobBaseFeePromise = isBlobTx ? this.tryTwice(()=>this.client.getBlobBaseFee(), 'Getting blob base fee') : null;
146
+ const [latestBlockResult, networkEstimateResult, pendingBlockResult, feeHistoryResult, blobBaseFeeResult] = await Promise.allSettled([
147
+ latestBlockPromise,
148
+ networkEstimatePromise,
149
+ pendingBlockPromise,
150
+ feeHistoryPromise,
151
+ blobBaseFeePromise ?? Promise.resolve(0n)
152
+ ]);
153
+ // Extract results
154
+ const baseFee = latestBlockResult.status === 'fulfilled' && typeof latestBlockResult.value === 'object' && latestBlockResult.value.baseFeePerGas ? latestBlockResult.value.baseFeePerGas : 0n;
155
+ // Get blob base fee if available
156
+ let blobBaseFee = 0n;
157
+ if (isBlobTx && blobBaseFeeResult.status === 'fulfilled' && typeof blobBaseFeeResult.value === 'bigint') {
158
+ blobBaseFee = blobBaseFeeResult.value;
159
+ } else if (isBlobTx) {
160
+ this.logger?.warn('Failed to get L1 blob base fee', attempt);
161
+ }
162
+ // Get competitive priority fee
163
+ let priorityFee = this.getCompetitivePriorityFee(networkEstimateResult, pendingBlockResult, feeHistoryResult);
164
+ // Apply minimum priority fee as a floor if configured
165
+ if (gasConfig.minimumPriorityFeePerGas) {
166
+ const minimumFee = BigInt(Math.trunc(gasConfig.minimumPriorityFeePerGas * Number(WEI_CONST)));
167
+ if (minimumFee > priorityFee) {
168
+ this.logger?.debug('Using minimum priority fee as floor', {
169
+ minimumPriorityFeePerGas: formatGwei(minimumFee),
170
+ competitiveFee: formatGwei(priorityFee)
171
+ });
172
+ priorityFee = minimumFee;
173
+ } else {
174
+ this.logger?.debug('Competitive fee exceeds minimum, using competitive fee', {
175
+ minimumPriorityFeePerGas: formatGwei(minimumFee),
176
+ competitiveFee: formatGwei(priorityFee)
177
+ });
178
+ }
179
+ }
180
+ let maxFeePerGas = baseFee;
181
+ let maxFeePerBlobGas = blobBaseFee;
182
+ // Bump base fee so it's valid for next blocks if it stalls
183
+ const numBlocks = Math.ceil(gasConfig.stallTimeMs / BLOCK_TIME_MS);
184
+ for(let i = 0; i < numBlocks; i++){
185
+ // each block can go up 12.5% from previous baseFee
186
+ maxFeePerGas = maxFeePerGas * (1_000n + 125n) / 1_000n;
187
+ // same for blob gas fee
188
+ maxFeePerBlobGas = maxFeePerBlobGas * (1_000n + 125n) / 1_000n;
189
+ }
190
+ if (attempt > 0) {
191
+ const configBump = gasConfig.priorityFeeRetryBumpPercentage ?? defaultL1TxUtilsConfig.priorityFeeRetryBumpPercentage;
192
+ // if this is a blob tx, we have to use the blob bump percentage
193
+ const minBumpPercentage = isBlobTx ? MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE : MIN_REPLACEMENT_BUMP_PERCENTAGE;
194
+ const bumpPercentage = configBump > minBumpPercentage ? configBump : minBumpPercentage;
195
+ // Calculate minimum required fees based on previous attempt
196
+ // multiply by 100 & divide by 100 to maintain some precision
197
+ const minPriorityFee = previousGasPrice.maxPriorityFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00)) / 100_00n;
198
+ const minMaxFee = previousGasPrice.maxFeePerGas * (100_00n + BigInt(bumpPercentage * 1_00)) / 100_00n;
199
+ // Apply bump percentage to competitive fee
200
+ const competitivePriorityFee = priorityFee * (100_00n + BigInt(configBump * 1_00)) / 100_00n;
201
+ this.logger?.debug(`Speed-up attempt ${attempt}: using competitive fee strategy`, {
202
+ networkEstimate: formatGwei(priorityFee),
203
+ competitiveFee: formatGwei(competitivePriorityFee),
204
+ minRequired: formatGwei(minPriorityFee),
205
+ bumpPercentage: configBump
206
+ });
207
+ // Use maximum between competitive fee and minimum required bump
208
+ const finalPriorityFee = competitivePriorityFee > minPriorityFee ? competitivePriorityFee : minPriorityFee;
209
+ const feeSource = finalPriorityFee === competitivePriorityFee ? 'competitive' : 'minimum-bump';
210
+ priorityFee = finalPriorityFee;
211
+ // Add the final priority fee to maxFeePerGas
212
+ maxFeePerGas += finalPriorityFee;
213
+ maxFeePerGas = maxFeePerGas > minMaxFee ? maxFeePerGas : minMaxFee;
214
+ this.logger?.debug(`Speed-up fee decision: using ${feeSource} fee`, {
215
+ finalPriorityFee: formatGwei(finalPriorityFee)
216
+ });
217
+ } else {
218
+ // First attempt: apply configured bump percentage to competitive fee
219
+ // multiply by 100 & divide by 100 to maintain some precision
220
+ priorityFee = priorityFee * (100_00n + BigInt((gasConfig.priorityFeeBumpPercentage || 0) * 1_00)) / 100_00n;
221
+ this.logger?.debug('Initial transaction: using competitive fee from market analysis', {
222
+ networkEstimate: formatGwei(priorityFee)
223
+ });
224
+ maxFeePerGas += priorityFee;
225
+ }
226
+ // maxGwei and maxBlobGwei are hard limits
227
+ const effectiveMaxGwei = BigInt(Math.trunc(gasConfig.maxGwei * Number(WEI_CONST)));
228
+ const effectiveMaxBlobGwei = BigInt(Math.trunc(gasConfig.maxBlobGwei * Number(WEI_CONST)));
229
+ // Ensure we don't exceed maxGwei
230
+ if (effectiveMaxGwei > 0n) {
231
+ maxFeePerGas = maxFeePerGas > effectiveMaxGwei ? effectiveMaxGwei : maxFeePerGas;
232
+ }
233
+ // Ensure we don't exceed maxBlobGwei
234
+ if (maxFeePerBlobGas && effectiveMaxBlobGwei > 0n) {
235
+ maxFeePerBlobGas = maxFeePerBlobGas > effectiveMaxBlobGwei ? effectiveMaxBlobGwei : maxFeePerBlobGas;
236
+ }
237
+ // Ensure priority fee doesn't exceed max fee
238
+ const maxPriorityFeePerGas = priorityFee > maxFeePerGas ? maxFeePerGas : priorityFee;
239
+ if (attempt > 0 && previousGasPrice?.maxFeePerBlobGas) {
240
+ const bumpPercentage = gasConfig.priorityFeeRetryBumpPercentage > MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE ? gasConfig.priorityFeeRetryBumpPercentage : MIN_BLOB_REPLACEMENT_BUMP_PERCENTAGE;
241
+ // calculate min blob fee based on previous attempt
242
+ const minBlobFee = previousGasPrice.maxFeePerBlobGas * (100_00n + BigInt(bumpPercentage * 1_00)) / 100_00n;
243
+ // use max between current network values and min required values
244
+ maxFeePerBlobGas = maxFeePerBlobGas > minBlobFee ? maxFeePerBlobGas : minBlobFee;
245
+ }
246
+ this.logger?.trace(`Computed L1 gas price max fee ${formatGwei(maxFeePerGas)} and max priority fee ${formatGwei(maxPriorityFeePerGas)}`, {
247
+ attempt,
248
+ baseFee: formatGwei(baseFee),
249
+ maxFeePerGas: formatGwei(maxFeePerGas),
250
+ maxPriorityFeePerGas: formatGwei(maxPriorityFeePerGas),
251
+ blobBaseFee: formatGwei(blobBaseFee),
252
+ maxFeePerBlobGas: formatGwei(maxFeePerBlobGas)
253
+ });
254
+ return {
255
+ maxFeePerGas,
256
+ maxPriorityFeePerGas,
257
+ ...maxFeePerBlobGas && {
258
+ maxFeePerBlobGas: maxFeePerBlobGas
259
+ }
260
+ };
261
+ }
262
+ /**
263
+ * Estimates gas and adds buffer
264
+ */ async estimateGas(account, request, _gasConfig, _blobInputs) {
265
+ const gasConfig = {
266
+ ...this.config,
267
+ ..._gasConfig
268
+ };
269
+ let initialEstimate = 0n;
270
+ if (_blobInputs) {
271
+ // @note requests with blobs also require maxFeePerBlobGas to be set
272
+ const gasPrice = await this.getGasPrice(gasConfig, true, 0);
273
+ initialEstimate = await this.client.estimateGas({
274
+ account,
275
+ ...request,
276
+ ..._blobInputs,
277
+ maxFeePerBlobGas: gasPrice.maxFeePerBlobGas,
278
+ gas: LARGE_GAS_LIMIT
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: LARGE_GAS_LIMIT
286
+ });
287
+ this.logger?.trace(`Estimated gas for non-blob tx: ${initialEstimate}`);
288
+ }
289
+ // Add buffer based on either fixed amount or percentage
290
+ const withBuffer = this.bumpGasLimit(initialEstimate, gasConfig);
291
+ return withBuffer;
292
+ }
293
+ async getTransactionStats(txHash) {
294
+ const tx = await this.client.getTransaction({
295
+ hash: txHash
296
+ });
297
+ if (!tx) {
298
+ return undefined;
299
+ }
300
+ const calldata = hexToBytes(tx.input);
301
+ return {
302
+ sender: tx.from.toString(),
303
+ transactionHash: tx.hash,
304
+ calldataSize: calldata.length,
305
+ calldataGas: getCalldataGasUsage(calldata)
306
+ };
307
+ }
308
+ async tryGetErrorFromRevertedTx(data, args, blobInputs, stateOverride = []) {
309
+ try {
310
+ await this.client.simulateContract({
311
+ ...args,
312
+ account: this.client.account,
313
+ stateOverride
314
+ });
315
+ this.logger?.trace('Simulated blob tx', {
316
+ blobInputs
317
+ });
318
+ // If the above passes, we have a blob error. We cannot simulate blob txs, and failed txs no longer throw errors.
319
+ // Strangely, the only way to throw the revert reason as an error and provide blobs is prepareTransactionRequest.
320
+ // See: https://github.com/wevm/viem/issues/2075
321
+ // This throws a EstimateGasExecutionError with the custom error information:
322
+ const request = blobInputs ? {
323
+ account: this.client.account,
324
+ to: args.address,
325
+ data,
326
+ blobs: blobInputs.blobs,
327
+ kzg: blobInputs.kzg,
328
+ maxFeePerBlobGas: blobInputs.maxFeePerBlobGas
329
+ } : {
330
+ account: this.client.account,
331
+ to: args.address,
332
+ data
333
+ };
334
+ this.logger?.trace('Preparing tx', {
335
+ request
336
+ });
337
+ await this.client.prepareTransactionRequest(request);
338
+ this.logger?.trace('Prepared tx');
339
+ return undefined;
340
+ } catch (simulationErr) {
341
+ // If we don't have a ContractFunctionExecutionError, we have a blob related error => use getContractError to get the error msg.
342
+ const contractErr = simulationErr.name === 'ContractFunctionExecutionError' ? simulationErr : getContractError(simulationErr, {
343
+ args: [],
344
+ abi: args.abi,
345
+ functionName: args.functionName,
346
+ address: args.address
347
+ });
348
+ if (contractErr.name === 'ContractFunctionExecutionError') {
349
+ const execErr = contractErr;
350
+ return tryGetCustomErrorNameContractFunction(execErr);
351
+ }
352
+ this.logger?.error(`Error getting error from simulation`, simulationErr);
353
+ }
354
+ }
355
+ async simulate(request, blockOverrides = {}, stateOverrides = [], abi = RollupAbi, _gasConfig) {
356
+ const gasConfig = {
357
+ ...this.config,
358
+ ..._gasConfig
359
+ };
360
+ const call = {
361
+ to: request.to,
362
+ data: request.data,
363
+ ...request.from && {
364
+ from: request.from
365
+ }
366
+ };
367
+ return await this._simulate(call, blockOverrides, stateOverrides, gasConfig, abi);
368
+ }
369
+ async _simulate(call, blockOverrides = {}, stateOverrides = [], gasConfig, abi) {
370
+ try {
371
+ const result = await this.client.simulateBlocks({
372
+ validation: false,
373
+ blocks: [
374
+ {
375
+ blockOverrides,
376
+ stateOverrides,
377
+ calls: [
378
+ call
379
+ ]
380
+ }
381
+ ]
382
+ });
383
+ if (result[0].calls[0].status === 'failure') {
384
+ this.logger?.error('L1 transaction simulation failed', result[0].calls[0].error);
385
+ const decodedError = decodeErrorResult({
386
+ abi,
387
+ data: result[0].calls[0].data
388
+ });
389
+ throw new Error(`L1 transaction simulation failed with error ${decodedError.errorName}(${decodedError.args?.join(',')})`);
390
+ }
391
+ this.logger?.debug(`L1 transaction simulation succeeded`, {
392
+ ...result[0].calls[0]
393
+ });
394
+ return {
395
+ gasUsed: result[0].gasUsed,
396
+ result: result[0].calls[0].data
397
+ };
398
+ } catch (err) {
399
+ if (err instanceof MethodNotFoundRpcError || err instanceof MethodNotSupportedRpcError) {
400
+ if (gasConfig.fallbackGasEstimate) {
401
+ this.logger?.warn(`Node does not support eth_simulateV1 API. Using fallback gas estimate: ${gasConfig.fallbackGasEstimate}`);
402
+ return {
403
+ gasUsed: gasConfig.fallbackGasEstimate,
404
+ result: '0x'
405
+ };
406
+ }
407
+ this.logger?.error('Node does not support eth_simulateV1 API');
408
+ }
409
+ throw err;
410
+ }
411
+ }
412
+ bumpGasLimit(gasLimit, _gasConfig) {
413
+ const gasConfig = {
414
+ ...this.config,
415
+ ..._gasConfig
416
+ };
417
+ const bumpedGasLimit = gasLimit + gasLimit * BigInt((gasConfig?.gasLimitBufferPercentage || 0) * 1_00) / 100_00n;
418
+ const cleanGasConfig = pickBy(gasConfig, (_, key)=>key in l1TxUtilsConfigMappings);
419
+ this.logger?.trace(`Bumping gas limit from ${gasLimit} to ${bumpedGasLimit}`, {
420
+ gasLimit,
421
+ gasConfig: cleanGasConfig,
422
+ bumpedGasLimit
423
+ });
424
+ return bumpedGasLimit;
425
+ }
426
+ /**
427
+ * Helper function to retry RPC calls twice
428
+ */ tryTwice(fn, description) {
429
+ return retry(fn, description, makeBackoff(times(2, ()=>0)), this.logger, true);
430
+ }
431
+ }
@@ -0,0 +1,4 @@
1
+ import { type WalletClient } from 'viem';
2
+ import type { SigningCallback } from './types.js';
3
+ export declare function createViemSigner(client: WalletClient): SigningCallback;
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbmVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbDFfdHhfdXRpbHMvc2lnbmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUdBLE9BQU8sRUFBZ0MsS0FBSyxZQUFZLEVBQW9CLE1BQU0sTUFBTSxDQUFDO0FBRXpGLE9BQU8sS0FBSyxFQUFFLGVBQWUsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUVsRCx3QkFBZ0IsZ0JBQWdCLENBQUMsTUFBTSxFQUFFLFlBQVksbUJBb0JwRCJ9
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../../src/l1_tx_utils/signer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgC,KAAK,YAAY,EAAoB,MAAM,MAAM,CAAC;AAEzF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,mBAoBpD"}
@@ -0,0 +1,16 @@
1
+ import { parseTransaction } from 'viem';
2
+ export function createViemSigner(client) {
3
+ const signer = async (tx, _address)=>{
4
+ const signedTx = await client.signTransaction(tx);
5
+ const parsed = parseTransaction(signedTx);
6
+ if (!parsed.r || !parsed.s || parsed.yParity !== 0 && parsed.yParity !== 1) {
7
+ throw new Error('Failed to extract signature from viem signed transaction');
8
+ }
9
+ return {
10
+ r: parsed.r,
11
+ s: parsed.s,
12
+ yParity: parsed.yParity
13
+ };
14
+ };
15
+ return signer;
16
+ }
@@ -0,0 +1,67 @@
1
+ import type { BlobKzgInstance } from '@aztec/blob-lib/types';
2
+ import { EthAddress } from '@aztec/foundation/eth-address';
3
+ import type { ViemTransactionSignature } from '@aztec/foundation/eth-signature';
4
+ import type { Abi, Address, Hex, TransactionReceipt, TransactionSerializable } from 'viem';
5
+ import type { L1TxUtilsConfig } from './config.js';
6
+ export interface L1TxRequest {
7
+ to: Address | null;
8
+ data?: Hex;
9
+ value?: bigint;
10
+ abi?: Abi;
11
+ }
12
+ export type L1TxConfig = Partial<L1TxUtilsConfig> & {
13
+ gasLimit?: bigint;
14
+ txTimeoutAt?: Date;
15
+ };
16
+ export interface L1BlobInputs {
17
+ blobs: Uint8Array[];
18
+ kzg: BlobKzgInstance;
19
+ maxFeePerBlobGas?: bigint;
20
+ }
21
+ export interface GasPrice {
22
+ maxFeePerGas: bigint;
23
+ maxPriorityFeePerGas: bigint;
24
+ maxFeePerBlobGas?: bigint;
25
+ }
26
+ export type TransactionStats = {
27
+ /** Address of the sender. */
28
+ sender: string;
29
+ /** Hash of the transaction. */
30
+ transactionHash: string;
31
+ /** Size in bytes of the tx calldata */
32
+ calldataSize: number;
33
+ /** Gas required to pay for the calldata inclusion (depends on size and number of zeros) */
34
+ calldataGas: number;
35
+ };
36
+ export declare enum TxUtilsState {
37
+ IDLE = 0,
38
+ SENT = 1,
39
+ SPEED_UP = 2,
40
+ CANCELLED = 3,
41
+ NOT_MINED = 4,
42
+ MINED = 5
43
+ }
44
+ export declare const TerminalTxUtilsState: TxUtilsState[];
45
+ export type L1TxState = {
46
+ id: number;
47
+ txHashes: Hex[];
48
+ cancelTxHashes: Hex[];
49
+ gasLimit: bigint;
50
+ gasPrice: GasPrice;
51
+ txConfigOverrides: L1TxConfig;
52
+ request: L1TxRequest;
53
+ status: TxUtilsState;
54
+ nonce: number;
55
+ sentAtL1Ts: Date;
56
+ lastSentAtL1Ts: Date;
57
+ receipt?: TransactionReceipt;
58
+ blobInputs: L1BlobInputs | undefined;
59
+ };
60
+ export type SigningCallback = (transaction: TransactionSerializable, signingAddress: EthAddress) => Promise<ViemTransactionSignature>;
61
+ export declare class UnknownMinedTxError extends Error {
62
+ constructor(nonce: number, account: string);
63
+ }
64
+ export declare class DroppedTransactionError extends Error {
65
+ constructor(nonce: number, account: string);
66
+ }
67
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sMV90eF91dGlscy90eXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxlQUFlLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDM0QsT0FBTyxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVoRixPQUFPLEtBQUssRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxrQkFBa0IsRUFBRSx1QkFBdUIsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUUzRixPQUFPLEtBQUssRUFBRSxlQUFlLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFbkQsTUFBTSxXQUFXLFdBQVc7SUFDMUIsRUFBRSxFQUFFLE9BQU8sR0FBRyxJQUFJLENBQUM7SUFDbkIsSUFBSSxDQUFDLEVBQUUsR0FBRyxDQUFDO0lBQ1gsS0FBSyxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ2YsR0FBRyxDQUFDLEVBQUUsR0FBRyxDQUFDO0NBQ1g7QUFFRCxNQUFNLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsR0FBRztJQUFFLFFBQVEsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUFDLFdBQVcsQ0FBQyxFQUFFLElBQUksQ0FBQTtDQUFFLENBQUM7QUFFOUYsTUFBTSxXQUFXLFlBQVk7SUFDM0IsS0FBSyxFQUFFLFVBQVUsRUFBRSxDQUFDO0lBQ3BCLEdBQUcsRUFBRSxlQUFlLENBQUM7SUFDckIsZ0JBQWdCLENBQUMsRUFBRSxNQUFNLENBQUM7Q0FDM0I7QUFFRCxNQUFNLFdBQVcsUUFBUTtJQUN2QixZQUFZLEVBQUUsTUFBTSxDQUFDO0lBQ3JCLG9CQUFvQixFQUFFLE1BQU0sQ0FBQztJQUM3QixnQkFBZ0IsQ0FBQyxFQUFFLE1BQU0sQ0FBQztDQUMzQjtBQUVELE1BQU0sTUFBTSxnQkFBZ0IsR0FBRztJQUM3Qiw2QkFBNkI7SUFDN0IsTUFBTSxFQUFFLE1BQU0sQ0FBQztJQUNmLCtCQUErQjtJQUMvQixlQUFlLEVBQUUsTUFBTSxDQUFDO0lBQ3hCLHVDQUF1QztJQUN2QyxZQUFZLEVBQUUsTUFBTSxDQUFDO0lBQ3JCLDRGQUE0RjtJQUM1RixXQUFXLEVBQUUsTUFBTSxDQUFDO0NBQ3JCLENBQUM7QUFFRixvQkFBWSxZQUFZO0lBQ3RCLElBQUksSUFBQTtJQUNKLElBQUksSUFBQTtJQUNKLFFBQVEsSUFBQTtJQUNSLFNBQVMsSUFBQTtJQUNULFNBQVMsSUFBQTtJQUNULEtBQUssSUFBQTtDQUNOO0FBRUQsZUFBTyxNQUFNLG9CQUFvQixnQkFBa0UsQ0FBQztBQUVwRyxNQUFNLE1BQU0sU0FBUyxHQUFHO0lBQ3RCLEVBQUUsRUFBRSxNQUFNLENBQUM7SUFDWCxRQUFRLEVBQUUsR0FBRyxFQUFFLENBQUM7SUFDaEIsY0FBYyxFQUFFLEdBQUcsRUFBRSxDQUFDO0lBQ3RCLFFBQVEsRUFBRSxNQUFNLENBQUM7SUFDakIsUUFBUSxFQUFFLFFBQVEsQ0FBQztJQUNuQixpQkFBaUIsRUFBRSxVQUFVLENBQUM7SUFDOUIsT0FBTyxFQUFFLFdBQVcsQ0FBQztJQUNyQixNQUFNLEVBQUUsWUFBWSxDQUFDO0lBQ3JCLEtBQUssRUFBRSxNQUFNLENBQUM7SUFDZCxVQUFVLEVBQUUsSUFBSSxDQUFDO0lBQ2pCLGNBQWMsRUFBRSxJQUFJLENBQUM7SUFDckIsT0FBTyxDQUFDLEVBQUUsa0JBQWtCLENBQUM7SUFDN0IsVUFBVSxFQUFFLFlBQVksR0FBRyxTQUFTLENBQUM7Q0FDdEMsQ0FBQztBQUVGLE1BQU0sTUFBTSxlQUFlLEdBQUcsQ0FDNUIsV0FBVyxFQUFFLHVCQUF1QixFQUNwQyxjQUFjLEVBQUUsVUFBVSxLQUN2QixPQUFPLENBQUMsd0JBQXdCLENBQUMsQ0FBQztBQUV2QyxxQkFBYSxtQkFBb0IsU0FBUSxLQUFLO0lBQzVDLFlBQVksS0FBSyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUd6QztDQUNGO0FBRUQscUJBQWEsdUJBQXdCLFNBQVEsS0FBSztJQUNoRCxZQUFZLEtBQUssRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFHekM7Q0FDRiJ9