stratus/eth/primitives/
transaction_execution.rs

1use alloy_consensus::Eip658Value;
2use alloy_consensus::Receipt;
3use alloy_consensus::ReceiptEnvelope;
4use alloy_consensus::ReceiptWithBloom;
5use display_json::DebugAsJson;
6
7use crate::alias::AlloyLog;
8use crate::alias::AlloyLogData;
9use crate::alias::AlloyLogPrimitive;
10use crate::alias::AlloyReceipt;
11use crate::alias::AlloyTransaction;
12use crate::eth::executor::EvmExecutionResult;
13use crate::eth::executor::EvmInput;
14use crate::eth::primitives::EvmExecutionMetrics;
15use crate::eth::primitives::ExecutionInfo;
16use crate::eth::primitives::Log;
17use crate::eth::primitives::MinedData;
18use crate::eth::primitives::Signature;
19use crate::eth::primitives::TransactionInfo;
20use crate::eth::primitives::TransactionInput;
21use crate::eth::primitives::logs_bloom::LogsBloom;
22use crate::ext::OptionExt;
23use crate::ext::RuintExt;
24
25#[derive(DebugAsJson, Clone, derive_new::new, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
26#[cfg_attr(test, derive(fake::Dummy))]
27pub struct TransactionExecution {
28    pub info: TransactionInfo,
29    pub signature: Signature,
30    pub evm_input: EvmInput,
31    pub result: EvmExecutionResult,
32}
33
34impl TransactionExecution {
35    /// Returns the EVM execution metrics.
36    pub fn metrics(&self) -> EvmExecutionMetrics {
37        self.result.metrics
38    }
39
40    pub fn create_alloy_logs(&self) -> Vec<AlloyLog> {
41        self.logs()
42            .iter()
43            .map(|log| AlloyLog {
44                inner: AlloyLogPrimitive {
45                    address: log.address.into(),
46                    data: AlloyLogData::new_unchecked(log.topics_non_empty().into_iter().map(Into::into).collect(), log.data.clone().into()),
47                },
48                block_hash: None,
49                block_number: Some(self.evm_input.block_number.as_u64()),
50                block_timestamp: Some(*self.evm_input.block_timestamp),
51                transaction_hash: Some(self.info.hash.into()),
52                transaction_index: None,
53                log_index: None,
54                removed: false,
55            })
56            .collect()
57    }
58
59    /// Computes the bloom filter from execution logs.
60    fn compute_bloom(&self) -> LogsBloom {
61        let mut bloom = LogsBloom::default();
62        for log in self.result.execution.logs.iter() {
63            bloom.accrue_log(log);
64        }
65        bloom
66    }
67
68    pub fn logs(&self) -> &Vec<Log> {
69        &self.result.execution.logs
70    }
71}
72
73impl From<TransactionExecution> for AlloyTransaction {
74    fn from(value: TransactionExecution) -> Self {
75        let tx_input: TransactionInput = value.into();
76        tx_input.into()
77    }
78}
79
80impl From<TransactionExecution> for TransactionInput {
81    fn from(value: TransactionExecution) -> Self {
82        Self {
83            transaction_info: value.info,
84            execution_info: ExecutionInfo {
85                chain_id: value.evm_input.chain_id,
86                nonce: value.evm_input.nonce.unwrap_or_default(),
87                signer: value.evm_input.from,
88                to: value.evm_input.to,
89                value: value.evm_input.value,
90                input: value.evm_input.data,
91                gas_limit: value.evm_input.gas_limit,
92                gas_price: value.evm_input.gas_price,
93            },
94            signature: value.signature,
95        }
96    }
97}
98
99pub fn _tx_to_alloy_receipt_impl(execution: TransactionExecution, alloy_logs: Vec<AlloyLog>, mined_data: Option<MinedData>) -> AlloyReceipt {
100    let receipt = Receipt {
101        status: Eip658Value::Eip658(execution.result.execution.is_success()),
102        cumulative_gas_used: execution.result.execution.gas_used.into(), // TODO: implement cumulative gas used correctly
103        logs: alloy_logs,
104    };
105
106    let receipt_with_bloom = ReceiptWithBloom {
107        receipt,
108        logs_bloom: execution.compute_bloom().into(),
109    };
110
111    let inner = match execution.info.tx_type.map(|tx| tx.as_u64()) {
112        Some(1u64) => ReceiptEnvelope::Eip2930(receipt_with_bloom),
113        Some(2u64) => ReceiptEnvelope::Eip1559(receipt_with_bloom),
114        Some(3u64) => ReceiptEnvelope::Eip4844(receipt_with_bloom),
115        Some(4u64) => ReceiptEnvelope::Eip7702(receipt_with_bloom),
116        _ => ReceiptEnvelope::Legacy(receipt_with_bloom),
117    };
118
119    AlloyReceipt {
120        inner,
121        transaction_hash: execution.info.hash.into(),
122        transaction_index: mined_data.map(|data| data.index.into()),
123        block_hash: mined_data.map(|data| data.block_hash.into()),
124        block_number: Some(execution.evm_input.block_number.as_u64()),
125        gas_used: execution.result.execution.gas_used.into(),
126        effective_gas_price: execution.evm_input.gas_price,
127        blob_gas_used: None,
128        blob_gas_price: None,
129        from: execution.evm_input.from.into(),
130        to: execution.evm_input.to.map_into(),
131        contract_address: execution.result.execution.deployed_contract_address.map_into(),
132    }
133}
134
135impl From<TransactionExecution> for AlloyReceipt {
136    fn from(value: TransactionExecution) -> Self {
137        let alloy_logs = value.create_alloy_logs();
138        _tx_to_alloy_receipt_impl(value, alloy_logs, None)
139    }
140}