diff --git a/src/backend.rs b/src/backend.rs index 6904f49..1b914fc 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -6,7 +6,7 @@ use crate::{ }; use alloy_primitives::{keccak256, Address, Bytes, B256, U256}; use alloy_provider::{network::AnyNetwork, Provider}; -use alloy_rpc_types::{Block, BlockId, Transaction}; +use alloy_rpc_types::{Account, Block, BlockId, Transaction}; use alloy_serde::WithOtherFields; use alloy_transport::Transport; use eyre::WrapErr; @@ -31,7 +31,7 @@ use std::{ pin::Pin, sync::{ mpsc::{channel as oneshot_channel, Sender as OneshotSender}, - Arc, + Arc, OnceLock, }, }; @@ -108,6 +108,15 @@ enum BackendRequest { UpdateBlockHash(BlockHashData), } +/// Serves as a marker to identify whether the RPC provider supports `eth_getAccount` +#[derive(PartialEq, Clone)] +enum GetAccountMode { + /// The provider supports `eth_getAccount` + EthGetAccount, + /// It doesn't support, and we have to fetch balance, nonce, and code concurrently + AccountCodeNonce, +} + /// Handles an internal provider and listens for requests. /// /// This handler will remain active as long as it is reachable (request channel still open) and @@ -133,6 +142,8 @@ pub struct BackendHandler { /// The block to fetch data from. // This is an `Option` so that we can have less code churn in the functions below block_id: Option, + /// Marker identifying whether the RPC provider supports `eth_getAccount` + get_account_mode: OnceLock, } impl BackendHandler @@ -157,6 +168,7 @@ where incoming: rx, block_id, transport: PhantomData, + get_account_mode: OnceLock::new(), } } @@ -250,14 +262,64 @@ where /// returns the future that fetches the account data fn get_account_req(&self, address: Address) -> ProviderRequest { trace!(target: "backendhandler", "preparing account request, address={:?}", address); + let provider = self.provider.clone(); let block_id = self.block_id.unwrap_or_default(); + let get_acc_mode = self.get_account_mode.clone(); + if get_acc_mode.get().is_none() { + let fut = Box::pin(async move { + let res = match provider.get_account(address).block_id(block_id).await { + Ok(Account { balance, nonce, code_hash, .. }) => { + let code = if code_hash != KECCAK_EMPTY { + provider + .get_code_at(address) + .block_id(block_id) + .await + .unwrap_or_default() + } else { + Bytes::default() + }; + + let _ = get_acc_mode.set(GetAccountMode::EthGetAccount); + Ok((balance, nonce, code)) + } + Err(err) => { + let _ = get_acc_mode.set(GetAccountMode::AccountCodeNonce); + Err(err.into()) + } + }; + + (res, address) + }); + return ProviderRequest::Account(fut); + } + let fut = Box::pin(async move { - let balance = provider.get_balance(address).block_id(block_id).into_future(); - let nonce = provider.get_transaction_count(address).block_id(block_id).into_future(); - let code = provider.get_code_at(address).block_id(block_id).into_future(); - let resp = tokio::try_join!(balance, nonce, code).map_err(Into::into); - (resp, address) + if let Some(GetAccountMode::EthGetAccount) = get_acc_mode.get() { + let res = match provider.get_account(address).block_id(block_id).await { + Ok(Account { balance, nonce, code_hash, .. }) => { + let code = if code_hash != KECCAK_EMPTY { + provider + .get_code_at(address) + .block_id(block_id) + .await + .unwrap_or_default() + } else { + Bytes::default() + }; + Ok((balance, nonce, code)) + } + Err(err) => Err(err.into()), + }; + (res, address) + } else { + let balance = provider.get_balance(address).block_id(block_id).into_future(); + let nonce = + provider.get_transaction_count(address).block_id(block_id).into_future(); + let code = provider.get_code_at(address).block_id(block_id).into_future(); + let resp = tokio::try_join!(balance, nonce, code).map_err(Into::into); + (resp, address) + } }); ProviderRequest::Account(fut) }