1use alloy_consensus::Transaction;
2use alloy_rpc_types_trace::geth::GethDebugTracingOptions;
3use display_json::DebugAsJson;
4
5use crate::eth::primitives::Address;
6use crate::eth::primitives::Block;
7use crate::eth::primitives::BlockNumber;
8use crate::eth::primitives::Bytes;
9use crate::eth::primitives::CallInput;
10use crate::eth::primitives::ChainId;
11use crate::eth::primitives::ExternalReceipt;
12use crate::eth::primitives::ExternalTransaction;
13use crate::eth::primitives::Gas;
14use crate::eth::primitives::Hash;
15use crate::eth::primitives::Nonce;
16use crate::eth::primitives::PendingBlockHeader;
17use crate::eth::primitives::PointInTime;
18use crate::eth::primitives::TransactionInput;
19use crate::eth::primitives::TransactionMined;
20use crate::eth::primitives::TransactionStage;
21use crate::eth::primitives::UnixTime;
22use crate::eth::primitives::Wei;
23use crate::eth::storage::ReadKind;
24use crate::eth::storage::TxCount;
25use crate::ext::OptionExt;
26use crate::ext::not;
27use crate::if_else;
28
29#[derive(DebugAsJson, Clone, Default, serde::Serialize, serde::Deserialize, fake::Dummy, PartialEq)]
31pub struct EvmInput {
32 pub from: Address,
39
40 pub to: Option<Address>,
47
48 pub value: Wei,
52
53 pub data: Bytes,
60
61 pub nonce: Option<Nonce>,
67
68 pub gas_limit: Gas,
70
71 pub gas_price: u128,
73
74 pub block_number: BlockNumber,
76
77 pub block_timestamp: UnixTime,
79
80 pub point_in_time: PointInTime,
82
83 pub chain_id: Option<ChainId>,
87
88 pub kind: ReadKind,
89}
90
91impl EvmInput {
92 pub fn from_eth_transaction(input: &TransactionInput, pending_header: &PendingBlockHeader) -> Self {
94 Self {
95 from: input.signer,
96 to: input.to,
97 value: input.value,
98 data: input.input.clone(),
99 gas_limit: Gas::MAX,
100 gas_price: 0,
101 nonce: Some(input.nonce),
102 block_number: pending_header.number,
103 block_timestamp: *pending_header.timestamp,
104 point_in_time: PointInTime::Pending,
105 chain_id: input.chain_id,
106 kind: ReadKind::Transaction,
107 }
108 }
109
110 pub fn from_pending_block(input: CallInput, pending_header: PendingBlockHeader, tx_count: TxCount) -> Self {
112 Self {
113 from: input.from.unwrap_or(Address::ZERO),
114 to: input.to.map_into(),
115 value: input.value,
116 data: input.data,
117 gas_limit: Gas::MAX,
118 gas_price: 0,
119 nonce: None,
120 block_number: pending_header.number,
121 block_timestamp: *pending_header.timestamp,
122 point_in_time: PointInTime::Pending,
123 chain_id: None,
124 kind: ReadKind::Call((pending_header.number, tx_count)),
125 }
126 }
127
128 pub fn from_mined_block(input: CallInput, block: Block, point_in_time: PointInTime) -> Self {
130 Self {
131 from: input.from.unwrap_or(Address::ZERO),
132 to: input.to.map_into(),
133 value: input.value,
134 data: input.data,
135 gas_limit: Gas::MAX,
136 gas_price: 0,
137 nonce: None,
138 block_number: block.number(),
139 block_timestamp: block.header.timestamp,
140 point_in_time,
141 chain_id: None,
142 kind: ReadKind::Call((block.number(), TxCount::Full)),
143 }
144 }
145
146 pub fn from_external(tx: &ExternalTransaction, receipt: &ExternalReceipt, block_number: BlockNumber, block_timestamp: UnixTime) -> anyhow::Result<Self> {
150 Ok(Self {
151 from: tx.inner.signer().into(),
152 to: tx.inner.to().map_into(),
153 value: tx.inner.value().into(),
154 data: tx.inner.input().clone().into(),
155 nonce: Some(tx.inner.nonce().into()),
156 gas_limit: if_else!(receipt.is_success(), Gas::MAX, tx.inner.gas_limit().into()),
157 gas_price: if_else!(receipt.is_success(), 0, tx.inner.gas_price().map_into().unwrap_or(0)),
158 point_in_time: PointInTime::Pending,
159 block_number,
160 block_timestamp,
161 chain_id: tx.inner.chain_id().map(Into::into),
162 kind: ReadKind::Transaction,
163 })
164 }
165
166 pub fn is_contract_call(&self) -> bool {
170 self.to.is_some() && not(self.data.is_empty())
171 }
172}
173
174impl PartialEq<(&TransactionInput, &PendingBlockHeader)> for EvmInput {
175 fn eq(&self, other: &(&TransactionInput, &PendingBlockHeader)) -> bool {
176 self.block_number == other.1.number
177 && self.block_timestamp == *other.1.timestamp
178 && self.chain_id == other.0.chain_id
179 && self.data == other.0.input
180 && self.from == other.0.signer
181 && self.nonce.is_some_and(|inner| inner == other.0.nonce)
182 && self.value == other.0.value
183 && self.to == other.0.to
184 }
185}
186
187impl From<TransactionMined> for EvmInput {
188 fn from(value: TransactionMined) -> Self {
189 Self {
190 from: value.input.signer,
191 to: value.input.to,
192 value: value.input.value,
193 data: value.input.input,
194 nonce: Some(value.input.nonce),
195 gas_limit: value.input.gas_limit,
196 gas_price: value.input.gas_price,
197 block_number: value.block_number,
198 block_timestamp: value.block_timestamp,
199 point_in_time: PointInTime::MinedPast(value.block_number),
200 chain_id: value.input.chain_id,
201 kind: ReadKind::Transaction,
202 }
203 }
204}
205
206impl TryFrom<TransactionStage> for EvmInput {
207 type Error = anyhow::Error;
208
209 fn try_from(value: TransactionStage) -> Result<Self, Self::Error> {
210 match value {
211 TransactionStage::Executed(tx) => Ok(tx.evm_input),
212 TransactionStage::Mined(tx) => Ok(tx.into()),
213 }
214 }
215}
216
217pub struct InspectorInput {
218 pub tx_hash: Hash,
219 pub opts: GethDebugTracingOptions,
220 pub trace_unsuccessful_only: bool,
221}