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