Skip to main content

alloy_provider/ext/
debug.rs

1//! This module extends the Ethereum JSON-RPC provider with the Debug namespace's RPC methods.
2use crate::Provider;
3use alloy_json_rpc::RpcRecv;
4use alloy_network::{Ethereum, Network};
5use alloy_primitives::{hex, Bytes, TxHash, B256};
6use alloy_rpc_types_debug::ExecutionWitness;
7use alloy_rpc_types_eth::{BadBlock, BlockId, BlockNumberOrTag, Bundle, StateContext};
8use alloy_rpc_types_trace::geth::{
9    BlockTraceResult, CallFrame, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace,
10    PreStateFrame, TraceResult,
11};
12use alloy_transport::TransportResult;
13
14/// Debug namespace rpc interface that gives access to several non-standard RPC methods.
15#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
16#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
17pub trait DebugApi<N: Network = Ethereum>: Send + Sync {
18    /// Returns an RLP-encoded header.
19    async fn debug_get_raw_header(&self, block: BlockId) -> TransportResult<Bytes>;
20
21    /// Retrieves and returns the RLP encoded block by number, hash or tag.
22    async fn debug_get_raw_block(&self, block: BlockId) -> TransportResult<Bytes>;
23
24    /// Returns an EIP-2718 binary-encoded transaction.
25    async fn debug_get_raw_transaction(&self, hash: TxHash) -> TransportResult<Bytes>;
26
27    /// Returns an array of EIP-2718 binary-encoded receipts.
28    async fn debug_get_raw_receipts(&self, block: BlockId) -> TransportResult<Vec<Bytes>>;
29
30    /// Returns an array of recent bad blocks that the client has seen on the network.
31    async fn debug_get_bad_blocks(&self) -> TransportResult<Vec<BadBlock>>;
32
33    /// Returns the structured logs created during the execution of EVM between two blocks
34    /// (excluding start) as a JSON object.
35    async fn debug_trace_chain(
36        &self,
37        start_exclusive: BlockNumberOrTag,
38        end_inclusive: BlockNumberOrTag,
39    ) -> TransportResult<Vec<BlockTraceResult>>;
40
41    /// Subscribes to traces for blocks in `(start, end]`.
42    #[cfg(feature = "pubsub")]
43    fn debug_subscribe_trace_chain(
44        &self,
45        start_exclusive: BlockNumberOrTag,
46        end_inclusive: BlockNumberOrTag,
47        trace_options: Option<GethDebugTracingOptions>,
48    ) -> crate::GetSubscription<
49        (&'static str, BlockNumberOrTag, BlockNumberOrTag, Option<GethDebugTracingOptions>),
50        alloy_rpc_types_trace::geth::ChainBlockTraceResult,
51    >;
52
53    /// The debug_traceBlock method will return a full stack trace of all invoked opcodes of all
54    /// transaction that were included in this block.
55    ///
56    /// This expects an RLP-encoded block.
57    ///
58    /// # Note
59    ///
60    /// The parent of this block must be present, or it will fail.
61    async fn debug_trace_block(
62        &self,
63        rlp_block: &[u8],
64        trace_options: GethDebugTracingOptions,
65    ) -> TransportResult<Vec<TraceResult>>;
66
67    /// Reruns the transaction specified by the hash and returns the trace.
68    ///
69    /// It will replay any prior transactions to achieve the same state the transaction was executed
70    /// in.
71    ///
72    /// [`GethDebugTracingOptions`] can be used to specify the trace options.
73    ///
74    /// # Note
75    ///
76    /// Not all nodes support this call.
77    async fn debug_trace_transaction(
78        &self,
79        hash: TxHash,
80        trace_options: GethDebugTracingOptions,
81    ) -> TransportResult<GethTrace>;
82
83    /// Reruns the transaction specified by the hash and returns the trace in a specified format.
84    ///
85    /// This method allows for the trace to be returned as a type that implements `RpcRecv` and
86    /// `serde::de::DeserializeOwned`.
87    ///
88    /// [`GethDebugTracingOptions`] can be used to specify the trace options.
89    ///
90    /// # Note
91    ///
92    /// Not all nodes support this call.
93    async fn debug_trace_transaction_as<R>(
94        &self,
95        hash: TxHash,
96        trace_options: GethDebugTracingOptions,
97    ) -> TransportResult<R>
98    where
99        R: RpcRecv + serde::de::DeserializeOwned;
100
101    /// Reruns the transaction specified by the hash and returns the trace as a JSON object.
102    ///
103    /// This method provides the trace in a JSON format, which can be useful for further processing
104    /// or inspection.
105    ///
106    /// [`GethDebugTracingOptions`] can be used to specify the trace options.
107    ///
108    /// # Note
109    ///
110    /// Not all nodes support this call.
111    async fn debug_trace_transaction_js(
112        &self,
113        hash: TxHash,
114        trace_options: GethDebugTracingOptions,
115    ) -> TransportResult<serde_json::Value>;
116
117    /// Reruns the transaction specified by the hash and returns the trace as a call frame.
118    ///
119    /// This method provides the trace in the form of a `CallFrame`, which can be useful for
120    /// analyzing the call stack and execution details.
121    ///
122    /// [`GethDebugTracingOptions`] can be used to specify the trace options.
123    ///
124    /// # Note
125    ///
126    /// Not all nodes support this call.
127    async fn debug_trace_transaction_call(
128        &self,
129        hash: TxHash,
130        trace_options: GethDebugTracingOptions,
131    ) -> TransportResult<CallFrame>;
132
133    /// Reruns the transaction specified by the hash and returns the trace in a specified format.
134    ///
135    /// This method allows for the trace to be returned as a type that implements `RpcRecv` and
136    /// `serde::de::DeserializeOwned`.
137    ///
138    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
139    ///
140    /// # Note
141    ///
142    /// Not all nodes support this call.
143    async fn debug_trace_call_as<R>(
144        &self,
145        tx: N::TransactionRequest,
146        block: BlockId,
147        trace_options: GethDebugTracingCallOptions,
148    ) -> TransportResult<R>
149    where
150        R: RpcRecv + serde::de::DeserializeOwned;
151
152    /// Reruns the transaction specified by the hash and returns the trace as a JSON object.
153    ///
154    /// This method provides the trace in a JSON format, which can be useful for further processing
155    /// or inspection.
156    ///
157    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
158    ///
159    /// # Note
160    ///
161    /// Not all nodes support this call.
162    async fn debug_trace_call_js(
163        &self,
164        tx: N::TransactionRequest,
165        block: BlockId,
166        trace_options: GethDebugTracingCallOptions,
167    ) -> TransportResult<serde_json::Value>;
168
169    /// Reruns the transaction specified by the hash and returns the trace as a call frame.
170    ///
171    /// This method provides the trace in the form of a `CallFrame`, which can be useful for
172    /// analyzing the call stack and execution details.
173    ///
174    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
175    ///
176    /// # Note
177    ///
178    /// Not all nodes support this call.
179    async fn debug_trace_call_callframe(
180        &self,
181        tx: N::TransactionRequest,
182        block: BlockId,
183        trace_options: GethDebugTracingCallOptions,
184    ) -> TransportResult<CallFrame>;
185
186    /// Reruns the transaction specified by the hash and returns the pre-state trace.
187    ///
188    /// This method provides the trace in the form of a `PreStateFrame`, which can be useful for
189    /// analyzing the state before execution.
190    ///
191    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
192    ///
193    /// # Note
194    ///
195    /// Not all nodes support this call.
196    async fn debug_trace_call_prestate(
197        &self,
198        tx: N::TransactionRequest,
199        block: BlockId,
200        trace_options: GethDebugTracingCallOptions,
201    ) -> TransportResult<PreStateFrame>;
202
203    /// Return a full stack trace of all invoked opcodes of all transaction that were included in
204    /// this block.
205    ///
206    /// The parent of the block must be present or it will fail.
207    ///
208    /// [`GethDebugTracingOptions`] can be used to specify the trace options.
209    ///
210    /// # Note
211    ///
212    /// Not all nodes support this call.
213    async fn debug_trace_block_by_hash(
214        &self,
215        block: B256,
216        trace_options: GethDebugTracingOptions,
217    ) -> TransportResult<Vec<TraceResult>>;
218
219    /// Same as `debug_trace_block_by_hash` but block is specified by number.
220    ///
221    /// [`GethDebugTracingOptions`] can be used to specify the trace options.
222    ///
223    /// # Note
224    ///
225    /// Not all nodes support this call.
226    async fn debug_trace_block_by_number(
227        &self,
228        block: BlockNumberOrTag,
229        trace_options: GethDebugTracingOptions,
230    ) -> TransportResult<Vec<TraceResult>>;
231
232    /// Executes the given transaction without publishing it like `eth_call` and returns the trace
233    /// of the execution.
234    ///
235    /// The transaction will be executed in the context of the given block number or tag.
236    /// The state its run on is the state of the previous block.
237    ///
238    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
239    ///
240    /// # Note
241    ///
242    ///
243    /// Not all nodes support this call.
244    async fn debug_trace_call(
245        &self,
246        tx: N::TransactionRequest,
247        block: BlockId,
248        trace_options: GethDebugTracingCallOptions,
249    ) -> TransportResult<GethTrace>;
250
251    /// Same as `debug_trace_call` but it used to run and trace multiple transactions at once.
252    ///
253    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
254    ///
255    /// # Note
256    ///
257    /// Not all nodes support this call.
258    async fn debug_trace_call_many(
259        &self,
260        bundles: Vec<Bundle>,
261        state_context: StateContext,
262        trace_options: GethDebugTracingCallOptions,
263    ) -> TransportResult<Vec<Vec<GethTrace>>>;
264
265    /// Same as `debug_trace_call_many` but returns the traces as a type that implements `RpcRecv`.
266    ///
267    /// This method allows for the traces to be returned as a type that implements `RpcRecv` and
268    /// `serde::de::DeserializeOwned`.
269    ///
270    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
271    ///
272    /// # Note
273    ///
274    /// Not all nodes support this call.
275    async fn debug_trace_call_many_as<R>(
276        &self,
277        bundles: Vec<Bundle>,
278        state_context: StateContext,
279        trace_options: GethDebugTracingCallOptions,
280    ) -> TransportResult<Vec<Vec<R>>>
281    where
282        R: RpcRecv + serde::de::DeserializeOwned;
283
284    /// Same as `debug_trace_call_many` but returns the traces as JSON objects.
285    ///
286    /// This method provides the traces in a JSON format, which can be useful for further processing
287    /// or inspection.
288    ///
289    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
290    ///
291    /// # Note
292    ///
293    /// Not all nodes support this call.
294    async fn debug_trace_call_many_js(
295        &self,
296        bundles: Vec<Bundle>,
297        state_context: StateContext,
298        trace_options: GethDebugTracingCallOptions,
299    ) -> TransportResult<Vec<Vec<serde_json::Value>>>;
300
301    /// Same as `debug_trace_call_many` but returns the traces as call frames.
302    ///
303    /// This method provides the traces in the form of `CallFrame`s, which can be useful for
304    /// analyzing the call stack and execution details.
305    ///
306    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
307    ///
308    /// # Note
309    ///
310    /// Not all nodes support this call.
311    async fn debug_trace_call_many_callframe(
312        &self,
313        bundles: Vec<Bundle>,
314        state_context: StateContext,
315        trace_options: GethDebugTracingCallOptions,
316    ) -> TransportResult<Vec<Vec<CallFrame>>>;
317
318    /// Same as `debug_trace_call_many` but returns the pre-state traces.
319    ///
320    /// This method provides the traces in the form of `PreStateFrame`s, which can be useful for
321    /// analyzing the state before execution.
322    ///
323    /// [`GethDebugTracingCallOptions`] can be used to specify the trace options.
324    ///
325    /// # Note
326    ///
327    /// Not all nodes support this call.
328    async fn debug_trace_call_many_prestate(
329        &self,
330        bundles: Vec<Bundle>,
331        state_context: StateContext,
332        trace_options: GethDebugTracingCallOptions,
333    ) -> TransportResult<Vec<Vec<PreStateFrame>>>;
334
335    /// The `debug_executionWitness` method allows for re-execution of a block with the purpose of
336    /// generating an execution witness. The witness contains lists of required preimages and
337    /// headers needed during execution and verification: state trie node preimages (`state`),
338    /// contract code preimages (`codes`), unhashed account/storage keys (`keys`), and
339    /// RLP-encoded block headers (`headers`) used to verify state reads and BLOCKHASH results.
340    ///
341    /// The first argument is the block number or block hash.
342    ///
343    /// # Note
344    ///
345    /// Not all nodes support this call.
346    async fn debug_execution_witness(
347        &self,
348        block: BlockNumberOrTag,
349    ) -> TransportResult<ExecutionWitness>;
350
351    /// The `debug_codeByHash` method returns the code associated with a given hash at the specified
352    /// block. If no code is found, it returns None. If no block is provided, it defaults to the
353    /// latest block.
354    ///
355    /// # Note
356    ///
357    /// Not all nodes support this call.
358    async fn debug_code_by_hash(
359        &self,
360        hash: B256,
361        block: Option<BlockId>,
362    ) -> TransportResult<Option<Bytes>>;
363
364    /// The `debug_dbGet` method retrieves a value from the database using the given key.
365    ///
366    /// The key can be provided in two formats:
367    /// - Hex-encoded string with `0x` prefix: `0x[hex_string]` - decoded as hex bytes
368    /// - Raw byte string without `0x` prefix: `[raw_byte_string]` - treated as raw bytes
369    ///
370    /// # Note
371    ///
372    /// Not all nodes support this call.
373    ///
374    /// # References
375    /// - [Reth implementation](https://github.com/paradigmxyz/reth/pull/19369)
376    /// - [Geth schema](https://github.com/ethereum/go-ethereum/blob/737ffd1bf0cbee378d0111a5b17ae4724fb2216c/core/rawdb/schema.go#L29)
377    async fn debug_db_get(&self, key: &str) -> TransportResult<Bytes>;
378}
379
380#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
381#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
382impl<N, P> DebugApi<N> for P
383where
384    N: Network,
385    P: Provider<N>,
386{
387    async fn debug_get_raw_header(&self, block: BlockId) -> TransportResult<Bytes> {
388        self.client().request("debug_getRawHeader", (block,)).await
389    }
390
391    async fn debug_get_raw_block(&self, block: BlockId) -> TransportResult<Bytes> {
392        self.client().request("debug_getRawBlock", (block,)).await
393    }
394
395    async fn debug_get_raw_transaction(&self, hash: TxHash) -> TransportResult<Bytes> {
396        self.client().request("debug_getRawTransaction", (hash,)).await
397    }
398
399    async fn debug_get_raw_receipts(&self, block: BlockId) -> TransportResult<Vec<Bytes>> {
400        self.client().request("debug_getRawReceipts", (block,)).await
401    }
402
403    async fn debug_get_bad_blocks(&self) -> TransportResult<Vec<BadBlock>> {
404        self.client().request_noparams("debug_getBadBlocks").await
405    }
406
407    async fn debug_trace_chain(
408        &self,
409        start_exclusive: BlockNumberOrTag,
410        end_inclusive: BlockNumberOrTag,
411    ) -> TransportResult<Vec<BlockTraceResult>> {
412        self.client().request("debug_traceChain", (start_exclusive, end_inclusive)).await
413    }
414
415    #[cfg(feature = "pubsub")]
416    fn debug_subscribe_trace_chain(
417        &self,
418        start_exclusive: BlockNumberOrTag,
419        end_inclusive: BlockNumberOrTag,
420        trace_options: Option<GethDebugTracingOptions>,
421    ) -> crate::GetSubscription<
422        (&'static str, BlockNumberOrTag, BlockNumberOrTag, Option<GethDebugTracingOptions>),
423        alloy_rpc_types_trace::geth::ChainBlockTraceResult,
424    > {
425        let mut call = self.client().request(
426            "debug_subscribe",
427            ("traceChain", start_exclusive, end_inclusive, trace_options),
428        );
429        call.set_is_subscription();
430        crate::GetSubscription::new(self.weak_client(), call)
431    }
432
433    async fn debug_trace_block(
434        &self,
435        rlp_block: &[u8],
436        trace_options: GethDebugTracingOptions,
437    ) -> TransportResult<Vec<TraceResult>> {
438        let rlp_block = hex::encode_prefixed(rlp_block);
439        self.client().request("debug_traceBlock", (rlp_block, trace_options)).await
440    }
441
442    async fn debug_trace_transaction(
443        &self,
444        hash: TxHash,
445        trace_options: GethDebugTracingOptions,
446    ) -> TransportResult<GethTrace> {
447        self.client().request("debug_traceTransaction", (hash, trace_options)).await
448    }
449
450    async fn debug_trace_transaction_as<R>(
451        &self,
452        hash: TxHash,
453        trace_options: GethDebugTracingOptions,
454    ) -> TransportResult<R>
455    where
456        R: RpcRecv,
457    {
458        self.client().request("debug_traceTransaction", (hash, trace_options)).await
459    }
460
461    async fn debug_trace_transaction_js(
462        &self,
463        hash: TxHash,
464        trace_options: GethDebugTracingOptions,
465    ) -> TransportResult<serde_json::Value> {
466        self.debug_trace_transaction_as::<serde_json::Value>(hash, trace_options).await
467    }
468
469    async fn debug_trace_transaction_call(
470        &self,
471        hash: TxHash,
472        trace_options: GethDebugTracingOptions,
473    ) -> TransportResult<CallFrame> {
474        self.debug_trace_transaction_as::<CallFrame>(hash, trace_options).await
475    }
476
477    async fn debug_trace_call_as<R>(
478        &self,
479        tx: N::TransactionRequest,
480        block: BlockId,
481        trace_options: GethDebugTracingCallOptions,
482    ) -> TransportResult<R>
483    where
484        R: RpcRecv,
485    {
486        self.client().request("debug_traceCall", (tx, block, trace_options)).await
487    }
488
489    async fn debug_trace_call_js(
490        &self,
491        tx: N::TransactionRequest,
492        block: BlockId,
493        trace_options: GethDebugTracingCallOptions,
494    ) -> TransportResult<serde_json::Value> {
495        self.debug_trace_call_as::<serde_json::Value>(tx, block, trace_options).await
496    }
497
498    async fn debug_trace_call_callframe(
499        &self,
500        tx: N::TransactionRequest,
501        block: BlockId,
502        trace_options: GethDebugTracingCallOptions,
503    ) -> TransportResult<CallFrame> {
504        self.debug_trace_call_as::<CallFrame>(tx, block, trace_options).await
505    }
506
507    async fn debug_trace_call_prestate(
508        &self,
509        tx: N::TransactionRequest,
510        block: BlockId,
511        trace_options: GethDebugTracingCallOptions,
512    ) -> TransportResult<PreStateFrame> {
513        self.debug_trace_call_as::<PreStateFrame>(tx, block, trace_options).await
514    }
515
516    async fn debug_trace_block_by_hash(
517        &self,
518        block: B256,
519        trace_options: GethDebugTracingOptions,
520    ) -> TransportResult<Vec<TraceResult>> {
521        self.client().request("debug_traceBlockByHash", (block, trace_options)).await
522    }
523
524    async fn debug_trace_block_by_number(
525        &self,
526        block: BlockNumberOrTag,
527        trace_options: GethDebugTracingOptions,
528    ) -> TransportResult<Vec<TraceResult>> {
529        self.client().request("debug_traceBlockByNumber", (block, trace_options)).await
530    }
531
532    async fn debug_trace_call(
533        &self,
534        tx: N::TransactionRequest,
535        block: BlockId,
536        trace_options: GethDebugTracingCallOptions,
537    ) -> TransportResult<GethTrace> {
538        self.client().request("debug_traceCall", (tx, block, trace_options)).await
539    }
540
541    async fn debug_trace_call_many(
542        &self,
543        bundles: Vec<Bundle>,
544        state_context: StateContext,
545        trace_options: GethDebugTracingCallOptions,
546    ) -> TransportResult<Vec<Vec<GethTrace>>> {
547        self.client().request("debug_traceCallMany", (bundles, state_context, trace_options)).await
548    }
549
550    async fn debug_trace_call_many_as<R>(
551        &self,
552        bundles: Vec<Bundle>,
553        state_context: StateContext,
554        trace_options: GethDebugTracingCallOptions,
555    ) -> TransportResult<Vec<Vec<R>>>
556    where
557        R: RpcRecv,
558    {
559        self.client().request("debug_traceCallMany", (bundles, state_context, trace_options)).await
560    }
561
562    async fn debug_trace_call_many_js(
563        &self,
564        bundles: Vec<Bundle>,
565        state_context: StateContext,
566        trace_options: GethDebugTracingCallOptions,
567    ) -> TransportResult<Vec<Vec<serde_json::Value>>> {
568        self.debug_trace_call_many_as::<serde_json::Value>(bundles, state_context, trace_options)
569            .await
570    }
571
572    async fn debug_trace_call_many_callframe(
573        &self,
574        bundles: Vec<Bundle>,
575        state_context: StateContext,
576        trace_options: GethDebugTracingCallOptions,
577    ) -> TransportResult<Vec<Vec<CallFrame>>> {
578        self.debug_trace_call_many_as::<CallFrame>(bundles, state_context, trace_options).await
579    }
580
581    async fn debug_trace_call_many_prestate(
582        &self,
583        bundles: Vec<Bundle>,
584        state_context: StateContext,
585        trace_options: GethDebugTracingCallOptions,
586    ) -> TransportResult<Vec<Vec<PreStateFrame>>> {
587        self.debug_trace_call_many_as::<PreStateFrame>(bundles, state_context, trace_options).await
588    }
589
590    async fn debug_execution_witness(
591        &self,
592        block: BlockNumberOrTag,
593    ) -> TransportResult<ExecutionWitness> {
594        self.client().request("debug_executionWitness", (block,)).await
595    }
596
597    async fn debug_code_by_hash(
598        &self,
599        hash: B256,
600        block: Option<BlockId>,
601    ) -> TransportResult<Option<Bytes>> {
602        self.client().request("debug_codeByHash", (hash, block)).await
603    }
604
605    async fn debug_db_get(&self, key: &str) -> TransportResult<Bytes> {
606        self.client().request("debug_dbGet", (key,)).await
607    }
608}
609
610#[cfg(test)]
611mod test {
612    use super::*;
613    use crate::{ext::test::async_ci_only, ProviderBuilder, WalletProvider};
614    use alloy_network::TransactionBuilder;
615    use alloy_node_bindings::{utils::run_with_tempdir, Geth, Reth};
616    use alloy_primitives::{address, U256};
617    use alloy_rpc_types_eth::TransactionRequest;
618
619    #[tokio::test]
620    async fn test_debug_trace_transaction() {
621        async_ci_only(|| async move {
622            let provider = ProviderBuilder::new().connect_anvil_with_wallet();
623            let from = provider.default_signer_address();
624
625            let gas_price = provider.get_gas_price().await.unwrap();
626            let tx = TransactionRequest::default()
627                .from(from)
628                .to(address!("deadbeef00000000deadbeef00000000deadbeef"))
629                .value(U256::from(100))
630                .max_fee_per_gas(gas_price + 1)
631                .max_priority_fee_per_gas(gas_price + 1);
632            let pending = provider.send_transaction(tx).await.unwrap();
633            let receipt = pending.get_receipt().await.unwrap();
634
635            let hash = receipt.transaction_hash;
636            let trace_options = GethDebugTracingOptions::default();
637
638            let trace = provider.debug_trace_transaction(hash, trace_options).await.unwrap();
639
640            if let GethTrace::Default(trace) = trace {
641                assert_eq!(trace.gas, 21000)
642            }
643        })
644        .await;
645    }
646
647    #[tokio::test]
648    async fn test_debug_trace_call() {
649        async_ci_only(|| async move {
650            let provider = ProviderBuilder::new().connect_anvil_with_wallet();
651            let from = provider.default_signer_address();
652            let gas_price = provider.get_gas_price().await.unwrap();
653            let tx = TransactionRequest::default()
654                .from(from)
655                .with_input("0xdeadbeef")
656                .max_fee_per_gas(gas_price + 1)
657                .max_priority_fee_per_gas(gas_price + 1);
658
659            let trace = provider
660                .debug_trace_call(
661                    tx,
662                    BlockNumberOrTag::Latest.into(),
663                    GethDebugTracingCallOptions::default(),
664                )
665                .await
666                .unwrap();
667
668            if let GethTrace::Default(trace) = trace {
669                assert!(!trace.struct_logs.is_empty());
670            }
671        })
672        .await;
673    }
674
675    #[tokio::test]
676    async fn call_debug_get_raw_header() {
677        async_ci_only(|| async move {
678            run_with_tempdir("geth-test-", |temp_dir| async move {
679                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
680                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
681
682                let rlp_header = provider
683                    .debug_get_raw_header(BlockId::Number(BlockNumberOrTag::Latest))
684                    .await
685                    .expect("debug_getRawHeader call should succeed");
686
687                assert!(!rlp_header.is_empty());
688            })
689            .await;
690        })
691        .await;
692    }
693
694    #[tokio::test]
695    async fn call_debug_get_raw_block() {
696        async_ci_only(|| async move {
697            run_with_tempdir("geth-test-", |temp_dir| async move {
698                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
699                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
700
701                let rlp_block = provider
702                    .debug_get_raw_block(BlockId::Number(BlockNumberOrTag::Latest))
703                    .await
704                    .expect("debug_getRawBlock call should succeed");
705
706                assert!(!rlp_block.is_empty());
707            })
708            .await;
709        })
710        .await;
711    }
712
713    #[tokio::test]
714    async fn call_debug_get_raw_receipts() {
715        async_ci_only(|| async move {
716            run_with_tempdir("geth-test-", |temp_dir| async move {
717                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
718                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
719
720                let result = provider
721                    .debug_get_raw_receipts(BlockId::Number(BlockNumberOrTag::Latest))
722                    .await;
723                assert!(result.is_ok());
724            })
725            .await;
726        })
727        .await;
728    }
729
730    #[tokio::test]
731    async fn call_debug_get_bad_blocks() {
732        async_ci_only(|| async move {
733            run_with_tempdir("geth-test-", |temp_dir| async move {
734                let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
735                let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
736
737                let result = provider.debug_get_bad_blocks().await;
738                assert!(result.is_ok());
739            })
740            .await;
741        })
742        .await;
743    }
744
745    #[tokio::test]
746    #[cfg_attr(windows, ignore = "no reth on windows")]
747    async fn debug_trace_call_many() {
748        async_ci_only(|| async move {
749            run_with_tempdir("reth-test-", |temp_dir| async move {
750                let reth = Reth::new().dev().disable_discovery().data_dir(temp_dir).spawn();
751                let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
752
753                let tx1 = TransactionRequest::default()
754                    .with_from(address!("0000000000000000000000000000000000000123"))
755                    .with_to(address!("0000000000000000000000000000000000000456"));
756
757                let tx2 = TransactionRequest::default()
758                    .with_from(address!("0000000000000000000000000000000000000456"))
759                    .with_to(address!("0000000000000000000000000000000000000789"));
760
761                let bundles = vec![Bundle { transactions: vec![tx1, tx2], block_override: None }];
762                let state_context = StateContext::default();
763                let trace_options = GethDebugTracingCallOptions::default();
764                let result =
765                    provider.debug_trace_call_many(bundles, state_context, trace_options).await;
766                assert!(result.is_ok());
767
768                let traces = result.unwrap();
769                assert_eq!(
770                    serde_json::to_string_pretty(&traces).unwrap().trim(),
771                    r#"
772[
773  [
774    {
775      "failed": false,
776      "gas": 21000,
777      "returnValue": "0x",
778      "structLogs": []
779    },
780    {
781      "failed": false,
782      "gas": 21000,
783      "returnValue": "0x",
784      "structLogs": []
785    }
786  ]
787]
788"#
789                    .trim(),
790                );
791            })
792            .await;
793        })
794        .await;
795    }
796
797    #[tokio::test]
798    #[cfg_attr(windows, ignore = "no reth on windows")]
799    async fn test_debug_code_by_hash() {
800        use alloy_primitives::b256;
801
802        async_ci_only(|| async move {
803            run_with_tempdir("reth-test-", |temp_dir| async move {
804                let reth = Reth::new().dev().disable_discovery().data_dir(temp_dir).spawn();
805                let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
806
807                // Test 1: Empty code hash (keccak256 of empty bytes)
808                // This is a valid hash that exists for EOA accounts
809                let empty_code_hash =
810                    b256!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
811                let empty_code = provider.debug_code_by_hash(empty_code_hash, None).await.unwrap();
812                // Reth might return Some(empty) or None for empty code
813                if let Some(code) = empty_code {
814                    assert!(
815                        code.is_empty() || code == Bytes::from_static(&[]),
816                        "Empty code hash should return empty bytes"
817                    );
818                }
819
820                // Test 2: Non-existent hash should return None
821                let non_existent_hash =
822                    b256!("0000000000000000000000000000000000000000000000000000000000000001");
823                let no_code = provider.debug_code_by_hash(non_existent_hash, None).await.unwrap();
824                assert!(no_code.is_none(), "Non-existent hash should return None");
825
826                // Test 3: Verify the API is callable and doesn't error
827                // This confirms Reth has the method implemented
828                let another_hash =
829                    b256!("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");
830                let result = provider.debug_code_by_hash(another_hash, None).await;
831                assert!(result.is_ok(), "API call should not error even for random hashes");
832            })
833            .await;
834        })
835        .await;
836    }
837}