diff --git a/crates/rpc/rpc-types/src/eth/call.rs b/crates/rpc/rpc-types/src/eth/call.rs index 6076d0733..507cc5271 100644 --- a/crates/rpc/rpc-types/src/eth/call.rs +++ b/crates/rpc/rpc-types/src/eth/call.rs @@ -28,3 +28,12 @@ pub struct CallRequest { /// AccessList pub access_list: Option, } + +impl CallRequest { + /// Returns the configured fee cap, if any. + /// + /// The returns `gas_price` (legacy) if set or `max_fee_per_gas` (EIP1559) + pub fn fee_cap(&self) -> Option { + self.gas_price.or(self.max_fee_per_gas) + } +} diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index dabd21045..602d45c33 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -68,7 +68,10 @@ where // impls and providers cfg.disable_block_gas_limit = true; - let env = build_call_evm_env(cfg, block, request)?; + let request_gas = request.gas; + + let mut env = build_call_evm_env(cfg, block, request)?; + let mut db = SubState::new(State::new(state)); // apply state overrides @@ -76,6 +79,23 @@ where apply_state_overrides(state_overrides, &mut db)?; } + if request_gas.is_none() && env.tx.gas_price > U256::ZERO { + // no gas limit was provided in the request, so we need to cap the request's gas limit + let mut allowance = db.basic(env.tx.caller)?.map(|acc| acc.balance).unwrap_or_default(); + + // subtract transferred value + allowance = allowance + .checked_sub(env.tx.value) + .ok_or_else(|| InvalidTransactionError::InsufficientFunds)?; + + // cap the gas limit + if let Ok(gas_limit) = + allowance.checked_div(env.tx.gas_price).unwrap_or_default().try_into() + { + env.tx.gas_limit = gas_limit; + } + } + transact(&mut db, env) } diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index 09fcb6c6d..fc7c35675 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -57,7 +57,9 @@ where Ok((res, evm.env)) } -/// Creates a new [Env] to be used for executing the [CallRequest] in `eth_call` +/// Creates a new [Env] to be used for executing the [CallRequest] in `eth_call`. +/// +/// Note: this does _not_ access the Database to check the sender. pub(crate) fn build_call_evm_env( cfg: CfgEnv, block: BlockEnv, @@ -68,6 +70,9 @@ pub(crate) fn build_call_evm_env( } /// Configures a new [TxEnv] for the [CallRequest] +/// +/// All [TxEnv] fields are derived from the given [CallRequest], if fields are `None`, they fall +/// back to the [BlockEnv]'s settings. pub(crate) fn create_txn_env(block_env: &BlockEnv, request: CallRequest) -> EthResult { let CallRequest { from,