@aztec/ethereum 0.0.1-commit.4d79d1f2d → 0.0.1-commit.4d9804df

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 (176) hide show
  1. package/dest/client.d.ts +25 -3
  2. package/dest/client.d.ts.map +1 -1
  3. package/dest/client.js +60 -8
  4. package/dest/config.d.ts +29 -8
  5. package/dest/config.d.ts.map +1 -1
  6. package/dest/config.js +68 -13
  7. package/dest/contracts/chain_state_override.d.ts +78 -0
  8. package/dest/contracts/chain_state_override.d.ts.map +1 -0
  9. package/dest/contracts/chain_state_override.js +137 -0
  10. package/dest/contracts/fee_asset_price_oracle.d.ts +105 -0
  11. package/dest/contracts/fee_asset_price_oracle.d.ts.map +1 -0
  12. package/dest/contracts/fee_asset_price_oracle.js +656 -0
  13. package/dest/contracts/governance.d.ts +108 -33
  14. package/dest/contracts/governance.d.ts.map +1 -1
  15. package/dest/contracts/governance.js +196 -15
  16. package/dest/contracts/governance_proposer.d.ts +33 -1
  17. package/dest/contracts/governance_proposer.d.ts.map +1 -1
  18. package/dest/contracts/governance_proposer.js +53 -2
  19. package/dest/contracts/gse.d.ts +4 -2
  20. package/dest/contracts/gse.d.ts.map +1 -1
  21. package/dest/contracts/gse.js +2 -2
  22. package/dest/contracts/inbox.d.ts +3 -3
  23. package/dest/contracts/inbox.d.ts.map +1 -1
  24. package/dest/contracts/inbox.js +12 -7
  25. package/dest/contracts/index.d.ts +4 -3
  26. package/dest/contracts/index.d.ts.map +1 -1
  27. package/dest/contracts/index.js +3 -2
  28. package/dest/contracts/multicall.d.ts +130 -13
  29. package/dest/contracts/multicall.d.ts.map +1 -1
  30. package/dest/contracts/multicall.js +203 -94
  31. package/dest/contracts/outbox.d.ts +27 -4
  32. package/dest/contracts/outbox.d.ts.map +1 -1
  33. package/dest/contracts/outbox.js +34 -7
  34. package/dest/contracts/registry.d.ts +3 -1
  35. package/dest/contracts/registry.d.ts.map +1 -1
  36. package/dest/contracts/registry.js +30 -1
  37. package/dest/contracts/rollup.d.ts +5419 -31
  38. package/dest/contracts/rollup.d.ts.map +1 -1
  39. package/dest/contracts/rollup.js +400 -75
  40. package/dest/contracts/{tally_slashing_proposer.d.ts → slashing_proposer.d.ts} +3 -4
  41. package/dest/contracts/slashing_proposer.d.ts.map +1 -0
  42. package/dest/contracts/{tally_slashing_proposer.js → slashing_proposer.js} +13 -15
  43. package/dest/deploy_aztec_l1_contracts.d.ts +19 -12
  44. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  45. package/dest/deploy_aztec_l1_contracts.js +61 -36
  46. package/dest/deploy_l1_contract.js +3 -3
  47. package/dest/foundry_binary.d.ts +13 -0
  48. package/dest/foundry_binary.d.ts.map +1 -0
  49. package/dest/foundry_binary.js +52 -0
  50. package/dest/l1_artifacts.d.ts +15524 -15847
  51. package/dest/l1_artifacts.d.ts.map +1 -1
  52. package/dest/l1_artifacts.js +9 -24
  53. package/dest/l1_contract_addresses.d.ts +61 -65
  54. package/dest/l1_contract_addresses.d.ts.map +1 -1
  55. package/dest/l1_contract_addresses.js +101 -82
  56. package/dest/l1_reader.d.ts +5 -5
  57. package/dest/l1_reader.d.ts.map +1 -1
  58. package/dest/l1_reader.js +9 -7
  59. package/dest/l1_tx_utils/config.d.ts +7 -1
  60. package/dest/l1_tx_utils/config.d.ts.map +1 -1
  61. package/dest/l1_tx_utils/config.js +17 -4
  62. package/dest/l1_tx_utils/factory.d.ts +18 -10
  63. package/dest/l1_tx_utils/factory.d.ts.map +1 -1
  64. package/dest/l1_tx_utils/factory.js +17 -7
  65. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts +15 -15
  66. package/dest/l1_tx_utils/forwarder_l1_tx_utils.d.ts.map +1 -1
  67. package/dest/l1_tx_utils/forwarder_l1_tx_utils.js +9 -15
  68. package/dest/l1_tx_utils/index-blobs.d.ts +3 -3
  69. package/dest/l1_tx_utils/index-blobs.d.ts.map +1 -1
  70. package/dest/l1_tx_utils/index-blobs.js +2 -2
  71. package/dest/l1_tx_utils/index.d.ts +2 -1
  72. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  73. package/dest/l1_tx_utils/index.js +1 -0
  74. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts +23 -4
  75. package/dest/l1_tx_utils/l1_fee_analyzer.d.ts.map +1 -1
  76. package/dest/l1_tx_utils/l1_fee_analyzer.js +65 -3
  77. package/dest/l1_tx_utils/l1_tx_utils.d.ts +26 -7
  78. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  79. package/dest/l1_tx_utils/l1_tx_utils.js +179 -82
  80. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +1 -1
  81. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  82. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +10 -5
  83. package/dest/l1_tx_utils/tx_delayer.d.ts +58 -0
  84. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -0
  85. package/dest/{test → l1_tx_utils}/tx_delayer.js +67 -37
  86. package/dest/l1_tx_utils/types.d.ts +27 -1
  87. package/dest/l1_tx_utils/types.d.ts.map +1 -1
  88. package/dest/l1_tx_utils/types.js +10 -0
  89. package/dest/publisher_manager.d.ts +34 -8
  90. package/dest/publisher_manager.d.ts.map +1 -1
  91. package/dest/publisher_manager.js +119 -8
  92. package/dest/queries.d.ts +13 -2
  93. package/dest/queries.d.ts.map +1 -1
  94. package/dest/queries.js +69 -4
  95. package/dest/test/blob_kzg_warmup.d.ts +19 -0
  96. package/dest/test/blob_kzg_warmup.d.ts.map +1 -0
  97. package/dest/test/blob_kzg_warmup.js +64 -0
  98. package/dest/test/chain_monitor.d.ts +55 -4
  99. package/dest/test/chain_monitor.d.ts.map +1 -1
  100. package/dest/test/chain_monitor.js +104 -3
  101. package/dest/test/eth_cheat_codes.d.ts +37 -6
  102. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  103. package/dest/test/eth_cheat_codes.js +89 -41
  104. package/dest/test/index.d.ts +2 -3
  105. package/dest/test/index.d.ts.map +1 -1
  106. package/dest/test/index.js +1 -2
  107. package/dest/test/rollup_cheat_codes.d.ts +60 -3
  108. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  109. package/dest/test/rollup_cheat_codes.js +86 -7
  110. package/dest/test/start_anvil.d.ts +23 -3
  111. package/dest/test/start_anvil.d.ts.map +1 -1
  112. package/dest/test/start_anvil.js +182 -32
  113. package/dest/test/upgrade_utils.js +2 -2
  114. package/dest/utils.d.ts +9 -2
  115. package/dest/utils.d.ts.map +1 -1
  116. package/dest/utils.js +48 -16
  117. package/package.json +6 -9
  118. package/src/client.ts +66 -2
  119. package/src/config.ts +96 -17
  120. package/src/contracts/chain_state_override.ts +200 -0
  121. package/src/contracts/fee_asset_price_oracle.ts +285 -0
  122. package/src/contracts/governance.ts +269 -12
  123. package/src/contracts/governance_proposer.ts +54 -1
  124. package/src/contracts/gse.ts +7 -2
  125. package/src/contracts/inbox.ts +13 -5
  126. package/src/contracts/index.ts +3 -2
  127. package/src/contracts/multicall.ts +247 -106
  128. package/src/contracts/outbox.ts +42 -8
  129. package/src/contracts/registry.ts +31 -1
  130. package/src/contracts/rollup.ts +457 -86
  131. package/src/contracts/{tally_slashing_proposer.ts → slashing_proposer.ts} +14 -16
  132. package/src/deploy_aztec_l1_contracts.ts +88 -47
  133. package/src/deploy_l1_contract.ts +3 -3
  134. package/src/foundry_binary.ts +57 -0
  135. package/src/l1_artifacts.ts +12 -35
  136. package/src/l1_contract_addresses.ts +120 -94
  137. package/src/l1_reader.ts +17 -10
  138. package/src/l1_tx_utils/config.ts +23 -3
  139. package/src/l1_tx_utils/factory.ts +31 -31
  140. package/src/l1_tx_utils/forwarder_l1_tx_utils.ts +43 -54
  141. package/src/l1_tx_utils/index-blobs.ts +2 -2
  142. package/src/l1_tx_utils/index.ts +1 -0
  143. package/src/l1_tx_utils/l1_fee_analyzer.ts +75 -3
  144. package/src/l1_tx_utils/l1_tx_utils.ts +164 -62
  145. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +10 -5
  146. package/src/{test → l1_tx_utils}/tx_delayer.ts +93 -56
  147. package/src/l1_tx_utils/types.ts +32 -0
  148. package/src/publisher_manager.ts +143 -12
  149. package/src/queries.ts +76 -4
  150. package/src/test/blob_kzg_warmup.ts +65 -0
  151. package/src/test/chain_monitor.ts +148 -3
  152. package/src/test/eth_cheat_codes.ts +82 -45
  153. package/src/test/index.ts +1 -2
  154. package/src/test/rollup_cheat_codes.ts +115 -6
  155. package/src/test/start_anvil.ts +212 -32
  156. package/src/test/upgrade_utils.ts +2 -2
  157. package/src/utils.ts +45 -18
  158. package/dest/contracts/empire_slashing_proposer.d.ts +0 -67
  159. package/dest/contracts/empire_slashing_proposer.d.ts.map +0 -1
  160. package/dest/contracts/empire_slashing_proposer.js +0 -207
  161. package/dest/contracts/tally_slashing_proposer.d.ts.map +0 -1
  162. package/dest/generated/l1-contracts-defaults.d.ts +0 -30
  163. package/dest/generated/l1-contracts-defaults.d.ts.map +0 -1
  164. package/dest/generated/l1-contracts-defaults.js +0 -30
  165. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts +0 -26
  166. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.d.ts.map +0 -1
  167. package/dest/l1_tx_utils/l1_tx_utils_with_blobs.js +0 -26
  168. package/dest/test/delayed_tx_utils.d.ts +0 -13
  169. package/dest/test/delayed_tx_utils.d.ts.map +0 -1
  170. package/dest/test/delayed_tx_utils.js +0 -28
  171. package/dest/test/tx_delayer.d.ts +0 -36
  172. package/dest/test/tx_delayer.d.ts.map +0 -1
  173. package/src/contracts/empire_slashing_proposer.ts +0 -259
  174. package/src/generated/l1-contracts-defaults.ts +0 -32
  175. package/src/l1_tx_utils/l1_tx_utils_with_blobs.ts +0 -77
  176. package/src/test/delayed_tx_utils.ts +0 -52
