1use crate::client::BackendClient;
2use alloy::consensus::TxEnvelope;
3use alloy::primitives::{Address, BlockHash, BlockNumber, Bytes, ChainId, B256, U256, U64};
4use alloy::providers::{Provider, ProviderBuilder, RootProvider};
5use alloy::pubsub::Subscription;
6use alloy::rlp::Encodable;
7use alloy::rpc::json_rpc::{RpcRecv, RpcSend};
8use alloy::rpc::types::eth::{
9 Block, BlockNumberOrTag, FeeHistory, Filter, Header, Log, SyncStatus, Transaction,
10 TransactionReceipt, TransactionRequest,
11};
12use alloy::transports::ws::WsConnect;
13use alloy::transports::{TransportError, TransportResult};
14use eigen_metrics_collectors_rpc_calls::RpcCallsMetrics as RpcCallsCollector;
15use hex;
16use std::time::Instant;
17use thiserror::Error;
18use tracing::error;
19use url::Url;
20
21const PENDING_TAG: &str = "pending";
22
23pub struct InstrumentedClient {
26 http_client: Option<RootProvider>,
27 ws_client: Option<RootProvider>,
28 rpc_collector: RpcCallsCollector,
29 net_version: u64,
30}
31
32#[derive(Error, Debug)]
34pub enum InstrumentedClientError {
35 #[error("invalid url")]
36 InvalidUrl,
37 #[error("error getting version")]
38 ErrorGettingVersion,
39 #[error("error running command")]
40 CommandError,
41}
42
43#[async_trait::async_trait]
44impl BackendClient for InstrumentedClient {
45 type Error = InstrumentedClientError;
46
47 async fn block_number(&self) -> Result<BlockNumber, Self::Error> {
57 self.instrument_function("eth_blockNumber", ())
58 .await
59 .inspect_err(|err| error!("Failed to get block number: {:?}", err.to_string()))
60 .map_err(|_err| InstrumentedClientError::CommandError)
61 .map(|result: U64| result.to())
62 }
63
64 async fn block_by_number(
78 &self,
79 number: BlockNumberOrTag,
80 ) -> Result<Option<Block>, Self::Error> {
81 self.instrument_function("eth_getBlockByNumber", (number, true))
82 .await
83 .inspect_err(|err| error!("Failed to get block by number: {:?}", err.to_string()))
84 .map_err(|_err| InstrumentedClientError::CommandError)
85 }
86}
87
88impl InstrumentedClient {
89 pub async fn new(url: &str) -> Result<Self, InstrumentedClientError> {
103 let url = Url::parse(url).map_err(|_| InstrumentedClientError::InvalidUrl)?;
104 let http_client = ProviderBuilder::new()
105 .disable_recommended_fillers()
106 .connect_http(url);
107 let net_version = http_client
108 .get_net_version()
109 .await
110 .map_err(|_| InstrumentedClientError::ErrorGettingVersion)?;
111
112 let rpc_collector = RpcCallsCollector::new();
113 Ok(InstrumentedClient {
114 http_client: Some(http_client),
115 ws_client: None,
116 rpc_collector,
117 net_version,
118 })
119 }
120
121 pub async fn new_ws(url: &str) -> Result<Self, InstrumentedClientError> {
135 let url = Url::parse(url).map_err(|_| InstrumentedClientError::InvalidUrl)?;
136 let ws_connect = WsConnect::new(url);
137
138 let ws_client = ProviderBuilder::new()
139 .disable_recommended_fillers()
140 .connect_ws(ws_connect)
141 .await
142 .unwrap();
143 let net_version = ws_client
144 .get_net_version()
145 .await
146 .map_err(|_| InstrumentedClientError::ErrorGettingVersion)?;
147
148 let rpc_collector = RpcCallsCollector::new();
149 Ok(InstrumentedClient {
150 http_client: None,
151 ws_client: Some(ws_client),
152 rpc_collector,
153 net_version,
154 })
155 }
156
157 pub async fn new_from_client(client: RootProvider) -> Result<Self, InstrumentedClientError> {
171 let net_version = client
172 .get_net_version()
173 .await
174 .map_err(|_| InstrumentedClientError::ErrorGettingVersion)?;
175
176 let rpc_collector = RpcCallsCollector::new();
177 Ok(InstrumentedClient {
178 http_client: Some(client),
179 ws_client: None,
180 rpc_collector,
181 net_version,
182 })
183 }
184
185 pub async fn chain_id(&self) -> TransportResult<ChainId> {
195 self.instrument_function("eth_chainId", ())
196 .await
197 .inspect_err(|err| error!("Failed to get chain id: {:?}", err.to_string()))
198 .map(|result: U64| result.to())
199 }
200
201 pub async fn balance_at(
212 &self,
213 account: Address,
214 block_number: BlockNumberOrTag,
215 ) -> TransportResult<U256> {
216 self.instrument_function("eth_getBalance", (account, block_number))
217 .await
218 .inspect_err(|err| error!("Failed to get balance: {:?}", err.to_string()))
219 }
220
221 pub async fn block_by_hash(&self, hash: BlockHash) -> TransportResult<Option<Block>> {
231 self.instrument_function("eth_getBlockByHash", (hash, true))
232 .await
233 .inspect_err(|err| error!("Failed to get block by hash: {:?}", err.to_string()))
234 }
235
236 pub async fn call_contract(
247 &self,
248 call: TransactionRequest,
249 block_number: BlockNumberOrTag,
250 ) -> TransportResult<Bytes> {
251 self.instrument_function("eth_call", (call, block_number))
252 .await
253 .inspect_err(|err| error!("Failed to call contract: {:?}", err.to_string()))
254 }
255
256 pub async fn code_at(
267 &self,
268 address: Address,
269 block_number: BlockNumberOrTag,
270 ) -> TransportResult<Bytes> {
271 self.instrument_function("eth_getCode", (address, block_number))
272 .await
273 .inspect_err(|err| error!("Failed to get code: {:?}", err.to_string()))
274 }
275
276 pub async fn estimate_gas(&self, tx: TransactionRequest) -> TransportResult<u64> {
286 self.instrument_function("eth_estimateGas", (tx,))
287 .await
288 .inspect_err(|err| error!("Failed to estimate gas: {:?}", err.to_string()))
289 .map(|result: U64| result.to())
290 }
291
292 pub async fn fee_history(
302 &self,
303 block_count: u64,
304 last_block: BlockNumberOrTag,
305 reward_percentiles: &[f64],
306 ) -> TransportResult<FeeHistory> {
307 self.instrument_function(
308 "eth_feeHistory",
309 (block_count, last_block, reward_percentiles),
310 )
311 .await
312 .inspect_err(|err| error!("Failed to get fee history: {:?}", err.to_string()))
313 }
314 pub async fn filter_logs(&self, filter: Filter) -> TransportResult<Vec<Log>> {
324 self.instrument_function("eth_getLogs", (filter,))
325 .await
326 .inspect_err(|err| error!("Failed to get filter logs: {:?}", err.to_string()))
327 }
328
329 pub async fn header_by_hash(&self, hash: B256) -> TransportResult<Header> {
339 let transaction_detail = false;
340 self.instrument_function("eth_getBlockByHash", (hash, transaction_detail))
341 .await
342 .inspect_err(|err| error!("Failed to get header by hash: {:?}", err.to_string()))
343 }
344
345 pub async fn header_by_number(
355 &self,
356 block_number: BlockNumberOrTag,
357 ) -> TransportResult<Header> {
358 let transaction_detail = false;
359 self.instrument_function("eth_getBlockByNumber", (block_number, transaction_detail))
360 .await
361 .inspect_err(|err| error!("Failed to get header by number: {:?}", err.to_string()))
362 }
363
364 pub async fn nonce_at(
375 &self,
376 account: Address,
377 block_number: BlockNumberOrTag,
378 ) -> TransportResult<u64> {
379 self.instrument_function("eth_getTransactionCount", (account, block_number))
380 .await
381 .inspect_err(|err| error!("Failed to get nonce: {:?}", err.to_string()))
382 .map(|result: U64| result.to())
383 }
384
385 pub async fn pending_balance_at(&self, account: Address) -> TransportResult<U256> {
395 self.instrument_function("eth_getBalance", (account, PENDING_TAG))
396 .await
397 .inspect_err(|err| error!("Failed to get pending balance: {:?}", err.to_string()))
398 }
399
400 pub async fn pending_call_contract(&self, call: TransactionRequest) -> TransportResult<Bytes> {
411 self.call_contract(call, BlockNumberOrTag::Pending).await
412 }
413
414 pub async fn pending_code_at(&self, account: Address) -> TransportResult<Bytes> {
424 self.instrument_function("eth_getCode", (account, PENDING_TAG))
425 .await
426 .inspect_err(|err| error!("Failed to get pending code: {:?}", err.to_string()))
427 }
428
429 pub async fn pending_nonce_at(&self, account: Address) -> TransportResult<u64> {
440 self.instrument_function("eth_getTransactionCount", (account, PENDING_TAG))
441 .await
442 .inspect_err(|err| error!("Failed to get pending nonce: {:?}", err.to_string()))
443 .map(|result: U64| result.to())
444 }
445
446 pub async fn pending_storage_at(&self, account: Address, key: U256) -> TransportResult<U256> {
457 self.instrument_function("eth_getStorageAt", (account, key, PENDING_TAG))
458 .await
459 .inspect_err(|err| error!("Failed to get pending storage: {:?}", err.to_string()))
460 }
461
462 pub async fn pending_transaction_count(&self) -> TransportResult<u64> {
468 self.instrument_function("eth_getBlockTransactionCountByNumber", (PENDING_TAG,))
469 .await
470 .inspect_err(|err| error!("Failed to get transaction count: {:?}", err.to_string()))
471 .map(|result: U64| result.to())
472 }
473
474 pub async fn send_transaction(&self, tx: TxEnvelope) -> TransportResult<B256> {
484 let mut encoded_tx = Vec::new();
485 tx.encode(&mut encoded_tx);
486 self.instrument_function("eth_sendRawTransaction", (hex::encode(encoded_tx),))
487 .await
488 .inspect_err(|err| error!("Failed to send transaction: {:?}", err.to_string()))
489 }
490
491 pub async fn storage_at(
503 &self,
504 account: Address,
505 key: U256,
506 block_number: U256,
507 ) -> TransportResult<U256> {
508 self.instrument_function("eth_getStorageAt", (account, key, block_number))
509 .await
510 .inspect_err(|err| error!("Failed to get storage: {:?}", err.to_string()))
511 }
512
513 pub async fn subscribe_filter_logs<R: RpcRecv>(
527 &self,
528 filter: Filter,
529 ) -> TransportResult<Subscription<R>> {
530 let id: U256 = self
531 .instrument_function("eth_subscribe", ("logs", filter))
532 .await
533 .inspect_err(|err| {
534 error!("Failed to get logs subscription id: {:?}", err.to_string())
535 })?;
536 if let Some(ws_client) = self.ws_client.as_ref() {
537 ws_client.get_subscription(id.into()).await
538 } else {
539 Err(TransportError::UnsupportedFeature(
540 "http client does not support eth_subscribe calls.",
541 ))
542 }
543 }
544
545 pub async fn subscribe_new_head<R: RpcRecv>(&self) -> TransportResult<Subscription<R>> {
556 let id: U256 = self
557 .instrument_function("eth_subscribe", ("newHeads",))
558 .await
559 .inspect_err(|err| error!("Failed to subscribe new head: {:?}", err.to_string()))?;
560 if let Some(ws_client) = self.ws_client.as_ref() {
561 ws_client.get_subscription(id.into()).await
562 } else {
563 Err(TransportError::UnsupportedFeature(
564 "http client does not support eth_subscribe calls.",
565 ))
566 }
567 }
568
569 pub async fn suggest_gas_price(&self) -> TransportResult<u64> {
575 self.instrument_function("eth_gasPrice", ())
576 .await
577 .inspect_err(|err| error!("Failed to suggest gas price: {:?}", err.to_string()))
578 .map(|result: U64| result.to())
579 }
580
581 pub async fn suggest_gas_tip_cap(&self) -> TransportResult<u64> {
587 self.instrument_function("eth_maxPriorityFeePerGas", ())
588 .await
589 .inspect_err(|err| error!("Failed to suggest gas tip cap: {:?}", err.to_string()))
590 .map(|result: U64| result.to())
591 }
592
593 pub async fn sync_progress(&self) -> TransportResult<SyncStatus> {
600 self.instrument_function("eth_syncing", ())
601 .await
602 .inspect_err(|err| error!("Failed to get sync progress: {:?}", err.to_string()))
603 }
604
605 pub async fn transaction_by_hash(&self, tx_hash: B256) -> TransportResult<Transaction> {
615 self.instrument_function("eth_getTransactionByHash", (tx_hash,))
616 .await
617 .inspect_err(|err| error!("Failed to get transaction by hash: {:?}", err.to_string()))
618 }
619
620 pub async fn transaction_count(&self, block_hash: B256) -> TransportResult<u64> {
630 self.instrument_function("eth_getBlockTransactionCountByHash", (block_hash,))
631 .await
632 .inspect_err(|err| error!("Failed to get transaction count: {:?}", err.to_string()))
633 .map(|result: U64| result.to())
634 }
635
636 pub async fn transaction_in_block(
647 &self,
648 block_hash: B256,
649 index: u64,
650 ) -> TransportResult<Transaction> {
651 self.instrument_function("eth_getTransactionByBlockHashAndIndex", (block_hash, index))
652 .await
653 .inspect_err(|err| error!("Failed to get transaction: {:?}", err.to_string()))
654 }
655
656 pub async fn transaction_receipt(&self, tx_hash: B256) -> TransportResult<TransactionReceipt> {
667 self.instrument_function("eth_getTransactionReceipt", (tx_hash,))
668 .await
669 .inspect_err(|err| error!("Failed to get receipt: {:?}", err.to_string()))
670 }
671
672 async fn instrument_function<'async_trait, P, R>(
685 &self,
686 rpc_method_name: &str,
687 params: P,
688 ) -> TransportResult<R>
689 where
690 P: RpcSend + 'async_trait,
691 R: RpcRecv + 'async_trait,
692 {
693 let start = Instant::now();
694 let method_string = String::from(rpc_method_name);
695
696 let result = match (self.http_client.as_ref(), self.ws_client.as_ref()) {
698 (Some(http_client), _) => http_client.raw_request(method_string.into(), params).await,
699 (_, Some(ws_client)) => ws_client.raw_request(method_string.into(), params).await,
700 (_, _) => unreachable!(),
701 };
702
703 let rpc_request_duration = start.elapsed();
704
705 self.rpc_collector.set_rpc_request_duration_seconds(
707 rpc_method_name,
708 self.net_version.to_string().as_str(),
709 rpc_request_duration.as_secs_f64(),
710 );
711 result
712 }
713}
714
715#[cfg(test)]
716mod tests {
717 use super::*;
718 use alloy::consensus::{SignableTransaction, TxLegacy};
719 use alloy::network::TxSignerSync;
720 use alloy::primitives::address;
721 use alloy::primitives::{bytes, TxKind::Call, U256};
722 use alloy::rpc::types::eth::{pubsub::SubscriptionResult, BlockId, BlockNumberOrTag};
723 use alloy::signers::local::PrivateKeySigner;
724 use eigen_common::get_provider;
725 use eigen_testing_utils::anvil::{set_account_balance, start_anvil_container};
726 use eigen_testing_utils::transaction::wait_transaction;
727 use tokio;
728
729 #[tokio::test]
730 async fn test_suggest_gas_tip_cap() {
731 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
732
733 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
734 let fee_per_gas = instrumented_client.suggest_gas_tip_cap().await.unwrap();
735 let expected_fee_per_gas = get_provider(&http_endpoint)
736 .get_max_priority_fee_per_gas()
737 .await
738 .unwrap();
739 assert_eq!(expected_fee_per_gas as u64, fee_per_gas);
740 }
741
742 #[tokio::test]
743 async fn test_gas_price() {
744 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
745 let provider = get_provider(&http_endpoint);
746
747 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
748 let gas_price = instrumented_client.suggest_gas_price().await.unwrap();
749 let expected_gas_price = provider.clone().get_gas_price().await.unwrap();
750 assert_eq!(gas_price, expected_gas_price as u64);
751 }
752
753 #[tokio::test]
754 async fn test_sync_status() {
755 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
756 let provider = get_provider(&http_endpoint);
757
758 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
759 let sync_status = instrumented_client.sync_progress().await.unwrap();
760 let expected_sync_status = provider.clone().syncing().await.unwrap();
761 assert_eq!(expected_sync_status, sync_status);
762 }
763
764 #[tokio::test]
765 async fn test_chain_id() {
766 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
767 let provider = get_provider(&http_endpoint);
768
769 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
770
771 let expected_chain_id = provider.clone().get_chain_id().await.unwrap();
772 let chain_id = instrumented_client.chain_id().await.unwrap();
773
774 assert_eq!(expected_chain_id, chain_id);
775 }
776
777 #[tokio::test]
778 async fn test_balance_at() {
779 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
780 let provider = get_provider(&http_endpoint);
781
782 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
783 let address = provider.get_accounts().await.unwrap()[0];
784
785 let expected_balance_at = provider.get_balance(address).await.unwrap();
786 let balance_at = instrumented_client
787 .balance_at(address, BlockNumberOrTag::Latest)
788 .await
789 .unwrap();
790
791 assert_eq!(expected_balance_at, balance_at);
792 }
793
794 #[tokio::test]
795 async fn test_subscribe_new_head() {
796 let (_container, _http_endpoint, ws_endpoint) = start_anvil_container().await;
797
798 let instrumented_client = InstrumentedClient::new_ws(&ws_endpoint).await.unwrap();
799 let subscription: TransportResult<Subscription<SubscriptionResult>> =
800 instrumented_client.subscribe_new_head().await;
801 assert!(subscription.is_ok())
802 }
803
804 #[tokio::test]
805 async fn test_subscribe_filter_logs() {
806 let (_container, http_endpoint, ws_endpoint) = start_anvil_container().await;
807 let provider = get_provider(&http_endpoint);
808
809 let instrumented_client = InstrumentedClient::new_ws(&ws_endpoint).await.unwrap();
810 let address = provider.clone().get_accounts().await.unwrap()[0];
811 let filter = Filter::new().address(address.to_string().parse::<Address>().unwrap());
812
813 let subscription: TransportResult<Subscription<SubscriptionResult>> =
814 instrumented_client.subscribe_filter_logs(filter).await;
815
816 assert!(subscription.is_ok());
817 }
818
819 #[tokio::test]
820 async fn test_block_by_hash() {
821 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
822 let provider = get_provider(&http_endpoint);
823
824 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
825
826 let hash = provider
828 .get_block(BlockId::latest())
829 .await
830 .unwrap()
831 .unwrap()
832 .header
833 .hash;
834
835 let expected_block = provider.get_block_by_hash(hash).full().await.unwrap();
836 let block = instrumented_client.block_by_hash(hash).await.unwrap();
837
838 assert_eq!(expected_block, block);
839 }
840
841 #[tokio::test]
842 async fn test_block_by_number() {
843 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
844 let provider = get_provider(&http_endpoint);
845
846 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
847 let block_number = 1;
848
849 let expected_block = provider
850 .get_block_by_number(block_number.into())
851 .full()
852 .await
853 .unwrap();
854 let block = instrumented_client
855 .block_by_number(block_number.into())
856 .await
857 .unwrap();
858
859 assert_eq!(expected_block, block);
860 }
861
862 #[tokio::test]
863 async fn test_transaction_count() {
864 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
865 let provider = get_provider(&http_endpoint);
866
867 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
868
869 let block = provider
870 .get_block(BlockId::latest())
871 .await
872 .unwrap()
873 .unwrap();
874
875 let block_hash = block.header.hash;
876 let tx_count = instrumented_client
877 .transaction_count(B256::from_slice(block_hash.as_slice()))
878 .await
879 .unwrap();
880 let expected_tx_count = block.transactions.len();
881
882 assert_eq!(tx_count, expected_tx_count as u64);
883 }
884
885 #[tokio::test]
891 async fn test_transaction_methods() {
892 let (container, rpc_url, _ws_endpoint) = start_anvil_container().await;
893 let instrumented_client = InstrumentedClient::new(&rpc_url).await.unwrap();
894
895 let private_key_hex =
897 "6b35c6d8110c888de06575b45181bf3f9e6c73451fa5cde812c95a6b31e66ddf".to_string();
898 let address = "009440d62dc85c73dbf889b7ad1f4da8b231d2ef";
899 set_account_balance(&container, address).await;
900
901 let to = address!("a0Ee7A142d267C1f36714E4a8F75612F20a79720");
903 let mut tx = TxLegacy {
904 to: Call(to),
905 value: U256::from(0),
906 gas_limit: 2_000_000,
907 nonce: 0,
908 gas_price: 21_000_000_000,
909 input: bytes!(),
910 chain_id: Some(31337),
911 };
912
913 let signer = private_key_hex.parse::<PrivateKeySigner>().unwrap();
914 let signature = signer.sign_transaction_sync(&mut tx).unwrap();
915 let signed_tx = tx.into_signed(signature);
916 let tx: TxEnvelope = TxEnvelope::from(signed_tx);
917
918 let tx_hash = instrumented_client.send_transaction(tx).await;
920 assert!(tx_hash.is_ok());
921 let tx_hash = B256::from_slice(tx_hash.unwrap().as_ref());
922
923 let tx_by_hash = instrumented_client.transaction_by_hash(tx_hash).await;
925 assert!(tx_by_hash.is_ok());
926
927 wait_transaction(&rpc_url, tx_hash).await.unwrap();
928
929 let receipt = instrumented_client.transaction_receipt(tx_hash).await;
931 assert!(receipt.is_ok());
932 let receipt = receipt.unwrap();
933
934 let tx_by_block = instrumented_client
936 .transaction_in_block(
937 receipt.block_hash.unwrap(),
938 receipt.transaction_index.unwrap(),
939 )
940 .await;
941 assert!(tx_by_block.is_ok());
942 }
943
944 #[tokio::test]
945 async fn test_estimate_gas() {
946 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
947 let provider = get_provider(&http_endpoint);
948
949 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
950 let accounts = provider.get_accounts().await.unwrap();
951 let from = accounts.first().unwrap();
952 let to = accounts.get(1).unwrap();
953
954 let tx = TxLegacy {
956 to: Call(*to),
957 value: U256::from(0),
958 gas_limit: 2_000_000,
959 nonce: 0,
960 gas_price: 1_000_000,
961 input: bytes!(),
962 chain_id: Some(31337),
963 };
964 let tx_request: TransactionRequest = tx.clone().into();
965 let tx_request = tx_request.from(*from);
966
967 let expected_estimated_gas = provider
968 .clone()
969 .estimate_gas(tx_request.clone())
970 .await
971 .unwrap();
972 let estimated_gas = instrumented_client.estimate_gas(tx_request).await.unwrap();
973 assert_eq!(expected_estimated_gas, estimated_gas);
974 }
975
976 #[tokio::test]
977 async fn test_call_contract_and_pending_call_contract() {
978 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
979 let provider = get_provider(&http_endpoint);
980
981 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
982
983 let anvil = provider.clone();
984 let accounts = anvil.get_accounts().await.unwrap();
985 let from = accounts.first().unwrap();
986 let to = accounts.get(1).unwrap();
987
988 let nonce = instrumented_client
989 .nonce_at(*from, BlockNumberOrTag::Latest)
990 .await
991 .unwrap();
992
993 let tx = TxLegacy {
995 to: Call(*to),
996 value: U256::from(0),
997 gas_limit: 1_000_000,
998 nonce,
999 gas_price: 21_000_000_000,
1000 input: bytes!(),
1001 chain_id: Some(31337),
1002 };
1003 let tx_request: TransactionRequest = tx.clone().into();
1004 let tx_request = tx_request.from(*from);
1005
1006 let expected_bytes = anvil.call(tx_request.clone()).await.unwrap();
1008 let bytes = instrumented_client
1009 .call_contract(tx_request.clone(), BlockNumberOrTag::Latest)
1010 .await
1011 .unwrap();
1012 assert_eq!(expected_bytes, bytes);
1013
1014 let bytes = instrumented_client.pending_call_contract(tx_request).await;
1016 assert!(bytes.is_ok());
1017 }
1018
1019 #[tokio::test]
1020 async fn test_filter_logs() {
1021 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1022 let provider = get_provider(&http_endpoint);
1023
1024 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1025 let address = provider.clone().get_accounts().await.unwrap()[0];
1026 let filter = Filter::new().address(address.to_string().parse::<Address>().unwrap());
1027
1028 let expected_logs = provider.clone().get_logs(&filter).await.unwrap();
1029 let logs = instrumented_client.filter_logs(filter).await.unwrap();
1030
1031 assert_eq!(expected_logs, logs);
1032 }
1033
1034 #[tokio::test]
1035 async fn test_storage_at() {
1036 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1037 let provider = get_provider(&http_endpoint);
1038
1039 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1040
1041 let account = provider.clone().get_accounts().await.unwrap()[0];
1042 let expected_storage = provider
1043 .clone()
1044 .get_storage_at(account, U256::ZERO)
1045 .await
1046 .unwrap();
1047
1048 let block_number = instrumented_client.block_number().await.unwrap();
1049 let storage = instrumented_client
1050 .storage_at(account, U256::ZERO, U256::from(block_number))
1051 .await
1052 .unwrap();
1053
1054 assert_eq!(expected_storage, storage);
1055 }
1056
1057 #[tokio::test]
1058 async fn test_block_number() {
1059 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1060 let provider = get_provider(&http_endpoint);
1061
1062 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1063
1064 let expected_block_number = provider.clone().get_block_number().await.unwrap();
1065 let block_number = instrumented_client.block_number().await.unwrap();
1066
1067 assert_eq!(expected_block_number, block_number);
1068 }
1069
1070 #[tokio::test]
1071 async fn test_code_at() {
1072 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1073 let provider = get_provider(&http_endpoint);
1074
1075 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1076 let address = provider.get_accounts().await.unwrap()[0];
1077
1078 let expected_code = provider.get_code_at(address).await.unwrap();
1079 let code = instrumented_client
1080 .code_at(address, BlockNumberOrTag::Latest)
1081 .await
1082 .unwrap();
1083
1084 assert_eq!(expected_code, code);
1085 }
1086
1087 #[tokio::test]
1088 async fn test_fee_history() {
1089 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1090 let provider = get_provider(&http_endpoint);
1091
1092 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1093 let block_count = 4;
1094 let last_block = BlockNumberOrTag::Latest;
1095 let reward_percentiles = [0.2, 0.3];
1096
1097 let expected_fee_history = provider
1098 .get_fee_history(block_count, last_block, &reward_percentiles)
1099 .await
1100 .unwrap();
1101 let fee_history = instrumented_client
1102 .fee_history(block_count, last_block, &reward_percentiles)
1103 .await
1104 .unwrap();
1105
1106 assert_eq!(expected_fee_history, fee_history);
1107 }
1108
1109 #[tokio::test]
1110 async fn test_header_by_hash() {
1111 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1112 let provider = get_provider(&http_endpoint);
1113
1114 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1115 let hash = provider
1116 .get_block(BlockId::latest())
1117 .await
1118 .unwrap()
1119 .unwrap()
1120 .header
1121 .hash;
1122 let expected_header = provider
1123 .get_block_by_hash(hash)
1124 .await
1125 .unwrap()
1126 .unwrap()
1127 .header;
1128 let header = instrumented_client.header_by_hash(hash).await.unwrap();
1129
1130 assert_eq!(expected_header, header);
1131 }
1132
1133 #[tokio::test]
1134 async fn test_header_by_number() {
1135 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1136 let provider = get_provider(&http_endpoint);
1137
1138 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1139 let block_number = BlockNumberOrTag::Earliest;
1140
1141 let header = instrumented_client
1142 .header_by_number(block_number)
1143 .await
1144 .unwrap();
1145
1146 let expected_header = provider
1147 .get_block_by_number(block_number)
1148 .await
1149 .unwrap()
1150 .unwrap()
1151 .header;
1152
1153 assert_eq!(expected_header, header);
1154 }
1155
1156 #[tokio::test]
1157 async fn test_nonce_at() {
1158 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1159 let provider = get_provider(&http_endpoint);
1160
1161 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1162 let address = provider.get_accounts().await.unwrap()[0];
1163
1164 let expected_nonce = provider.get_transaction_count(address).await.unwrap();
1165 let nonce = instrumented_client
1166 .nonce_at(address, BlockNumberOrTag::Latest)
1167 .await
1168 .unwrap();
1169
1170 assert_eq!(expected_nonce, nonce);
1171 }
1172
1173 #[tokio::test]
1174 async fn test_pending_balance_at() {
1175 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1176 let provider = get_provider(&http_endpoint);
1177
1178 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1179 let address = provider.get_accounts().await.unwrap()[0];
1180
1181 let expected_balance = provider.get_balance(address).await.unwrap();
1183 let balance = instrumented_client
1184 .pending_balance_at(address)
1185 .await
1186 .unwrap();
1187
1188 assert_eq!(expected_balance, balance);
1189 }
1190
1191 #[tokio::test]
1192 async fn test_pending_code_at() {
1193 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1194 let provider = get_provider(&http_endpoint);
1195
1196 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1197 let address = provider.get_accounts().await.unwrap()[0];
1198
1199 let expected_code = provider.get_code_at(address).await.unwrap();
1201 let code = instrumented_client.pending_code_at(address).await.unwrap();
1202
1203 assert_eq!(expected_code, code);
1204 }
1205
1206 #[tokio::test]
1207 async fn test_pending_nonce_at() {
1208 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1209 let provider = get_provider(&http_endpoint);
1210
1211 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1212 let address = provider.get_accounts().await.unwrap()[0];
1213
1214 let expected_pending_nonce_at = provider.get_transaction_count(address).await.unwrap();
1216 let pending_nonce_at = instrumented_client.pending_nonce_at(address).await.unwrap();
1217
1218 assert_eq!(expected_pending_nonce_at, pending_nonce_at);
1219 }
1220
1221 #[tokio::test]
1222 async fn test_pending_storage_at() {
1223 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1224 let provider = get_provider(&http_endpoint);
1225
1226 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1227 let address = provider.get_accounts().await.unwrap()[0];
1228 let key = U256::from(10);
1229
1230 let expected_pending_storage_at = provider.get_storage_at(address, key).await.unwrap();
1233 let pending_storage_at = instrumented_client
1234 .pending_storage_at(address, key)
1235 .await
1236 .unwrap();
1237
1238 assert_eq!(expected_pending_storage_at, pending_storage_at);
1239 }
1240
1241 #[tokio::test]
1242 async fn test_pending_transaction_count() {
1243 let (_container, http_endpoint, _ws_endpoint) = start_anvil_container().await;
1244 let provider = get_provider(&http_endpoint);
1245
1246 let instrumented_client = InstrumentedClient::new(&http_endpoint).await.unwrap();
1247
1248 let expected_transaction_count: u64 = provider
1249 .get_block_by_number(BlockNumberOrTag::Pending)
1250 .await
1251 .unwrap()
1252 .unwrap()
1253 .transactions
1254 .len() as u64;
1255
1256 let transaction_count = instrumented_client.pending_transaction_count().await;
1257
1258 assert_eq!(expected_transaction_count, transaction_count.unwrap());
1259 }
1260}