use crate::client::BackendClient;
use alloy::consensus::TxEnvelope;
use alloy::providers::{Provider, ProviderBuilder, RootProvider};
use alloy::pubsub::{PubSubFrontend, Subscription};
use alloy::rpc::types::eth::{
Block, BlockNumberOrTag, FeeHistory, Filter, Header, Log, SyncStatus, Transaction,
TransactionReceipt, TransactionRequest,
};
use alloy::transports::http::{Client, Http};
use alloy::transports::ws::WsConnect;
use alloy::transports::{TransportError, TransportResult};
use alloy_json_rpc::{RpcParam, RpcReturn};
use alloy_primitives::{Address, BlockHash, BlockNumber, Bytes, ChainId, B256, U256, U64};
use alloy_rlp::Encodable;
use eigen_logging::get_test_logger;
use eigen_metrics_collectors_rpc_calls::RpcCallsMetrics as RpcCallsCollector;
use hex;
use std::time::Instant;
use thiserror::Error;
use url::Url;
const PENDING_TAG: &str = "pending";
pub struct InstrumentedClient {
http_client: Option<RootProvider<Http<Client>>>,
ws_client: Option<RootProvider<PubSubFrontend>>,
rpc_collector: RpcCallsCollector,
net_version: u64,
}
#[derive(Error, Debug)]
pub enum InstrumentedClientError {
#[error("invalid url")]
InvalidUrl,
#[error("error getting version")]
ErrorGettingVersion,
#[error("error running command")]
CommandError,
}
#[async_trait::async_trait]
impl BackendClient for InstrumentedClient {
type Error = InstrumentedClientError;
async fn block_number(&self) -> Result<BlockNumber, Self::Error> {
self.instrument_function("eth_blockNumber", ())
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get block number", err.to_string().as_str())
})
.map_err(|_err| InstrumentedClientError::CommandError)
.map(|result: U64| result.to())
}
async fn block_by_number(
&self,
number: BlockNumberOrTag,
) -> Result<Option<Block>, Self::Error> {
self.instrument_function("eth_getBlockByNumber", (number, true))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get block by number", err.to_string().as_str())
})
.map_err(|_err| InstrumentedClientError::CommandError)
}
}
impl InstrumentedClient {
pub async fn new(url: &str) -> Result<Self, InstrumentedClientError> {
let url = Url::parse(url).map_err(|_| InstrumentedClientError::InvalidUrl)?;
let http_client = ProviderBuilder::new().on_http(url);
let net_version = http_client
.get_net_version()
.await
.map_err(|_| InstrumentedClientError::ErrorGettingVersion)?;
let rpc_collector = RpcCallsCollector::new(get_test_logger().clone());
Ok(InstrumentedClient {
http_client: Some(http_client),
ws_client: None,
rpc_collector,
net_version,
})
}
pub async fn new_ws(url: &str) -> Result<Self, InstrumentedClientError> {
let url = Url::parse(url).map_err(|_| InstrumentedClientError::InvalidUrl)?;
let ws_connect = WsConnect::new(url);
let ws_client = ProviderBuilder::new().on_ws(ws_connect).await.unwrap();
let net_version = ws_client
.get_net_version()
.await
.map_err(|_| InstrumentedClientError::ErrorGettingVersion)?;
let rpc_collector = RpcCallsCollector::new(get_test_logger().clone());
Ok(InstrumentedClient {
http_client: None,
ws_client: Some(ws_client),
rpc_collector,
net_version,
})
}
pub async fn new_from_client(
client: RootProvider<Http<Client>>,
) -> Result<Self, InstrumentedClientError> {
let net_version = client
.get_net_version()
.await
.map_err(|_| InstrumentedClientError::ErrorGettingVersion)?;
let rpc_collector = RpcCallsCollector::new(get_test_logger().clone());
Ok(InstrumentedClient {
http_client: Some(client),
ws_client: None,
rpc_collector,
net_version,
})
}
pub async fn chain_id(&self) -> TransportResult<ChainId> {
self.instrument_function("eth_chainId", ())
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get chain id", err.to_string().as_str())
})
.map(|result: U64| result.to())
}
pub async fn balance_at(
&self,
account: Address,
block_number: BlockNumberOrTag,
) -> TransportResult<U256> {
self.instrument_function("eth_getBalance", (account, block_number))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get balance", err.to_string().as_str())
})
}
pub async fn block_by_hash(&self, hash: BlockHash) -> TransportResult<Option<Block>> {
self.instrument_function("eth_getBlockByHash", (hash, true))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get block by hash", err.to_string().as_str())
})
}
pub async fn call_contract(
&self,
call: TransactionRequest,
block_number: BlockNumberOrTag,
) -> TransportResult<Bytes> {
self.instrument_function("eth_call", (call, block_number))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to call contract", err.to_string().as_str())
})
}
pub async fn code_at(
&self,
address: Address,
block_number: BlockNumberOrTag,
) -> TransportResult<Bytes> {
self.instrument_function("eth_getCode", (address, block_number))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get code", err.to_string().as_str())
})
}
pub async fn estimate_gas(&self, tx: TransactionRequest) -> TransportResult<u64> {
self.instrument_function("eth_estimateGas", (tx,))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to estimate gas", err.to_string().as_str())
})
.map(|result: U64| result.to())
}
pub async fn fee_history(
&self,
block_count: u64,
last_block: BlockNumberOrTag,
reward_percentiles: &[f64],
) -> TransportResult<FeeHistory> {
self.instrument_function(
"eth_feeHistory",
(block_count, last_block, reward_percentiles),
)
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get fee history", err.to_string().as_str())
})
}
pub async fn filter_logs(&self, filter: Filter) -> TransportResult<Vec<Log>> {
self.instrument_function("eth_getLogs", (filter,))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get filter logs", err.to_string().as_str())
})
}
pub async fn header_by_hash(&self, hash: B256) -> TransportResult<Header> {
let transaction_detail = false;
self.instrument_function("eth_getBlockByHash", (hash, transaction_detail))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get header by hash", err.to_string().as_str())
})
}
pub async fn header_by_number(
&self,
block_number: BlockNumberOrTag,
) -> TransportResult<Header> {
let transaction_detail = false;
self.instrument_function("eth_getBlockByNumber", (block_number, transaction_detail))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get header by number", err.to_string().as_str())
})
}
pub async fn nonce_at(
&self,
account: Address,
block_number: BlockNumberOrTag,
) -> TransportResult<u64> {
self.instrument_function("eth_getTransactionCount", (account, block_number))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get nonce", err.to_string().as_str())
})
.map(|result: U64| result.to())
}
pub async fn pending_balance_at(&self, account: Address) -> TransportResult<U256> {
self.instrument_function("eth_getBalance", (account, PENDING_TAG))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get pending balance", err.to_string().as_str())
})
}
pub async fn pending_call_contract(&self, call: TransactionRequest) -> TransportResult<Bytes> {
self.call_contract(call, BlockNumberOrTag::Pending).await
}
pub async fn pending_code_at(&self, account: Address) -> TransportResult<Bytes> {
self.instrument_function("eth_getCode", (account, PENDING_TAG))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get pending code", err.to_string().as_str())
})
}
pub async fn pending_nonce_at(&self, account: Address) -> TransportResult<u64> {
self.instrument_function("eth_getTransactionCount", (account, PENDING_TAG))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get pending nonce", err.to_string().as_str())
})
.map(|result: U64| result.to())
}
pub async fn pending_storage_at(&self, account: Address, key: U256) -> TransportResult<U256> {
self.instrument_function("eth_getStorageAt", (account, key, PENDING_TAG))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get pending storage", err.to_string().as_str())
})
}
pub async fn pending_transaction_count(&self) -> TransportResult<u64> {
self.instrument_function("eth_getBlockTransactionCountByNumber", (PENDING_TAG,))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get transaction count", err.to_string().as_str())
})
.map(|result: U64| result.to())
}
pub async fn send_transaction(&self, tx: TxEnvelope) -> TransportResult<B256> {
let mut encoded_tx = Vec::new();
tx.encode(&mut encoded_tx);
self.instrument_function("eth_sendRawTransaction", (hex::encode(encoded_tx),))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to send transaction", err.to_string().as_str())
})
}
pub async fn storage_at(
&self,
account: Address,
key: U256,
block_number: U256,
) -> TransportResult<U256> {
self.instrument_function("eth_getStorageAt", (account, key, block_number))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get storage", err.to_string().as_str())
})
}
pub async fn subscribe_filter_logs<R: RpcReturn>(
&self,
filter: Filter,
) -> TransportResult<Subscription<R>> {
let id: U256 = self
.instrument_function("eth_subscribe", ("logs", filter))
.await
.inspect_err(|err| {
self.rpc_collector.logger().error(
"Failed to get logs subscription id",
err.to_string().as_str(),
)
})?;
if let Some(ws_client) = self.ws_client.as_ref() {
ws_client.get_subscription(id.into()).await
} else {
Err(TransportError::UnsupportedFeature(
"http client does not support eth_subscribe calls.",
))
}
}
pub async fn subscribe_new_head<R: RpcReturn>(&self) -> TransportResult<Subscription<R>> {
let id: U256 = self
.instrument_function("eth_subscribe", ("newHeads",))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to subscribe new head", err.to_string().as_str())
})?;
if let Some(ws_client) = self.ws_client.as_ref() {
ws_client.get_subscription(id.into()).await
} else {
Err(TransportError::UnsupportedFeature(
"http client does not support eth_subscribe calls.",
))
}
}
pub async fn suggest_gas_price(&self) -> TransportResult<u64> {
self.instrument_function("eth_gasPrice", ())
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to suggest gas price", err.to_string().as_str())
})
.map(|result: U64| result.to())
}
pub async fn suggest_gas_tip_cap(&self) -> TransportResult<u64> {
self.instrument_function("eth_maxPriorityFeePerGas", ())
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to suggest gas tip cap", err.to_string().as_str())
})
.map(|result: U64| result.to())
}
pub async fn sync_progress(&self) -> TransportResult<SyncStatus> {
self.instrument_function("eth_syncing", ())
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get sync progress", err.to_string().as_str())
})
}
pub async fn transaction_by_hash(&self, tx_hash: B256) -> TransportResult<Transaction> {
self.instrument_function("eth_getTransactionByHash", (tx_hash,))
.await
.inspect_err(|err| {
self.rpc_collector.logger().error(
"Failed to get transaction by hash",
err.to_string().as_str(),
)
})
}
pub async fn transaction_count(&self, block_hash: B256) -> TransportResult<u64> {
self.instrument_function("eth_getBlockTransactionCountByHash", (block_hash,))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get transaction count", err.to_string().as_str())
})
.map(|result: U64| result.to())
}
pub async fn transaction_in_block(
&self,
block_hash: B256,
index: u64,
) -> TransportResult<Transaction> {
self.instrument_function("eth_getTransactionByBlockHashAndIndex", (block_hash, index))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get transaction", err.to_string().as_str())
})
}
pub async fn transaction_receipt(&self, tx_hash: B256) -> TransportResult<TransactionReceipt> {
self.instrument_function("eth_getTransactionReceipt", (tx_hash,))
.await
.inspect_err(|err| {
self.rpc_collector
.logger()
.error("Failed to get receipt", err.to_string().as_str())
})
}
async fn instrument_function<'async_trait, P, R>(
&self,
rpc_method_name: &str,
params: P,
) -> TransportResult<R>
where
P: RpcParam + 'async_trait,
R: RpcReturn + 'async_trait,
{
let start = Instant::now();
let method_string = String::from(rpc_method_name);
let result = match (self.http_client.as_ref(), self.ws_client.as_ref()) {
(Some(http_client), _) => http_client.raw_request(method_string.into(), params).await,
(_, Some(ws_client)) => ws_client.raw_request(method_string.into(), params).await,
(_, _) => unreachable!(),
};
let rpc_request_duration = start.elapsed();
self.rpc_collector.set_rpc_request_duration_seconds(
rpc_method_name,
self.net_version.to_string().as_str(),
rpc_request_duration.as_secs_f64(),
);
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy::consensus::{SignableTransaction, TxLegacy};
use alloy::network::TxSignerSync;
use alloy::primitives::{bytes, TxKind::Call, U256};
use alloy::rpc::types::eth::{
pubsub::SubscriptionResult, BlockId, BlockNumberOrTag, BlockTransactionsKind,
};
use alloy_primitives::address;
use eigen_common::get_provider;
use eigen_signer::signer::Config;
use eigen_testing_utils::anvil::start_anvil_container;
use eigen_testing_utils::transaction::wait_transaction;
use tokio;
#[tokio::test]
async fn test_suggest_gas_tip_cap() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let fee_per_gas = instrumented_client.suggest_gas_tip_cap().await.unwrap();
let expected_fee_per_gas = get_provider(&http_endpoint)
.get_max_priority_fee_per_gas()
.await
.unwrap();
assert_eq!(expected_fee_per_gas as u64, fee_per_gas);
}
#[tokio::test]
async fn test_gas_price() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let gas_price = instrumented_client.suggest_gas_price().await.unwrap();
let expected_gas_price = provider.clone().get_gas_price().await.unwrap();
assert_eq!(gas_price, expected_gas_price as u64);
}
#[tokio::test]
async fn test_sync_status() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let sync_status = instrumented_client.sync_progress().await.unwrap();
let expected_sync_status = provider.clone().syncing().await.unwrap();
assert_eq!(expected_sync_status, sync_status);
}
#[tokio::test]
async fn test_chain_id() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let expected_chain_id = provider.clone().get_chain_id().await.unwrap();
let chain_id = instrumented_client.chain_id().await.unwrap();
assert_eq!(expected_chain_id, chain_id);
}
#[tokio::test]
async fn test_balance_at() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let address = provider.get_accounts().await.unwrap()[0];
let expected_balance_at = provider.get_balance(address).await.unwrap();
let balance_at = instrumented_client
.balance_at(address, BlockNumberOrTag::Latest)
.await
.unwrap();
assert_eq!(expected_balance_at, balance_at);
}
#[tokio::test]
async fn test_subscribe_new_head() {
let (_container, _http_endpoint, ws_endpoint) = start_anvil_container().await;
let instrumented_client = InstrumentedClient::new_ws(&ws_endpoint).await.unwrap();
let subscription: TransportResult<Subscription<SubscriptionResult>> =
instrumented_client.subscribe_new_head().await;
assert!(subscription.is_ok())
}
#[tokio::test]
async fn test_subscribe_filter_logs() {
let (_container, http_endpoint, ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new_ws(&ws_endpoint).await.unwrap();
let address = provider.clone().get_accounts().await.unwrap()[0];
let filter = Filter::new().address(address.to_string().parse::<Address>().unwrap());
let subscription: TransportResult<Subscription<SubscriptionResult>> =
instrumented_client.subscribe_filter_logs(filter).await;
assert!(subscription.is_ok());
}
#[tokio::test]
async fn test_block_by_hash() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let hash = provider
.get_block(BlockId::latest(), BlockTransactionsKind::Hashes)
.await
.unwrap()
.unwrap()
.header
.hash;
let expected_block = provider
.get_block_by_hash(hash, BlockTransactionsKind::Full)
.await
.unwrap();
let block = instrumented_client.block_by_hash(hash).await.unwrap();
assert_eq!(expected_block, block);
}
#[tokio::test]
async fn test_block_by_number() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let block_number = 1;
let expected_block = provider
.get_block_by_number(block_number.into(), BlockTransactionsKind::Full)
.await
.unwrap();
let block = instrumented_client
.block_by_number(block_number.into())
.await
.unwrap();
assert_eq!(expected_block, block);
}
#[tokio::test]
async fn test_transaction_count() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let block = provider
.get_block(BlockId::latest(), BlockTransactionsKind::Hashes)
.await
.unwrap()
.unwrap();
let block_hash = block.header.hash;
let tx_count = instrumented_client
.transaction_count(B256::from_slice(block_hash.as_slice()))
.await
.unwrap();
let expected_tx_count = block.transactions.len();
assert_eq!(tx_count, expected_tx_count as u64);
}
#[tokio::test]
async fn test_transaction_methods() {
let (_container, rpc_url, _ws_endpoint) = start_anvil_container().await;
let instrumented_client = InstrumentedClient::new(&rpc_url).await.unwrap();
let to = address!("a0Ee7A142d267C1f36714E4a8F75612F20a79720");
let mut tx = TxLegacy {
to: Call(to),
value: U256::from(0),
gas_limit: 2_000_000,
nonce: 0x69, gas_price: 21_000_000_000,
input: bytes!(),
chain_id: Some(31337),
};
let private_key_hex =
"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".to_string();
let config = Config::PrivateKey(private_key_hex);
let signer = Config::signer_from_config(config).unwrap();
let signature = signer.sign_transaction_sync(&mut tx).unwrap();
let signed_tx = tx.into_signed(signature);
let tx: TxEnvelope = TxEnvelope::from(signed_tx);
let tx_hash = instrumented_client.send_transaction(tx).await;
assert!(tx_hash.is_ok());
let tx_hash = B256::from_slice(tx_hash.unwrap().as_ref());
let tx_by_hash = instrumented_client.transaction_by_hash(tx_hash).await;
assert!(tx_by_hash.is_ok());
wait_transaction(&rpc_url, tx_hash).await.unwrap();
let receipt = instrumented_client.transaction_receipt(tx_hash).await;
assert!(receipt.is_ok());
let receipt = receipt.unwrap();
let tx_by_block = instrumented_client
.transaction_in_block(
receipt.block_hash.unwrap(),
receipt.transaction_index.unwrap(),
)
.await;
assert!(tx_by_block.is_ok());
}
#[tokio::test]
async fn test_estimate_gas() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let accounts = provider.get_accounts().await.unwrap();
let from = accounts.first().unwrap();
let to = accounts.get(1).unwrap();
let tx = TxLegacy {
to: Call(*to),
value: U256::from(0),
gas_limit: 2_000_000,
nonce: 0,
gas_price: 1_000_000,
input: bytes!(),
chain_id: Some(31337),
};
let tx_request: TransactionRequest = tx.clone().into();
let tx_request = tx_request.from(*from);
let expected_estimated_gas = provider.clone().estimate_gas(&tx_request).await.unwrap();
let estimated_gas = instrumented_client.estimate_gas(tx_request).await.unwrap();
assert_eq!(expected_estimated_gas, estimated_gas);
}
#[tokio::test]
async fn test_call_contract_and_pending_call_contract() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let anvil = provider.clone();
let accounts = anvil.get_accounts().await.unwrap();
let from = accounts.first().unwrap();
let to = accounts.get(1).unwrap();
let nonce = instrumented_client
.nonce_at(*from, BlockNumberOrTag::Latest)
.await
.unwrap();
let tx = TxLegacy {
to: Call(*to),
value: U256::from(0),
gas_limit: 1_000_000,
nonce,
gas_price: 21_000_000_000,
input: bytes!(),
chain_id: Some(31337),
};
let tx_request: TransactionRequest = tx.clone().into();
let tx_request = tx_request.from(*from);
let expected_bytes = anvil.call(&tx_request).await.unwrap();
let bytes = instrumented_client
.call_contract(tx_request.clone(), BlockNumberOrTag::Earliest)
.await
.unwrap();
assert_eq!(expected_bytes, bytes);
let bytes = instrumented_client.pending_call_contract(tx_request).await;
assert!(bytes.is_ok());
}
#[tokio::test]
async fn test_filter_logs() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let address = provider.clone().get_accounts().await.unwrap()[0];
let filter = Filter::new().address(address.to_string().parse::<Address>().unwrap());
let expected_logs = provider.clone().get_logs(&filter).await.unwrap();
let logs = instrumented_client.filter_logs(filter).await.unwrap();
assert_eq!(expected_logs, logs);
}
#[tokio::test]
async fn test_storage_at() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let account = provider.clone().get_accounts().await.unwrap()[0];
let expected_storage = provider
.clone()
.get_storage_at(account, U256::ZERO)
.await
.unwrap();
let storage = instrumented_client
.storage_at(account, U256::ZERO, U256::ZERO)
.await
.unwrap();
assert_eq!(expected_storage, storage);
}
#[tokio::test]
async fn test_block_number() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let expected_block_number = provider.clone().get_block_number().await.unwrap();
let block_number = instrumented_client.block_number().await.unwrap();
assert_eq!(expected_block_number, block_number);
}
#[tokio::test]
async fn test_code_at() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let address = provider.get_accounts().await.unwrap()[0];
let expected_code = provider.get_code_at(address).await.unwrap();
let code = instrumented_client
.code_at(address, BlockNumberOrTag::Latest)
.await
.unwrap();
assert_eq!(expected_code, code);
}
#[tokio::test]
async fn test_fee_history() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let block_count = 4;
let last_block = BlockNumberOrTag::Latest;
let reward_percentiles = [0.2, 0.3];
let expected_fee_history = provider
.get_fee_history(block_count, last_block, &reward_percentiles)
.await
.unwrap();
let fee_history = instrumented_client
.fee_history(block_count, last_block, &reward_percentiles)
.await
.unwrap();
assert_eq!(expected_fee_history, fee_history);
}
#[tokio::test]
async fn test_header_by_hash() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let hash = provider
.get_block(BlockId::latest(), BlockTransactionsKind::Hashes)
.await
.unwrap()
.unwrap()
.header
.hash;
let expected_header = provider
.get_block_by_hash(hash, BlockTransactionsKind::Hashes)
.await
.unwrap()
.unwrap()
.header;
let header = instrumented_client.header_by_hash(hash).await.unwrap();
assert_eq!(expected_header, header);
}
#[tokio::test]
async fn test_header_by_number() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let block_number = BlockNumberOrTag::Earliest;
let header = instrumented_client
.header_by_number(block_number)
.await
.unwrap();
let expected_header = provider
.get_block_by_number(block_number, BlockTransactionsKind::Hashes)
.await
.unwrap()
.unwrap()
.header;
assert_eq!(expected_header, header);
}
#[tokio::test]
async fn test_nonce_at() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let address = provider.get_accounts().await.unwrap()[0];
let expected_nonce = provider.get_transaction_count(address).await.unwrap();
let nonce = instrumented_client
.nonce_at(address, BlockNumberOrTag::Latest)
.await
.unwrap();
assert_eq!(expected_nonce, nonce);
}
#[tokio::test]
async fn test_pending_balance_at() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let address = provider.get_accounts().await.unwrap()[0];
let expected_balance = provider.get_balance(address).await.unwrap();
let balance = instrumented_client
.pending_balance_at(address)
.await
.unwrap();
assert_eq!(expected_balance, balance);
}
#[tokio::test]
async fn test_pending_code_at() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let address = provider.get_accounts().await.unwrap()[0];
let expected_code = provider.get_code_at(address).await.unwrap();
let code = instrumented_client.pending_code_at(address).await.unwrap();
assert_eq!(expected_code, code);
}
#[tokio::test]
async fn test_pending_nonce_at() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let address = provider.get_accounts().await.unwrap()[0];
let expected_pending_nonce_at = provider.get_transaction_count(address).await.unwrap();
let pending_nonce_at = instrumented_client.pending_nonce_at(address).await.unwrap();
assert_eq!(expected_pending_nonce_at, pending_nonce_at);
}
#[tokio::test]
async fn test_pending_storage_at() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let address = provider.get_accounts().await.unwrap()[0];
let key = U256::from(10);
let expected_pending_storage_at = provider.get_storage_at(address, key).await.unwrap();
let pending_storage_at = instrumented_client
.pending_storage_at(address, key)
.await
.unwrap();
assert_eq!(expected_pending_storage_at, pending_storage_at);
}
#[tokio::test]
async fn test_pending_transaction_count() {
let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
let provider = get_provider(&http_endpoint);
let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
let expected_transaction_count: u64 = provider
.get_block_by_number(BlockNumberOrTag::Pending, BlockTransactionsKind::Hashes)
.await
.unwrap()
.unwrap()
.transactions
.len() as u64;
let transaction_count = instrumented_client.pending_transaction_count().await;
assert_eq!(expected_transaction_count, transaction_count.unwrap());
}
}