diff --git a/crates/module-system/module-implementations/sov-evm/src/helpers.rs b/crates/module-system/module-implementations/sov-evm/src/helpers.rs index 8283d27a07..8185acb495 100644 --- a/crates/module-system/module-implementations/sov-evm/src/helpers.rs +++ b/crates/module-system/module-implementations/sov-evm/src/helpers.rs @@ -67,6 +67,19 @@ pub(crate) fn from_recovered_with_block_context( alloy_rpc_types::Transaction::from_transaction(tx.convert(), tx_info) } +pub(crate) fn from_recovered_pending( + tx: Recovered, +) -> alloy_rpc_types::Transaction { + let tx_info = TransactionInfo { + block_hash: None, + block_number: None, + index: None, + hash: None, + base_fee: None, + }; + alloy_rpc_types::Transaction::from_transaction(tx.convert(), tx_info) +} + #[cfg(test)] mod tests { use alloy_primitives::{Address, U256}; diff --git a/crates/module-system/module-implementations/sov-evm/src/rpc/handlers.rs b/crates/module-system/module-implementations/sov-evm/src/rpc/handlers.rs index 5ecfd21387..1ef7a28bf8 100644 --- a/crates/module-system/module-implementations/sov-evm/src/rpc/handlers.rs +++ b/crates/module-system/module-implementations/sov-evm/src/rpc/handlers.rs @@ -1,6 +1,7 @@ use crate::error::into_rpc_error; use crate::rpc::error::ensure_success; use alloy_consensus::ReceiptEnvelope; +use alloy_consensus::Transaction as TransactionTrait; use alloy_eips::BlockId; use alloy_primitives::{Address, U64}; use alloy_primitives::{Bytes, B256, U256}; @@ -153,16 +154,32 @@ where block_id: Option, state: &mut ApiStateAccessor, ) -> RpcResult { - let mut state = self.resolve_state_for_block_id(block_id, state)?; - - let ethereum_address: EthereumAddress = address.into(); - let credential_id = ethereum_address.as_credential_id(); - - let nonce = self - .uniqueness_module - .next_nonce(&credential_id, state.deref_mut()) - .unwrap_or_default(); - + let block_id = block_id.unwrap_or_else(BlockId::latest); + let is_pending = block_id.is_latest() || block_id.is_pending(); + + let nonce = { + let mut resolved_state = self.resolve_state_for_block_id(Some(block_id), state)?; + let ethereum_address: EthereumAddress = address.into(); + let credential_id = ethereum_address.as_credential_id(); + self.uniqueness_module + .next_nonce(&credential_id, resolved_state.deref_mut()) + .unwrap_or_default() + }; + + let pending_nonce = if is_pending { + let pending_txs: Vec<_> = self.pending_transactions.collect_infallible(state); + pending_txs + .iter() + .filter(|pending| pending.transaction.signer == address) + .map(|pending| pending.transaction.signed_transaction.nonce()) + .max() + .map(|nonce| nonce.saturating_add(1)) + .unwrap_or(0) + } else { + 0 + }; + + let nonce = nonce.max(pending_nonce); trace!(%address, nonce, method = "eth_getTransactionCount", "EVM module JSON-RPC request"); Ok(U64::from(nonce)) } @@ -217,7 +234,9 @@ where hash: B256, state: &mut ApiStateAccessor, ) -> RpcResult> { - let transaction = self.get_transaction(hash, state); + let transaction = self + .get_transaction(hash, state) + .or_else(|| self.get_pending_transaction(hash, state)); trace!( %hash, ?transaction, diff --git a/crates/module-system/module-implementations/sov-evm/src/rpc/mod.rs b/crates/module-system/module-implementations/sov-evm/src/rpc/mod.rs index ffd9d99127..0cab4539a7 100644 --- a/crates/module-system/module-implementations/sov-evm/src/rpc/mod.rs +++ b/crates/module-system/module-implementations/sov-evm/src/rpc/mod.rs @@ -5,7 +5,7 @@ use crate::error::into_rpc_error; use crate::evm::executor; use crate::evm::primitive_types::{Receipt, TransactionSigned, TxSignedAndRecovered}; use crate::executor::get_cfg_env; -use crate::helpers::{from_recovered_with_block_context, prepare_call_env}; +use crate::helpers::{from_recovered_pending, from_recovered_with_block_context, prepare_call_env}; pub use crate::primitive_types::MaybeSealedBlock; use crate::{verify_contract_creation_allowlist, Evm, SealedBlock}; use alloy_consensus::{transaction::Recovered, Transaction as TransactionTrait, TxReceipt}; @@ -151,6 +151,18 @@ where Some(tx) } + fn get_pending_transaction( + &self, + hash: B256, + state: &mut ApiStateAccessor, + ) -> Option { + let pending_transactions: Vec<_> = self.pending_transactions.collect_infallible(state); + let pending = pending_transactions + .iter() + .find(|pending| *pending.transaction.signed_transaction.hash() == hash)?; + Some(from_recovered_pending(pending.transaction.clone().into())) + } + fn get_receipt_by_hash( &self, hash: B256,