1use 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#[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 async fn debug_get_raw_header(&self, block: BlockId) -> TransportResult<Bytes>;
20
21 async fn debug_get_raw_block(&self, block: BlockId) -> TransportResult<Bytes>;
23
24 async fn debug_get_raw_transaction(&self, hash: TxHash) -> TransportResult<Bytes>;
26
27 async fn debug_get_raw_receipts(&self, block: BlockId) -> TransportResult<Vec<Bytes>>;
29
30 async fn debug_get_bad_blocks(&self) -> TransportResult<Vec<BadBlock>>;
32
33 async fn debug_trace_chain(
36 &self,
37 start_exclusive: BlockNumberOrTag,
38 end_inclusive: BlockNumberOrTag,
39 ) -> TransportResult<Vec<BlockTraceResult>>;
40
41 async fn debug_trace_block(
50 &self,
51 rlp_block: &[u8],
52 trace_options: GethDebugTracingOptions,
53 ) -> TransportResult<Vec<TraceResult>>;
54
55 async fn debug_trace_transaction(
66 &self,
67 hash: TxHash,
68 trace_options: GethDebugTracingOptions,
69 ) -> TransportResult<GethTrace>;
70
71 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 async fn debug_trace_transaction_js(
100 &self,
101 hash: TxHash,
102 trace_options: GethDebugTracingOptions,
103 ) -> TransportResult<serde_json::Value>;
104
105 async fn debug_trace_transaction_call(
116 &self,
117 hash: TxHash,
118 trace_options: GethDebugTracingOptions,
119 ) -> TransportResult<CallFrame>;
120
121 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 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 async fn debug_trace_call_callframe(
168 &self,
169 tx: N::TransactionRequest,
170 block: BlockId,
171 trace_options: GethDebugTracingCallOptions,
172 ) -> TransportResult<CallFrame>;
173
174 async fn debug_trace_call_prestate(
185 &self,
186 tx: N::TransactionRequest,
187 block: BlockId,
188 trace_options: GethDebugTracingCallOptions,
189 ) -> TransportResult<PreStateFrame>;
190
191 async fn debug_trace_block_by_hash(
202 &self,
203 block: B256,
204 trace_options: GethDebugTracingOptions,
205 ) -> TransportResult<Vec<TraceResult>>;
206
207 async fn debug_trace_block_by_number(
215 &self,
216 block: BlockNumberOrTag,
217 trace_options: GethDebugTracingOptions,
218 ) -> TransportResult<Vec<TraceResult>>;
219
220 async fn debug_trace_call(
233 &self,
234 tx: N::TransactionRequest,
235 block: BlockId,
236 trace_options: GethDebugTracingCallOptions,
237 ) -> TransportResult<GethTrace>;
238
239 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 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 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 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 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 async fn debug_execution_witness(
334 &self,
335 block: BlockNumberOrTag,
336 ) -> TransportResult<ExecutionWitness>;
337
338 async fn debug_code_by_hash(
346 &self,
347 hash: B256,
348 block: Option<BlockId>,
349 ) -> TransportResult<Option<Bytes>>;
350}
351
352#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
353#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
354impl<N, P> DebugApi<N> for P
355where
356 N: Network,
357 P: Provider<N>,
358{
359 async fn debug_get_raw_header(&self, block: BlockId) -> TransportResult<Bytes> {
360 self.client().request("debug_getRawHeader", (block,)).await
361 }
362
363 async fn debug_get_raw_block(&self, block: BlockId) -> TransportResult<Bytes> {
364 self.client().request("debug_getRawBlock", (block,)).await
365 }
366
367 async fn debug_get_raw_transaction(&self, hash: TxHash) -> TransportResult<Bytes> {
368 self.client().request("debug_getRawTransaction", (hash,)).await
369 }
370
371 async fn debug_get_raw_receipts(&self, block: BlockId) -> TransportResult<Vec<Bytes>> {
372 self.client().request("debug_getRawReceipts", (block,)).await
373 }
374
375 async fn debug_get_bad_blocks(&self) -> TransportResult<Vec<BadBlock>> {
376 self.client().request_noparams("debug_getBadBlocks").await
377 }
378
379 async fn debug_trace_chain(
380 &self,
381 start_exclusive: BlockNumberOrTag,
382 end_inclusive: BlockNumberOrTag,
383 ) -> TransportResult<Vec<BlockTraceResult>> {
384 self.client().request("debug_traceChain", (start_exclusive, end_inclusive)).await
385 }
386
387 async fn debug_trace_block(
388 &self,
389 rlp_block: &[u8],
390 trace_options: GethDebugTracingOptions,
391 ) -> TransportResult<Vec<TraceResult>> {
392 let rlp_block = hex::encode_prefixed(rlp_block);
393 self.client().request("debug_traceBlock", (rlp_block, trace_options)).await
394 }
395
396 async fn debug_trace_transaction(
397 &self,
398 hash: TxHash,
399 trace_options: GethDebugTracingOptions,
400 ) -> TransportResult<GethTrace> {
401 self.client().request("debug_traceTransaction", (hash, trace_options)).await
402 }
403
404 async fn debug_trace_transaction_as<R>(
405 &self,
406 hash: TxHash,
407 trace_options: GethDebugTracingOptions,
408 ) -> TransportResult<R>
409 where
410 R: RpcRecv,
411 {
412 self.client().request("debug_traceTransaction", (hash, trace_options)).await
413 }
414
415 async fn debug_trace_transaction_js(
416 &self,
417 hash: TxHash,
418 trace_options: GethDebugTracingOptions,
419 ) -> TransportResult<serde_json::Value> {
420 self.debug_trace_transaction_as::<serde_json::Value>(hash, trace_options).await
421 }
422
423 async fn debug_trace_transaction_call(
424 &self,
425 hash: TxHash,
426 trace_options: GethDebugTracingOptions,
427 ) -> TransportResult<CallFrame> {
428 self.debug_trace_transaction_as::<CallFrame>(hash, trace_options).await
429 }
430
431 async fn debug_trace_call_as<R>(
432 &self,
433 tx: N::TransactionRequest,
434 block: BlockId,
435 trace_options: GethDebugTracingCallOptions,
436 ) -> TransportResult<R>
437 where
438 R: RpcRecv,
439 {
440 self.client().request("debug_traceCall", (tx, block, trace_options)).await
441 }
442
443 async fn debug_trace_call_js(
444 &self,
445 tx: N::TransactionRequest,
446 block: BlockId,
447 trace_options: GethDebugTracingCallOptions,
448 ) -> TransportResult<serde_json::Value> {
449 self.debug_trace_call_as::<serde_json::Value>(tx, block, trace_options).await
450 }
451
452 async fn debug_trace_call_callframe(
453 &self,
454 tx: N::TransactionRequest,
455 block: BlockId,
456 trace_options: GethDebugTracingCallOptions,
457 ) -> TransportResult<CallFrame> {
458 self.debug_trace_call_as::<CallFrame>(tx, block, trace_options).await
459 }
460
461 async fn debug_trace_call_prestate(
462 &self,
463 tx: N::TransactionRequest,
464 block: BlockId,
465 trace_options: GethDebugTracingCallOptions,
466 ) -> TransportResult<PreStateFrame> {
467 self.debug_trace_call_as::<PreStateFrame>(tx, block, trace_options).await
468 }
469
470 async fn debug_trace_block_by_hash(
471 &self,
472 block: B256,
473 trace_options: GethDebugTracingOptions,
474 ) -> TransportResult<Vec<TraceResult>> {
475 self.client().request("debug_traceBlockByHash", (block, trace_options)).await
476 }
477
478 async fn debug_trace_block_by_number(
479 &self,
480 block: BlockNumberOrTag,
481 trace_options: GethDebugTracingOptions,
482 ) -> TransportResult<Vec<TraceResult>> {
483 self.client().request("debug_traceBlockByNumber", (block, trace_options)).await
484 }
485
486 async fn debug_trace_call(
487 &self,
488 tx: N::TransactionRequest,
489 block: BlockId,
490 trace_options: GethDebugTracingCallOptions,
491 ) -> TransportResult<GethTrace> {
492 self.client().request("debug_traceCall", (tx, block, trace_options)).await
493 }
494
495 async fn debug_trace_call_many(
496 &self,
497 bundles: Vec<Bundle>,
498 state_context: StateContext,
499 trace_options: GethDebugTracingCallOptions,
500 ) -> TransportResult<Vec<Vec<GethTrace>>> {
501 self.client().request("debug_traceCallMany", (bundles, state_context, trace_options)).await
502 }
503
504 async fn debug_trace_call_many_as<R>(
505 &self,
506 bundles: Vec<Bundle>,
507 state_context: StateContext,
508 trace_options: GethDebugTracingCallOptions,
509 ) -> TransportResult<Vec<Vec<R>>>
510 where
511 R: RpcRecv,
512 {
513 self.client().request("debug_traceCallMany", (bundles, state_context, trace_options)).await
514 }
515
516 async fn debug_trace_call_many_js(
517 &self,
518 bundles: Vec<Bundle>,
519 state_context: StateContext,
520 trace_options: GethDebugTracingCallOptions,
521 ) -> TransportResult<Vec<Vec<serde_json::Value>>> {
522 self.debug_trace_call_many_as::<serde_json::Value>(bundles, state_context, trace_options)
523 .await
524 }
525
526 async fn debug_trace_call_many_callframe(
527 &self,
528 bundles: Vec<Bundle>,
529 state_context: StateContext,
530 trace_options: GethDebugTracingCallOptions,
531 ) -> TransportResult<Vec<Vec<CallFrame>>> {
532 self.debug_trace_call_many_as::<CallFrame>(bundles, state_context, trace_options).await
533 }
534
535 async fn debug_trace_call_many_prestate(
536 &self,
537 bundles: Vec<Bundle>,
538 state_context: StateContext,
539 trace_options: GethDebugTracingCallOptions,
540 ) -> TransportResult<Vec<Vec<PreStateFrame>>> {
541 self.debug_trace_call_many_as::<PreStateFrame>(bundles, state_context, trace_options).await
542 }
543
544 async fn debug_execution_witness(
545 &self,
546 block: BlockNumberOrTag,
547 ) -> TransportResult<ExecutionWitness> {
548 self.client().request("debug_executionWitness", (block,)).await
549 }
550
551 async fn debug_code_by_hash(
552 &self,
553 hash: B256,
554 block: Option<BlockId>,
555 ) -> TransportResult<Option<Bytes>> {
556 self.client().request("debug_codeByHash", (hash, block)).await
557 }
558}
559
560#[cfg(test)]
561mod test {
562 use super::*;
563 use crate::{ext::test::async_ci_only, ProviderBuilder, WalletProvider};
564 use alloy_network::TransactionBuilder;
565 use alloy_node_bindings::{utils::run_with_tempdir, Geth, Reth};
566 use alloy_primitives::{address, U256};
567 use alloy_rpc_types_eth::TransactionRequest;
568
569 #[tokio::test]
570 async fn test_debug_trace_transaction() {
571 async_ci_only(|| async move {
572 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
573 let from = provider.default_signer_address();
574
575 let gas_price = provider.get_gas_price().await.unwrap();
576 let tx = TransactionRequest::default()
577 .from(from)
578 .to(address!("deadbeef00000000deadbeef00000000deadbeef"))
579 .value(U256::from(100))
580 .max_fee_per_gas(gas_price + 1)
581 .max_priority_fee_per_gas(gas_price + 1);
582 let pending = provider.send_transaction(tx).await.unwrap();
583 let receipt = pending.get_receipt().await.unwrap();
584
585 let hash = receipt.transaction_hash;
586 let trace_options = GethDebugTracingOptions::default();
587
588 let trace = provider.debug_trace_transaction(hash, trace_options).await.unwrap();
589
590 if let GethTrace::Default(trace) = trace {
591 assert_eq!(trace.gas, 21000)
592 }
593 })
594 .await;
595 }
596
597 #[tokio::test]
598 async fn test_debug_trace_call() {
599 async_ci_only(|| async move {
600 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
601 let from = provider.default_signer_address();
602 let gas_price = provider.get_gas_price().await.unwrap();
603 let tx = TransactionRequest::default()
604 .from(from)
605 .with_input("0xdeadbeef")
606 .max_fee_per_gas(gas_price + 1)
607 .max_priority_fee_per_gas(gas_price + 1);
608
609 let trace = provider
610 .debug_trace_call(
611 tx,
612 BlockNumberOrTag::Latest.into(),
613 GethDebugTracingCallOptions::default(),
614 )
615 .await
616 .unwrap();
617
618 if let GethTrace::Default(trace) = trace {
619 assert!(!trace.struct_logs.is_empty());
620 }
621 })
622 .await;
623 }
624
625 #[tokio::test]
626 async fn call_debug_get_raw_header() {
627 async_ci_only(|| async move {
628 run_with_tempdir("geth-test-", |temp_dir| async move {
629 let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
630 let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
631
632 let rlp_header = provider
633 .debug_get_raw_header(BlockId::Number(BlockNumberOrTag::Latest))
634 .await
635 .expect("debug_getRawHeader call should succeed");
636
637 assert!(!rlp_header.is_empty());
638 })
639 .await;
640 })
641 .await;
642 }
643
644 #[tokio::test]
645 async fn call_debug_get_raw_block() {
646 async_ci_only(|| async move {
647 run_with_tempdir("geth-test-", |temp_dir| async move {
648 let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
649 let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
650
651 let rlp_block = provider
652 .debug_get_raw_block(BlockId::Number(BlockNumberOrTag::Latest))
653 .await
654 .expect("debug_getRawBlock call should succeed");
655
656 assert!(!rlp_block.is_empty());
657 })
658 .await;
659 })
660 .await;
661 }
662
663 #[tokio::test]
664 async fn call_debug_get_raw_receipts() {
665 async_ci_only(|| async move {
666 run_with_tempdir("geth-test-", |temp_dir| async move {
667 let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
668 let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
669
670 let result = provider
671 .debug_get_raw_receipts(BlockId::Number(BlockNumberOrTag::Latest))
672 .await;
673 assert!(result.is_ok());
674 })
675 .await;
676 })
677 .await;
678 }
679
680 #[tokio::test]
681 async fn call_debug_get_bad_blocks() {
682 async_ci_only(|| async move {
683 run_with_tempdir("geth-test-", |temp_dir| async move {
684 let geth = Geth::new().disable_discovery().data_dir(temp_dir).spawn();
685 let provider = ProviderBuilder::new().connect_http(geth.endpoint_url());
686
687 let result = provider.debug_get_bad_blocks().await;
688 assert!(result.is_ok());
689 })
690 .await;
691 })
692 .await;
693 }
694
695 #[tokio::test]
696 #[cfg_attr(windows, ignore = "no reth on windows")]
697 async fn debug_trace_call_many() {
698 async_ci_only(|| async move {
699 run_with_tempdir("reth-test-", |temp_dir| async move {
700 let reth = Reth::new().dev().disable_discovery().data_dir(temp_dir).spawn();
701 let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
702
703 let tx1 = TransactionRequest::default()
704 .with_from(address!("0000000000000000000000000000000000000123"))
705 .with_to(address!("0000000000000000000000000000000000000456"));
706
707 let tx2 = TransactionRequest::default()
708 .with_from(address!("0000000000000000000000000000000000000456"))
709 .with_to(address!("0000000000000000000000000000000000000789"));
710
711 let bundles = vec![Bundle { transactions: vec![tx1, tx2], block_override: None }];
712 let state_context = StateContext::default();
713 let trace_options = GethDebugTracingCallOptions::default();
714 let result =
715 provider.debug_trace_call_many(bundles, state_context, trace_options).await;
716 assert!(result.is_ok());
717
718 let traces = result.unwrap();
719 assert_eq!(
720 serde_json::to_string_pretty(&traces).unwrap().trim(),
721 r#"
722[
723 [
724 {
725 "failed": false,
726 "gas": 21000,
727 "returnValue": "0x",
728 "structLogs": []
729 },
730 {
731 "failed": false,
732 "gas": 21000,
733 "returnValue": "0x",
734 "structLogs": []
735 }
736 ]
737]
738"#
739 .trim(),
740 );
741 })
742 .await;
743 })
744 .await;
745 }
746
747 }