diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index 0b3f07eeab..4e2bf3e2de 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -136,7 +136,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA block_env.basefee = U256::ZERO; } - let SimBlock { block_overrides, state_overrides, mut calls } = block; + let SimBlock { block_overrides, state_overrides, calls } = block; if let Some(block_overrides) = block_overrides { apply_block_overrides(block_overrides, &mut db, &mut block_env); @@ -151,26 +151,51 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA ) } - // Resolve transactions, populate missing fields and enforce calls correctness. - let transactions = simulate::resolve_transactions( - &mut calls, - validation, - block_env.gas_limit.to(), - cfg_env_with_handler_cfg.chain_id, - &mut db, - this.tx_resp_builder(), - )?; + let default_gas_limit = { + let total_specified_gas = calls.iter().filter_map(|tx| tx.gas).sum::(); + let txs_without_gas_limit = + calls.iter().filter(|tx| tx.gas.is_none()).count(); + + if total_specified_gas > block_env.gas_limit.to() { + return Err(EthApiError::Other(Box::new( + EthSimulateError::BlockGasLimitExceeded, + )) + .into()) + } + + if txs_without_gas_limit > 0 { + (block_env.gas_limit.to::() - total_specified_gas) / + txs_without_gas_limit as u64 + } else { + 0 + } + }; let mut calls = calls.into_iter().peekable(); - let mut senders = Vec::with_capacity(transactions.len()); + let mut transactions = Vec::with_capacity(calls.len()); + let mut senders = Vec::with_capacity(calls.len()); let mut results = Vec::with_capacity(calls.len()); - while let Some(tx) = calls.next() { - let env = this.build_call_evm_env( + while let Some(call) = calls.next() { + let sender = call.from.unwrap_or_default(); + + // Resolve transaction, populate missing fields and enforce calls + // correctness. + let tx = simulate::resolve_transaction( + call, + validation, + default_gas_limit, + cfg_env_with_handler_cfg.chain_id, + &mut db, + this.tx_resp_builder(), + )?; + + let tx_env = this.evm_config().tx_env(&tx, sender); + let env = EnvWithHandlerCfg::new_with_cfg_env( cfg_env_with_handler_cfg.clone(), block_env.clone(), - tx, - )?; + tx_env, + ); let (res, env) = { if trace_transfers { @@ -190,6 +215,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA db.commit(res.state); } + transactions.push(tx); senders.push(env.tx.caller); results.push(res.result); } diff --git a/crates/rpc/rpc-eth-types/src/simulate.rs b/crates/rpc/rpc-eth-types/src/simulate.rs index 94bdcd8687..a5d4773981 100644 --- a/crates/rpc/rpc-eth-types/src/simulate.rs +++ b/crates/rpc/rpc-eth-types/src/simulate.rs @@ -50,87 +50,62 @@ impl ToRpcError for EthSimulateError { /// /// If validation is enabled, the function will return error if any of the transactions can't be /// built right away. -pub fn resolve_transactions>( - txs: &mut [TransactionRequest], +pub fn resolve_transaction>( + mut tx: TransactionRequest, validation: bool, - block_gas_limit: u64, + default_gas_limit: u64, chain_id: u64, db: &mut DB, tx_resp_builder: &T, -) -> Result, EthApiError> +) -> Result where EthApiError: From, { - let mut transactions = Vec::with_capacity(txs.len()); + if tx.buildable_type().is_none() && validation { + return Err(EthApiError::TransactionConversionError); + } + // If we're missing any fields and validation is disabled, we try filling nonce, gas and + // gas price. + let tx_type = tx.preferred_type(); - let default_gas_limit = { - let total_specified_gas = txs.iter().filter_map(|tx| tx.gas).sum::(); - let txs_without_gas_limit = txs.iter().filter(|tx| tx.gas.is_none()).count(); - - if total_specified_gas > block_gas_limit { - return Err(EthApiError::Other(Box::new(EthSimulateError::BlockGasLimitExceeded))) - } - - if txs_without_gas_limit > 0 { - (block_gas_limit - total_specified_gas) / txs_without_gas_limit as u64 - } else { - 0 - } + let from = if let Some(from) = tx.from { + from + } else { + tx.from = Some(Address::ZERO); + Address::ZERO }; - for tx in txs { - if tx.buildable_type().is_none() && validation { - return Err(EthApiError::TransactionConversionError); - } - // If we're missing any fields and validation is disabled, we try filling nonce, gas and - // gas price. - let tx_type = tx.preferred_type(); - - let from = if let Some(from) = tx.from { - from - } else { - tx.from = Some(Address::ZERO); - Address::ZERO - }; - - if tx.nonce.is_none() { - tx.nonce = Some(db.basic(from)?.map(|acc| acc.nonce).unwrap_or_default()); - } - - if tx.gas.is_none() { - tx.gas = Some(default_gas_limit); - } - - if tx.chain_id.is_none() { - tx.chain_id = Some(chain_id); - } - - if tx.to.is_none() { - tx.to = Some(TxKind::Create); - } - - match tx_type { - TxType::Legacy | TxType::Eip2930 => { - if tx.gas_price.is_none() { - tx.gas_price = Some(0); - } - } - _ => { - if tx.max_fee_per_gas.is_none() { - tx.max_fee_per_gas = Some(0); - tx.max_priority_fee_per_gas = Some(0); - } - } - } - - transactions.push( - tx_resp_builder - .build_simulate_v1_transaction(tx.clone()) - .map_err(|e| EthApiError::other(e.into()))?, - ); + if tx.nonce.is_none() { + tx.nonce = Some(db.basic(from)?.map(|acc| acc.nonce).unwrap_or_default()); } - Ok(transactions) + if tx.gas.is_none() { + tx.gas = Some(default_gas_limit); + } + + if tx.chain_id.is_none() { + tx.chain_id = Some(chain_id); + } + + if tx.to.is_none() { + tx.to = Some(TxKind::Create); + } + + match tx_type { + TxType::Legacy | TxType::Eip2930 => { + if tx.gas_price.is_none() { + tx.gas_price = Some(0); + } + } + _ => { + if tx.max_fee_per_gas.is_none() { + tx.max_fee_per_gas = Some(0); + tx.max_priority_fee_per_gas = Some(0); + } + } + } + + tx_resp_builder.build_simulate_v1_transaction(tx).map_err(|e| EthApiError::other(e.into())) } /// Handles outputs of the calls execution and builds a [`SimulatedBlock`].