| 131 | /// Should be called in order for each transaction. |
| 132 | #[instrument(level = "debug", skip_all, fields(tx_hash = %transaction.tx_hash(), idx = idx))] |
| 133 | pub fn execute_transaction( |
| 134 | &mut self, |
| 135 | idx: usize, |
| 136 | transaction: Recovered<BaseTxEnvelope>, |
| 137 | ) -> Result<ExecutedPendingTransaction, StateProcessorError> { |
| 138 | let tx_hash = transaction.tx_hash(); |
| 139 | |
| 140 | let effective_gas_price = if transaction.is_deposit() { |
| 141 | 0 |
| 142 | } else { |
| 143 | self.pending_block |
| 144 | .base_fee_per_gas |
| 145 | .map(|base_fee| { |
| 146 | transaction.effective_tip_per_gas(base_fee).unwrap_or_default() |
| 147 | + base_fee as u128 |
| 148 | }) |
| 149 | .unwrap_or_else(|| transaction.max_fee_per_gas()) |
| 150 | }; |
| 151 | |
| 152 | // Check if we have all the data we need to reuse the previous execution. |
| 153 | let cached_execution = self.prev_pending_blocks.as_ref().and_then(|p| { |
| 154 | Some(CachedTransactionExecution { |
| 155 | receipt: p.get_receipt(tx_hash)?.clone(), |
| 156 | state: p.get_transaction_state(&tx_hash)?, |
| 157 | result: p.get_transaction_result(&tx_hash)?.clone(), |
| 158 | execution_time_us: p.get_execution_time(&tx_hash), |
| 159 | }) |
| 160 | }); |
| 161 | |
| 162 | // If cached, we can fill out pending block data using previous execution results |
| 163 | // If not cached, we need to execute the transaction and build pending block data from scratch |
| 164 | if let Some(cached_execution) = cached_execution { |
| 165 | self.execute_with_cached_data(transaction, cached_execution, idx, effective_gas_price) |
| 166 | } else { |
| 167 | self.execute_with_evm(transaction, idx, effective_gas_price) |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | /// Applies EIP-4788, EIP-2935, and Canyon create2 deployer pre-execution changes to the EVM. |
| 172 | /// |