@@ -1,55 +1,205 @@
1
1
  import { createLogger } from '@aztec/foundation/log';
2
2
  import { makeBackoff, retry } from '@aztec/foundation/retry';
3
- import { fileURLToPath } from '@aztec/foundation/url';
4
- import { createAnvil } from '@viem/anvil';
5
- import { dirname, resolve } from 'path';
3
+ import { spawn } from 'child_process';
4
+ import { resolveFoundryBinary } from '../foundry_binary.js';
5
+ // Watchdog wrapper: instead of spawning anvil directly, we spawn a small bash supervisor that runs
6
+ // anvil as a background child and polls its own parent (this node process). If the parent dies for
7
+ // ANY reason — including SIGKILL / crash / OOM, where node's own exit handlers never run — the poll
8
+ // loop ends and the EXIT trap reaps anvil. The script is inlined (rather than shipped as a `.sh`) so
9
+ // it works from the published npm tarball too, and the resolved anvil binary is passed via
10
+ // `$ANVIL_BIN` so it works without `anvil` on PATH.
11
+ //
12
+ // `$@` is the anvil argv; `bash -c <script> bash <...args>` puts the args in `$@` and `$0` = 'bash'.
13
+ //
14
+ // The EXIT trap reaps anvil; INT/TERM just `exit` (which fires the EXIT trap) so a signal terminates
15
+ // the supervisor promptly instead of being swallowed — a trapped TERM does NOT terminate the shell,
16
+ // so trapping the kill directly on TERM would leave the poll loop running and the caller's teardown
17
+ // hanging until its SIGKILL escalation. `sleep & wait` makes the poll interruptible, so INT/TERM are
18
+ // handled immediately rather than after the current `sleep` returns.
19
+ const ANVIL_WATCHDOG = `
20
+ set -u
21
+ parent=$PPID
22
+ "$ANVIL_BIN" "$@" &
23
+ anvil_pid=$!
24
+ trap 'kill "$anvil_pid" 2>/dev/null' EXIT
25
+ trap 'exit 0' INT TERM
26
+ while kill -0 "$parent" 2>/dev/null; do sleep 1 & wait $!; done
27
+ `;
6
28
  /**
7
29
  * Ensures there's a running Anvil instance and returns the RPC URL.
8
30
  */ export async function startAnvil(opts = {}) {
9
- const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh');
31
+ const anvilBinary = resolveFoundryBinary('anvil');
10
32
  const logger = opts.log ? createLogger('ethereum:anvil') : undefined;
11
33
  const methodCalls = opts.captureMethodCalls ? [] : undefined;
12
- let port;
13
- // Start anvil.
14
- // We go via a wrapper script to ensure if the parent dies, anvil dies.
34
+ let detectedPort;
15
35
  const anvil = await retry(async ()=>{
16
- const anvil = createAnvil({
17
- anvilBinary,
18
- host: '127.0.0.1',
19
- port: opts.port ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : 8545),
20
- blockTime: opts.l1BlockTime,
21
- stopTimeout: 1000,
22
- accounts: opts.accounts ?? 20,
23
- gasLimit: 45_000_000n,
24
- chainId: opts.chainId ?? 31337
25
- });
26
- // Listen to the anvil output to get the port.
27
- const removeHandler = anvil.on('message', (message)=>{
28
- logger?.debug(message.trim());
29
- methodCalls?.push(...message.match(/eth_[^\s]+/g) || []);
30
- if (port === undefined && message.includes('Listening on')) {
31
- port = parseInt(message.match(/Listening on ([^:]+):(\d+)/)[2]);
36
+ // `--port 0` lets anvil bind an OS-assigned ephemeral port; the actual port is read back from
37
+ // its "Listening on host:port" stdout below, so independent suites can spawn their own anvil
38
+ // in parallel without fighting over a fixed port.
39
+ const port = opts.port ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : 8545);
40
+ const args = [
41
+ '--host',
42
+ '127.0.0.1',
43
+ '--port',
44
+ String(port),
45
+ '--accounts',
46
+ String(opts.accounts ?? 20),
47
+ '--gas-limit',
48
+ String(45_000_000),
49
+ '--chain-id',
50
+ String(opts.chainId ?? 31337)
51
+ ];
52
+ if (opts.l1BlockTime !== undefined) {
53
+ args.push('--block-time', String(opts.l1BlockTime));
54
+ }
55
+ if (opts.hardfork !== undefined) {
56
+ args.push('--hardfork', opts.hardfork);
57
+ }
58
+ args.push('--slots-in-an-epoch', String(opts.slotsInAnEpoch ?? 1));
59
+ // Spawn the watchdog (see ANVIL_WATCHDOG). It launches anvil with these args and reaps it if we
60
+ // die; `$0` is 'bash' and `$@` is the anvil argv.
61
+ const child = spawn('bash', [
62
+ '-c',
63
+ ANVIL_WATCHDOG,
64
+ 'bash',
65
+ ...args
66
+ ], {
67
+ stdio: [
68
+ 'ignore',
69
+ 'pipe',
70
+ 'pipe'
71
+ ],
72
+ env: {
73
+ ...process.env,
74
+ ANVIL_BIN: anvilBinary,
75
+ RAYON_NUM_THREADS: '1'
32
76
  }
33
77
  });
34
- await anvil.start();
35
- if (!logger && !opts.captureMethodCalls) {
36
- removeHandler();
78
+ // Wait for "Listening on" or an early exit.
79
+ await new Promise((resolve, reject)=>{
80
+ let stderr = '';
81
+ const onStdout = (data)=>{
82
+ const text = data.toString();
83
+ logger?.debug(text.trim());
84
+ methodCalls?.push(...text.match(/eth_[^\s]+/g) || []);
85
+ if (detectedPort === undefined && text.includes('Listening on')) {
86
+ const match = text.match(/Listening on ([^:]+):(\d+)/);
87
+ if (match) {
88
+ detectedPort = parseInt(match[2]);
89
+ }
90
+ }
91
+ if (detectedPort !== undefined) {
92
+ child.stdout?.removeListener('data', onStdout);
93
+ child.stderr?.removeListener('data', onStderr);
94
+ child.removeListener('close', onClose);
95
+ resolve();
96
+ }
97
+ };
98
+ const onStderr = (data)=>{
99
+ stderr += data.toString();
100
+ logger?.debug(data.toString().trim());
101
+ };
102
+ const onClose = (code)=>{
103
+ child.stdout?.removeListener('data', onStdout);
104
+ child.stderr?.removeListener('data', onStderr);
105
+ reject(new Error(`Anvil exited with code ${code} before listening. stderr: ${stderr}`));
106
+ };
107
+ child.stdout?.on('data', onStdout);
108
+ child.stderr?.on('data', onStderr);
109
+ child.once('close', onClose);
110
+ });
111
+ // Continue piping for logging, method-call capture, and/or dateProvider sync after startup.
112
+ if (logger || opts.captureMethodCalls || opts.dateProvider) {
113
+ child.stdout?.on('data', (data)=>{
114
+ const text = data.toString();
115
+ logger?.debug(text.trim());
116
+ methodCalls?.push(...text.match(/eth_[^\s]+/g) || []);
117
+ if (opts.dateProvider) {
118
+ syncDateProviderFromAnvilOutput(text, opts.dateProvider);
119
+ }
120
+ });
121
+ child.stderr?.on('data', (data)=>{
122
+ logger?.debug(data.toString().trim());
123
+ });
124
+ } else {
125
+ // Consume streams so the child process doesn't block on full pipe buffers.
126
+ child.stdout?.resume();
127
+ child.stderr?.resume();
37
128
  }
38
- return anvil;
129
+ return child;
39
130
  }, 'Start anvil', makeBackoff([
40
131
  5,
41
132
  5,
42
133
  5
43
134
  ]));
44
- if (!port) {
135
+ if (!detectedPort) {
45
136
  throw new Error('Failed to start anvil');
46
137
  }
47
- // Monkeypatch the anvil instance to include the actually assigned port
48
- // Object.defineProperty(anvil, 'port', { value: port, writable: false });
138
+ const port = detectedPort;
139
+ let status = 'listening';
140
+ anvil.once('close', ()=>{
141
+ status = 'idle';
142
+ });
143
+ const stop = async ()=>{
144
+ if (status === 'idle') {
145
+ return;
146
+ }
147
+ await killChild(anvil);
148
+ };
149
+ const anvilObj = {
150
+ port,
151
+ host: '127.0.0.1',
152
+ get status () {
153
+ return status;
154
+ },
155
+ stop
156
+ };
49
157
  return {
50
- anvil,
158
+ anvil: anvilObj,
51
159
  methodCalls,
52
- stop: ()=>anvil.stop(),
160
+ stop,
53
161
  rpcUrl: `http://127.0.0.1:${port}`
54
162
  };
55
163
  }
164
+ /** Extracts block time from anvil stdout and syncs the dateProvider. */ function syncDateProviderFromAnvilOutput(text, dateProvider) {
165
+ // Anvil logs mined blocks as:
166
+ // Block Time: "Fri, 20 Mar 2026 02:10:46 +0000"
167
+ const match = text.match(/Block Time:\s*"([^"]+)"/);
168
+ if (match) {
169
+ const blockTimeMs = new Date(match[1]).getTime();
170
+ if (!isNaN(blockTimeMs)) {
171
+ dateProvider.setTime(blockTimeMs);
172
+ }
173
+ }
174
+ }
175
+ /**
176
+ * Send SIGTERM to the watchdog, wait up to 5 s, then SIGKILL. The watchdog's trap forwards the
177
+ * signal to anvil, so terminating it tears down anvil too. All timers are always cleared.
178
+ */ function killChild(child) {
179
+ return new Promise((resolve)=>{
180
+ if (child.exitCode !== null || child.killed) {
181
+ child.stdout?.destroy();
182
+ child.stderr?.destroy();
183
+ resolve();
184
+ return;
185
+ }
186
+ let killTimer;
187
+ const onClose = ()=>{
188
+ if (killTimer !== undefined) {
189
+ clearTimeout(killTimer);
190
+ }
191
+ // Destroy stdio streams so their PipeWrap handles don't keep the event loop alive.
192
+ child.stdout?.destroy();
193
+ child.stderr?.destroy();
194
+ resolve();
195
+ };
196
+ child.once('close', onClose);
197
+ child.kill('SIGTERM');
198
+ killTimer = setTimeout(()=>{
199
+ killTimer = undefined;
200
+ child.kill('SIGKILL');
201
+ }, 5000);
202
+ // Ensure the timer does not prevent Node from exiting.
203
+ killTimer.unref();
204
+ });
205
+ }
@@ -3,13 +3,13 @@ import { GovernanceAbi } from '@aztec/l1-artifacts/GovernanceAbi';
3
3
  import { TestERC20Abi as StakingAssetAbi } from '@aztec/l1-artifacts/TestERC20Abi';
4
4
  import { getContract } from 'viem';
5
5
  import { extractProposalIdFromLogs } from '../contracts/governance.js';
6
- import { createL1TxUtilsFromViemWallet } from '../l1_tx_utils/index.js';
6
+ import { createL1TxUtils } from '../l1_tx_utils/index.js';
7
7
  import { EthCheatCodes } from './eth_cheat_codes.js';
8
8
  export async function executeGovernanceProposal(proposalId, governance, voteAmount, privateKey, l1Client, rpcUrls, logger) {
9
9
  const proposal = await governance.read.getProposal([
10
10
  proposalId
11
11
  ]);
12
- const l1TxUtils = createL1TxUtilsFromViemWallet(l1Client);
12
+ const l1TxUtils = createL1TxUtils(l1Client);
13
13
  const waitL1Block = async ()=>{
14
14
  await l1TxUtils.sendAndMonitorTransaction({
15
15
  to: l1Client.account.address,
package/dest/utils.d.ts CHANGED
@@ -9,7 +9,7 @@ export interface L2Claim {
9
9
  }
10
10
  export declare class FormattedViemError extends Error {
11
11
  metaMessages?: any[];
12
- constructor(message: string, metaMessages?: any[]);
12
+ constructor(message: string, metaMessages?: any[], options?: ErrorOptions);
13
13
  }
14
14
  export declare function extractEvent<const TAbi extends Abi | readonly unknown[], TEventName extends ContractEventName<TAbi>, TEventType = DecodeEventLogReturnType<TAbi, TEventName, Hex[], undefined, true>>(logs: Log[], address: Hex, abi: TAbi, eventName: TEventName, filter?: (log: TEventType) => boolean, logger?: Logger): TEventType;
15
15
  export declare function tryExtractEvent<const TAbi extends Abi | readonly unknown[], TEventName extends ContractEventName<TAbi>, TEventType = DecodeEventLogReturnType<TAbi, TEventName, Hex[], undefined, true>>(logs: Log[], address: Hex, abi: TAbi, eventName: TEventName, filter?: (log: TEventType) => boolean, logger?: Logger): TEventType | undefined;
@@ -22,6 +22,13 @@ export declare function mergeAbis(abis: Abi[]): Abi;
22
22
  * @returns A FormattedViemError instance.
23
23
  */
24
24
  export declare function formatViemError(error: any, abi?: Abi): FormattedViemError;
25
+ /**
26
+ * Best-effort decode of a raw revert payload (`0x...`) against an ABI.
27
+ * Returns a human-readable `ErrorName(arg1, arg2, ...)` string, or `undefined` if the selector
28
+ * is unknown or the payload is empty. Use to surface decoded error names alongside the raw
29
+ * payload in log lines for operators.
30
+ */
31
+ export declare function tryDecodeRevertReason(data: Hex | undefined, abi: Abi): string | undefined;
25
32
  export declare function tryGetCustomErrorName(err: any): string | undefined;
26
33
  /**
27
34
  * Type guard to check if a transaction is a blob transaction (EIP-4844).
@@ -35,4 +42,4 @@ export declare function isBlobTransaction(tx: FormattedTransaction): tx is Forma
35
42
  * Calculates a percentile from an array of bigints
36
43
  */
37
44
  export declare function calculatePercentile(values: bigint[], percentile: number): bigint;
38
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN6RCxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEVBQ0wsS0FBSyxHQUFHLEVBR1IsS0FBSyxpQkFBaUIsRUFFdEIsS0FBSyx3QkFBd0IsRUFDN0IsS0FBSyxvQkFBb0IsRUFDekIsS0FBSyxHQUFHLEVBQ1IsS0FBSyxHQUFHLEVBR1QsTUFBTSxNQUFNLENBQUM7QUFHZCxNQUFNLFdBQVcsT0FBTztJQUN0QixXQUFXLEVBQUUsRUFBRSxDQUFDO0lBQ2hCLFdBQVcsRUFBRSxFQUFFLENBQUM7SUFDaEIsV0FBVyxFQUFFLEdBQUcsQ0FBQztJQUNqQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7Q0FDMUI7QUFFRCxxQkFBYSxrQkFBbUIsU0FBUSxLQUFLO0lBQzNDLFlBQVksQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDO0lBRXJCLFlBQVksT0FBTyxFQUFFLE1BQU0sRUFBRSxZQUFZLENBQUMsRUFBRSxHQUFHLEVBQUUsRUFJaEQ7Q0FDRjtBQUVELHdCQUFnQixZQUFZLENBQzFCLEtBQUssQ0FBQyxJQUFJLFNBQVMsR0FBRyxHQUFHLFNBQVMsT0FBTyxFQUFFLEVBQzNDLFVBQVUsU0FBUyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsRUFDMUMsVUFBVSxHQUFHLHdCQUF3QixDQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxFQUUvRSxJQUFJLEVBQUUsR0FBRyxFQUFFLEVBQ1gsT0FBTyxFQUFFLEdBQUcsRUFDWixHQUFHLEVBQUUsSUFBSSxFQUNULFNBQVMsRUFBRSxVQUFVLEVBQ3JCLE1BQU0sQ0FBQyxFQUFFLENBQUMsR0FBRyxFQUFFLFVBQVUsS0FBSyxPQUFPLEVBQ3JDLE1BQU0sQ0FBQyxFQUFFLE1BQU0sR0FDZCxVQUFVLENBTVo7QUFFRCx3QkFBZ0IsZUFBZSxDQUM3QixLQUFLLENBQUMsSUFBSSxTQUFTLEdBQUcsR0FBRyxTQUFTLE9BQU8sRUFBRSxFQUMzQyxVQUFVLFNBQVMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEVBQzFDLFVBQVUsR0FBRyx3QkFBd0IsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsRUFFL0UsSUFBSSxFQUFFLEdBQUcsRUFBRSxFQUNYLE9BQU8sRUFBRSxHQUFHLEVBQ1osR0FBRyxFQUFFLElBQUksRUFDVCxTQUFTLEVBQUUsVUFBVSxFQUNyQixNQUFNLENBQUMsRUFBRSxDQUFDLEdBQUcsRUFBRSxVQUFVLEtBQUssT0FBTyxFQUNyQyxNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsVUFBVSxHQUFHLFNBQVMsQ0FnQnhCO0FBRUQsd0JBQWdCLHFCQUFxQixDQUFDLEdBQUcsRUFBRSxHQUFHLE9BVzdDO0FBRUQsd0JBQWdCLFNBQVMsQ0FBQyxJQUFJLEVBQUUsR0FBRyxFQUFFLEdBQUcsR0FBRyxDQWUxQztBQTRERDs7Ozs7R0FLRztBQUNILHdCQUFnQixlQUFlLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxHQUFHLEdBQUUsR0FBZSxHQUFHLGtCQUFrQixDQXdFcEY7QUF5QkQsd0JBQWdCLHFCQUFxQixDQUFDLEdBQUcsRUFBRSxHQUFHLHNCQWE3QztBQUVEOzs7R0FHRztBQUNILHdCQUFnQixpQkFBaUIsQ0FBQyxFQUFFLEVBQUUsb0JBQW9CLEdBQUcsRUFBRSxJQUFJLG9CQUFvQixHQUFHO0lBQ3hGLGdCQUFnQixFQUFFLE1BQU0sQ0FBQztJQUN6QixtQkFBbUIsRUFBRSxTQUFTLEdBQUcsRUFBRSxDQUFDO0NBQ3JDLENBT0E7QUFFRDs7R0FFRztBQUNILHdCQUFnQixtQkFBbUIsQ0FBQyxNQUFNLEVBQUUsTUFBTSxFQUFFLEVBQUUsVUFBVSxFQUFFLE1BQU0sR0FBRyxNQUFNLENBT2hGIn0=
45
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN6RCxPQUFPLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUdwRCxPQUFPLEVBQ0wsS0FBSyxHQUFHLEVBR1IsS0FBSyxpQkFBaUIsRUFFdEIsS0FBSyx3QkFBd0IsRUFDN0IsS0FBSyxvQkFBb0IsRUFDekIsS0FBSyxHQUFHLEVBQ1IsS0FBSyxHQUFHLEVBR1QsTUFBTSxNQUFNLENBQUM7QUFHZCxNQUFNLFdBQVcsT0FBTztJQUN0QixXQUFXLEVBQUUsRUFBRSxDQUFDO0lBQ2hCLFdBQVcsRUFBRSxFQUFFLENBQUM7SUFDaEIsV0FBVyxFQUFFLEdBQUcsQ0FBQztJQUNqQixnQkFBZ0IsRUFBRSxNQUFNLENBQUM7Q0FDMUI7QUFFRCxxQkFBYSxrQkFBbUIsU0FBUSxLQUFLO0lBQzNDLFlBQVksQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDO0lBRXJCLFlBQVksT0FBTyxFQUFFLE1BQU0sRUFBRSxZQUFZLENBQUMsRUFBRSxHQUFHLEVBQUUsRUFBRSxPQUFPLENBQUMsRUFBRSxZQUFZLEVBSXhFO0NBQ0Y7QUFFRCx3QkFBZ0IsWUFBWSxDQUMxQixLQUFLLENBQUMsSUFBSSxTQUFTLEdBQUcsR0FBRyxTQUFTLE9BQU8sRUFBRSxFQUMzQyxVQUFVLFNBQVMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEVBQzFDLFVBQVUsR0FBRyx3QkFBd0IsQ0FBQyxJQUFJLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsRUFFL0UsSUFBSSxFQUFFLEdBQUcsRUFBRSxFQUNYLE9BQU8sRUFBRSxHQUFHLEVBQ1osR0FBRyxFQUFFLElBQUksRUFDVCxTQUFTLEVBQUUsVUFBVSxFQUNyQixNQUFNLENBQUMsRUFBRSxDQUFDLEdBQUcsRUFBRSxVQUFVLEtBQUssT0FBTyxFQUNyQyxNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsVUFBVSxDQU1aO0FBRUQsd0JBQWdCLGVBQWUsQ0FDN0IsS0FBSyxDQUFDLElBQUksU0FBUyxHQUFHLEdBQUcsU0FBUyxPQUFPLEVBQUUsRUFDM0MsVUFBVSxTQUFTLGlCQUFpQixDQUFDLElBQUksQ0FBQyxFQUMxQyxVQUFVLEdBQUcsd0JBQXdCLENBQUMsSUFBSSxFQUFFLFVBQVUsRUFBRSxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLEVBRS9FLElBQUksRUFBRSxHQUFHLEVBQUUsRUFDWCxPQUFPLEVBQUUsR0FBRyxFQUNaLEdBQUcsRUFBRSxJQUFJLEVBQ1QsU0FBUyxFQUFFLFVBQVUsRUFDckIsTUFBTSxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsVUFBVSxLQUFLLE9BQU8sRUFDckMsTUFBTSxDQUFDLEVBQUUsTUFBTSxHQUNkLFVBQVUsR0FBRyxTQUFTLENBZ0J4QjtBQUVELHdCQUFnQixxQkFBcUIsQ0FBQyxHQUFHLEVBQUUsR0FBRyxPQVc3QztBQUVELHdCQUFnQixTQUFTLENBQUMsSUFBSSxFQUFFLEdBQUcsRUFBRSxHQUFHLEdBQUcsQ0FlMUM7QUEyRUQ7Ozs7O0dBS0c7QUFDSCx3QkFBZ0IsZUFBZSxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsR0FBRyxHQUFFLEdBQWUsR0FBRyxrQkFBa0IsQ0FrRXBGO0FBeUJEOzs7OztHQUtHO0FBQ0gsd0JBQWdCLHFCQUFxQixDQUFDLElBQUksRUFBRSxHQUFHLEdBQUcsU0FBUyxFQUFFLEdBQUcsRUFBRSxHQUFHLEdBQUcsTUFBTSxHQUFHLFNBQVMsQ0FVekY7QUFFRCx3QkFBZ0IscUJBQXFCLENBQUMsR0FBRyxFQUFFLEdBQUcsc0JBYTdDO0FBRUQ7OztHQUdHO0FBQ0gsd0JBQWdCLGlCQUFpQixDQUFDLEVBQUUsRUFBRSxvQkFBb0IsR0FBRyxFQUFFLElBQUksb0JBQW9CLEdBQUc7SUFDeEYsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDO0lBQ3pCLG1CQUFtQixFQUFFLFNBQVMsR0FBRyxFQUFFLENBQUM7Q0FDckMsQ0FPQTtBQUVEOztHQUVHO0FBQ0gsd0JBQWdCLG1CQUFtQixDQUFDLE1BQU0sRUFBRSxNQUFNLEVBQUUsRUFBRSxVQUFVLEVBQUUsTUFBTSxHQUFHLE1BQU0sQ0FPaEYifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EACL,KAAK,GAAG,EAGR,KAAK,iBAAiB,EAEtB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,GAAG,EACR,KAAK,GAAG,EAGT,MAAM,MAAM,CAAC;AAGd,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,EAAE,CAAC;IAChB,WAAW,EAAE,EAAE,CAAC;IAChB,WAAW,EAAE,GAAG,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IAErB,YAAY,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,GAAG,EAAE,EAIhD;CACF;AAED,wBAAgB,YAAY,CAC1B,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG,SAAS,OAAO,EAAE,EAC3C,UAAU,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAC1C,UAAU,GAAG,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAE/E,IAAI,EAAE,GAAG,EAAE,EACX,OAAO,EAAE,GAAG,EACZ,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,UAAU,EACrB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,EACrC,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,CAMZ;AAED,wBAAgB,eAAe,CAC7B,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG,SAAS,OAAO,EAAE,EAC3C,UAAU,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAC1C,UAAU,GAAG,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAE/E,IAAI,EAAE,GAAG,EAAE,EACX,OAAO,EAAE,GAAG,EACZ,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,UAAU,EACrB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,EACrC,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,GAAG,SAAS,CAgBxB;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,OAW7C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAe1C;AA4DD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,GAAE,GAAe,GAAG,kBAAkB,CAwEpF;AAyBD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,sBAa7C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,oBAAoB,GAAG,EAAE,IAAI,oBAAoB,GAAG;IACxF,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,SAAS,GAAG,EAAE,CAAC;CACrC,CAOA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAOhF"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAGpD,OAAO,EACL,KAAK,GAAG,EAGR,KAAK,iBAAiB,EAEtB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,GAAG,EACR,KAAK,GAAG,EAGT,MAAM,MAAM,CAAC;AAGd,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,EAAE,CAAC;IAChB,WAAW,EAAE,EAAE,CAAC;IAChB,WAAW,EAAE,GAAG,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;IAErB,YAAY,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,GAAG,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,EAIxE;CACF;AAED,wBAAgB,YAAY,CAC1B,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG,SAAS,OAAO,EAAE,EAC3C,UAAU,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAC1C,UAAU,GAAG,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAE/E,IAAI,EAAE,GAAG,EAAE,EACX,OAAO,EAAE,GAAG,EACZ,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,UAAU,EACrB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,EACrC,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,CAMZ;AAED,wBAAgB,eAAe,CAC7B,KAAK,CAAC,IAAI,SAAS,GAAG,GAAG,SAAS,OAAO,EAAE,EAC3C,UAAU,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAC1C,UAAU,GAAG,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,EAE/E,IAAI,EAAE,GAAG,EAAE,EACX,OAAO,EAAE,GAAG,EACZ,GAAG,EAAE,IAAI,EACT,SAAS,EAAE,UAAU,EACrB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,EACrC,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,GAAG,SAAS,CAgBxB;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,OAW7C;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAe1C;AA2ED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,GAAE,GAAe,GAAG,kBAAkB,CAkEpF;AAyBD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,GAAG,GAAG,SAAS,EAAE,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAUzF;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,GAAG,sBAa7C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,oBAAoB,GAAG,EAAE,IAAI,oBAAoB,GAAG;IACxF,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,SAAS,GAAG,EAAE,CAAC;CACrC,CAOA;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAOhF"}
package/dest/utils.js CHANGED
@@ -3,8 +3,8 @@ import { BaseError, ContractFunctionRevertedError, decodeErrorResult, decodeEven
3
3
  import { formatAbiItem, formatAbiParams } from 'viem/utils';
4
4
  export class FormattedViemError extends Error {
5
5
  metaMessages;
6
- constructor(message, metaMessages){
7
- super(message);
6
+ constructor(message, metaMessages, options){
7
+ super(message, options);
8
8
  this.name = 'FormattedViemError';
9
9
  this.metaMessages = metaMessages;
10
10
  }
@@ -112,6 +112,19 @@ function getNestedErrorData(error) {
112
112
  // Not found
113
113
  return undefined;
114
114
  }
115
+ /**
116
+ * Truncates an error message to a safe length for log renderers.
117
+ * LogExplorer can only render up to 2500 characters in its summary view.
118
+ * We cap at 2000 to leave room for decorating context added by callers.
119
+ */ function truncateErrorMessage(message) {
120
+ const MAX = 2000;
121
+ const CHUNK = 950;
122
+ if (message.length <= MAX) {
123
+ return message;
124
+ }
125
+ const truncated = message.length - 2 * CHUNK;
126
+ return message.slice(0, CHUNK) + `...${truncated} characters truncated...` + message.slice(-CHUNK);
127
+ }
115
128
  /**
116
129
  * Formats a Viem error into a FormattedViemError instance.
117
130
  * @param error - The error to format.
@@ -122,6 +135,7 @@ function getNestedErrorData(error) {
122
135
  if (error instanceof FormattedViemError) {
123
136
  return error;
124
137
  }
138
+ const originalError = error;
125
139
  // First try to decode as a custom error using the ABI
126
140
  try {
127
141
  const data = getNestedErrorData(error);
@@ -132,7 +146,9 @@ function getNestedErrorData(error) {
132
146
  data: data
133
147
  });
134
148
  if (decoded) {
135
- return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages);
149
+ return new FormattedViemError(`${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`, error?.metaMessages, {
150
+ cause: originalError
151
+ });
136
152
  }
137
153
  }
138
154
  // If it's a BaseError, try to get the custom error through ContractFunctionRevertedError
@@ -144,7 +160,9 @@ function getNestedErrorData(error) {
144
160
  errorName = revertError.signature ?? '';
145
161
  }
146
162
  const args = revertError.metaMessages && revertError.metaMessages?.length > 1 ? revertError.metaMessages[1].trimStart() : '';
147
- return new FormattedViemError(`${errorName}${args}`, error?.metaMessages);
163
+ return new FormattedViemError(`${errorName}${args}`, error?.metaMessages, {
164
+ cause: originalError
165
+ });
148
166
  }
149
167
  }
150
168
  } catch {
@@ -162,18 +180,13 @@ function getNestedErrorData(error) {
162
180
  }
163
181
  // If it's a regular Error instance, return it with its message
164
182
  if (error instanceof Error) {
165
- return new FormattedViemError(error.message, error?.metaMessages);
166
- }
167
- const body = String(error);
168
- const length = body.length;
169
- // LogExplorer can only render up to 2500 characters in it's summary view. Try to keep the whole message below this number
170
- // Limit the error to 2000 chacaters in order to allow code higher up to decorate this error with extra details (up to 500 characters)
171
- if (length > 2000) {
172
- const chunk = 950;
173
- const truncated = length - 2 * chunk;
174
- return new FormattedViemError(body.slice(0, chunk) + `...${truncated} characters truncated...` + body.slice(-1 * chunk));
175
- }
176
- return new FormattedViemError(body);
183
+ return new FormattedViemError(truncateErrorMessage(error.message), error?.metaMessages, {
184
+ cause: originalError
185
+ });
186
+ }
187
+ return new FormattedViemError(truncateErrorMessage(String(error)), undefined, {
188
+ cause: originalError
189
+ });
177
190
  }
178
191
  function stripAbis(obj) {
179
192
  if (!obj || typeof obj !== 'object') {
@@ -194,6 +207,25 @@ function stripAbis(obj) {
194
207
  }
195
208
  });
196
209
  }
210
+ /**
211
+ * Best-effort decode of a raw revert payload (`0x...`) against an ABI.
212
+ * Returns a human-readable `ErrorName(arg1, arg2, ...)` string, or `undefined` if the selector
213
+ * is unknown or the payload is empty. Use to surface decoded error names alongside the raw
214
+ * payload in log lines for operators.
215
+ */ export function tryDecodeRevertReason(data, abi) {
216
+ if (!data || data === '0x') {
217
+ return undefined;
218
+ }
219
+ try {
220
+ const decoded = decodeErrorResult({
221
+ abi,
222
+ data
223
+ });
224
+ return `${decoded.errorName}(${decoded.args?.join(', ') ?? ''})`;
225
+ } catch {
226
+ return undefined;
227
+ }
228
+ }
197
229
  export function tryGetCustomErrorName(err) {
198
230
  try {
199
231
  // See https://viem.sh/docs/contract/simulateContract#handling-custom-errors
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/ethereum",
3
- "version": "0.0.1-commit.4d79d1f2d",
3
+ "version": "0.0.1-commit.4d9804df",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./account": "./dest/account.js",
@@ -40,7 +40,6 @@
40
40
  "build": "yarn clean && ../scripts/tsc.sh",
41
41
  "build:dev": "../scripts/tsc.sh --watch",
42
42
  "clean": "rm -rf ./dest .tsbuildinfo",
43
- "generate": "./scripts/generate.sh",
44
43
  "start:dev": "concurrently -k \"../scripts/tsc.sh --watch\" \"nodemon --watch dest --exec yarn start\"",
45
44
  "start": "node ./dest/index.js",
46
45
  "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}",
@@ -50,17 +49,16 @@
50
49
  "../package.common.json"
51
50
  ],
52
51
  "dependencies": {
53
- "@aztec/blob-lib": "0.0.1-commit.4d79d1f2d",
54
- "@aztec/constants": "0.0.1-commit.4d79d1f2d",
55
- "@aztec/foundation": "0.0.1-commit.4d79d1f2d",
56
- "@aztec/l1-artifacts": "0.0.1-commit.4d79d1f2d",
57
- "@viem/anvil": "^0.0.10",
52
+ "@aztec/blob-lib": "0.0.1-commit.4d9804df",
53
+ "@aztec/constants": "0.0.1-commit.4d9804df",
54
+ "@aztec/foundation": "0.0.1-commit.4d9804df",
55
+ "@aztec/l1-artifacts": "0.0.1-commit.4d9804df",
58
56
  "dotenv": "^16.0.3",
59
57
  "lodash.chunk": "^4.2.0",
60
58
  "lodash.pickby": "^4.5.0",
61
59
  "tslib": "^2.4.0",
62
60
  "viem": "npm:@aztec/viem@2.38.2",
63
- "zod": "^3.23.8"
61
+ "zod": "^4"
64
62
  },
65
63
  "devDependencies": {
66
64
  "@jest/globals": "^30.0.0",
@@ -69,7 +67,6 @@
69
67
  "@types/lodash.pickby": "^4",
70
68
  "@types/node": "^22.15.17",
71
69
  "@typescript/native-preview": "7.0.0-dev.20260113.1",
72
- "@viem/anvil": "^0.0.10",
73
70
  "get-port": "^7.1.0",
74
71
  "jest": "^30.0.0",
75
72
  "jest-mock-extended": "^4.0.0",
package/src/client.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  import type { Logger } from '@aztec/foundation/log';
2
2
  import { retryUntil } from '@aztec/foundation/retry';
3
+ import { getErrorCause } from '@aztec/foundation/types';
3
4
 
4
5
  import {
5
6
  type Chain,
7
+ type FallbackTransport,
6
8
  type HDAccount,
9
+ HttpRequestError,
10
+ type HttpTransport,
7
11
  type LocalAccount,
8
12
  type PrivateKeyAccount,
9
13
  createPublicClient,
@@ -25,10 +29,69 @@ type Config = {
25
29
  l1ChainId: number;
26
30
  /** The polling interval viem uses in ms */
27
31
  viemPollingIntervalMS?: number;
32
+ /** Timeout for HTTP requests to the L1 RPC node in ms. */
33
+ l1HttpTimeoutMS?: number;
28
34
  };
29
35
 
30
36
  export type { Config as EthereumClientConfig };
31
37
 
38
+ /** Error exposed by L1 RPC transports without including provider URLs in its message. */
39
+ export class L1RpcError extends Error {
40
+ constructor(message: string, options?: ErrorOptions) {
41
+ super(message, options);
42
+ this.name = 'L1RpcError';
43
+ }
44
+ }
45
+
46
+ /** Creates a viem fallback HTTP transport for the given L1 RPC URLs. */
47
+ export function makeL1HttpTransport(rpcUrls: string[], opts?: { timeout?: number }) {
48
+ return wrapL1RpcTransport(fallback(rpcUrls.map(url => http(url, { batch: false, timeout: opts?.timeout }))));
49
+ }
50
+
51
+ /** Returns the HTTP status from an L1 RPC error's cause chain, if one is available. */
52
+ export function getL1RpcHttpStatus(err: unknown): number | undefined {
53
+ return getErrorCause(err, HttpRequestError)?.status;
54
+ }
55
+
56
+ /** Returns true when an L1 RPC error's cause chain contains the given HTTP status. */
57
+ export function isL1RpcHttpStatus(err: unknown, status: number): boolean {
58
+ return getL1RpcHttpStatus(err) === status;
59
+ }
60
+
61
+ function wrapL1RpcTransport(transport: FallbackTransport<HttpTransport[]>): FallbackTransport<HttpTransport[]> {
62
+ const wrappedTransport: FallbackTransport<HttpTransport[]> = parameters => {
63
+ const fallbackTransport = transport(parameters);
64
+ const request: typeof fallbackTransport.request = async args => {
65
+ try {
66
+ return await fallbackTransport.request(args);
67
+ } catch (err) {
68
+ throw err instanceof L1RpcError ? err : new L1RpcError('L1 RPC request failed', { cause: err });
69
+ }
70
+ };
71
+ return { ...fallbackTransport, request };
72
+ };
73
+
74
+ return wrappedTransport;
75
+ }
76
+
77
+ /**
78
+ * Returns the individual RPC URLs underlying a viem public client that was constructed with a
79
+ * fallback HTTP transport (see {@link makeL1HttpTransport}). Returns an empty array if the
80
+ * transport shape is not recognized (e.g. mock clients in tests, or non-fallback transports).
81
+ */
82
+ export function getRpcUrlsFromClient(client: ViemPublicClient): string[] {
83
+ const transport = client.transport as unknown as {
84
+ transports?: { value?: { url?: string } }[];
85
+ value?: { url?: string };
86
+ url?: string;
87
+ };
88
+ if (Array.isArray(transport?.transports)) {
89
+ return transport.transports.map(t => t?.value?.url).filter((url): url is string => typeof url === 'string');
90
+ }
91
+ const singleUrl = transport?.value?.url ?? transport?.url;
92
+ return typeof singleUrl === 'string' ? [singleUrl] : [];
93
+ }
94
+
32
95
  // TODO: Use these methods to abstract the creation of viem clients.
33
96
 
34
97
  /** Returns a viem public client given the L1 config. */
@@ -36,7 +99,7 @@ export function getPublicClient(config: Config): ViemPublicClient {
36
99
  const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
37
100
  return createPublicClient({
38
101
  chain: chain.chainInfo,
39
- transport: fallback(config.l1RpcUrls.map(url => http(url, { batch: false }))),
102
+ transport: makeL1HttpTransport(config.l1RpcUrls, { timeout: config.l1HttpTimeoutMS }),
40
103
  pollingInterval: config.viemPollingIntervalMS,
41
104
  });
42
105
  }
@@ -77,6 +140,7 @@ export function createExtendedL1Client(
77
140
  chain: Chain = foundry,
78
141
  pollingIntervalMS?: number,
79
142
  addressIndex?: number,
143
+ opts?: { httpTimeoutMS?: number },
80
144
  ): ExtendedViemWalletClient {
81
145
  const hdAccount =
82
146
  typeof mnemonicOrPrivateKeyOrHdAccount === 'string'
@@ -88,7 +152,7 @@ export function createExtendedL1Client(
88
152
  const extendedClient = createWalletClient({
89
153
  account: hdAccount,
90
154
  chain,
91
- transport: fallback(rpcUrls.map(url => http(url, { batch: false }))),
155
+ transport: makeL1HttpTransport(rpcUrls, { timeout: opts?.httpTimeoutMS }),
92
156
  pollingInterval: pollingIntervalMS,
93
157
  }).extend(publicActions);
94
158