anvil_polkadot/api_server/
trace_helpers.rs1use crate::api_server::{error::Error, revive_conversions::AlloyU256};
2use alloy_primitives::{Address, B256};
3use alloy_rpc_types::trace::parity::{
4 Action, CallAction, CallOutput, CallType, CreateAction, CreateOutput, CreationMethod,
5 LocalizedTransactionTrace, SelfdestructAction, TraceOutput, TransactionTrace,
6};
7use pallet_revive_eth_rpc::EthRpcError;
8use polkadot_sdk::pallet_revive::evm::{
9 Block, CallTrace, CallType as ReviveCallType, HashesOrTransactionInfos, Trace, TransactionInfo,
10 TransactionTrace as ReviveTransactionTrace,
11};
12
13pub fn parity_block_trace_builder(
18 traces: Vec<ReviveTransactionTrace>,
19 block: Block,
20) -> Result<Vec<LocalizedTransactionTrace>, Error> {
21 let mut parity_block_traces = Vec::new();
22 let HashesOrTransactionInfos::TransactionInfos(transaction_infos) = block.transactions else {
23 return Err(Error::InternalError(
24 "Block transactions infos are not available in the block".to_string(),
25 ));
26 };
27 for revive_transaction_trace in traces {
28 let tx_info = transaction_infos
29 .iter()
30 .find(|item| item.hash == revive_transaction_trace.tx_hash)
31 .ok_or(Error::InternalError("Transaction info not found".to_string()))?;
32 let parity_transaction_trace = parity_transaction_trace_builder(
33 revive_transaction_trace.trace,
34 Some(tx_info.clone()),
35 )?;
36 parity_block_traces.extend(parity_transaction_trace);
37 }
38 Ok(parity_block_traces)
39}
40
41pub fn parity_transaction_trace_builder(
45 trace: Trace,
46 tx_info: Option<TransactionInfo>,
47) -> Result<Vec<LocalizedTransactionTrace>, Error> {
48 let call_trace = match trace {
49 Trace::Call(call_trace) => call_trace,
50 Trace::Prestate(_) => {
51 return Err(Error::InternalError("Trace is not a call trace".to_string()));
52 }
53 };
54 let mut parity_tx_traces = Vec::new();
55 let mut next_traces = vec![(vec![], call_trace)];
56 while let Some((trace_address, trace)) = next_traces.pop() {
57 let transaction_trace =
58 parity_transaction_trace_from_call_trace(trace.clone(), trace_address.clone())?;
59 let localized_trace = LocalizedTransactionTrace {
60 trace: transaction_trace,
61 block_hash: tx_info
62 .as_ref()
63 .map(|tx_info| B256::from_slice(tx_info.block_hash.as_ref())),
64 block_number: tx_info
65 .as_ref()
66 .map(|tx_info| tx_info.block_number.try_into().unwrap_or_default()),
67 transaction_hash: tx_info
68 .as_ref()
69 .map(|tx_info| B256::from_slice(tx_info.hash.as_ref())),
70 transaction_position: tx_info
71 .as_ref()
72 .map(|tx_info| tx_info.transaction_index.try_into().unwrap_or_default()),
73 };
74 parity_tx_traces.push(localized_trace);
75 for (call_index, call) in trace.calls.iter().enumerate() {
76 let mut new_trace_address = trace_address.clone();
77 new_trace_address.push(call_index);
78 next_traces.push((new_trace_address, call.clone()));
79 }
80 }
81 Ok(parity_tx_traces)
82}
83
84fn parity_transaction_trace_from_call_trace(
88 trace: CallTrace,
89 trace_address: Vec<usize>,
90) -> Result<TransactionTrace, Error> {
91 match trace.call_type {
92 ReviveCallType::Call | ReviveCallType::StaticCall | ReviveCallType::DelegateCall => {
93 Ok(TransactionTrace {
94 action: Action::Call(CallAction {
95 from: Address::from_slice(trace.from.as_ref()),
96 call_type: CallType::Call,
97 gas: trace.gas.try_into().map_err(|_| EthRpcError::ConversionError)?,
98 input: trace.input.0.into(),
99 to: Address::from_slice(trace.to.as_ref()),
100 value: AlloyU256::from(trace.value.unwrap_or_default()).inner(),
101 }),
102 error: trace.error,
103 result: Some(TraceOutput::Call(CallOutput {
104 gas_used: trace
105 .gas_used
106 .try_into()
107 .map_err(|_| EthRpcError::ConversionError)?,
108 output: trace.output.0.into(),
109 })),
110 subtraces: trace
111 .child_call_count
112 .try_into()
113 .map_err(|_| EthRpcError::ConversionError)?,
114 trace_address,
115 })
116 }
117 ReviveCallType::Create | ReviveCallType::Create2 => {
118 let creation_method = match trace.call_type {
119 ReviveCallType::Create => CreationMethod::Create,
120 ReviveCallType::Create2 => CreationMethod::Create2,
121 _ => unreachable!("Unexpected call type: should be Create or Create2"),
122 };
123 Ok(TransactionTrace {
124 action: Action::Create(CreateAction {
125 from: Address::from_slice(trace.from.as_ref()),
126 gas: trace.gas.try_into().map_err(|_| EthRpcError::ConversionError)?,
127 init: trace.input.0.into(),
128 value: AlloyU256::from(trace.value.unwrap_or_default()).inner(),
129 creation_method,
130 }),
131 error: trace.error,
132 result: Some(TraceOutput::Create(CreateOutput {
133 address: Address::from_slice(trace.to.as_ref()),
134 code: Default::default(),
135 gas_used: trace
136 .gas_used
137 .try_into()
138 .map_err(|_| EthRpcError::ConversionError)?,
139 })),
140 subtraces: trace
141 .child_call_count
142 .try_into()
143 .map_err(|_| EthRpcError::ConversionError)?,
144 trace_address,
145 })
146 }
147 ReviveCallType::Selfdestruct => Ok(TransactionTrace {
148 action: Action::Selfdestruct(SelfdestructAction {
149 address: Address::from_slice(trace.from.as_ref()),
150 balance: AlloyU256::from(trace.value.unwrap_or_default()).inner(),
151 refund_address: Address::from_slice(trace.to.as_ref()),
152 }),
153 error: trace.error,
154 result: None,
155 subtraces: trace
156 .child_call_count
157 .try_into()
158 .map_err(|_| EthRpcError::ConversionError)?,
159 trace_address,
160 }),
161 }
162}