1#![allow(unknown_lints, mismatched_lifetime_syntaxes)]
4
5#[cfg(feature = "pubsub")]
6use super::get_block::SubFullBlocks;
7use super::{DynProvider, Empty, EthCallMany, MulticallBuilder, WatchBlocks};
8#[cfg(feature = "pubsub")]
9use crate::GetSubscription;
10use crate::{
11 heart::PendingTransactionError,
12 utils::{self, Eip1559Estimation, Eip1559Estimator},
13 EthCall, EthGetBlock, Identity, PendingTransaction, PendingTransactionBuilder,
14 PendingTransactionConfig, ProviderBuilder, ProviderCall, RootProvider, RpcWithBlock,
15 SendableTx,
16};
17use alloy_consensus::BlockHeader;
18use alloy_eips::eip2718::Encodable2718;
19use alloy_json_rpc::{RpcError, RpcRecv, RpcSend};
20use alloy_network::{Ethereum, Network};
21use alloy_network_primitives::{BlockResponse, ReceiptResponse};
22use alloy_primitives::{
23 hex, Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, B256, U128,
24 U256, U64,
25};
26use alloy_rpc_client::{ClientRef, NoParams, PollerBuilder, WeakClient};
27#[cfg(feature = "pubsub")]
28use alloy_rpc_types_eth::pubsub::{Params, SubscriptionKind};
29use alloy_rpc_types_eth::{
30 erc4337::TransactionConditional,
31 simulate::{SimulatePayload, SimulatedBlock},
32 AccessListResult, BlockId, BlockNumberOrTag, Bundle, EIP1186AccountProofResponse,
33 EthCallResponse, FeeHistory, Filter, FilterChanges, Index, Log, SyncStatus,
34};
35use alloy_transport::TransportResult;
36use serde_json::value::RawValue;
37use std::borrow::Cow;
38
39pub type FilterPollerBuilder<R> = PollerBuilder<(U256,), Vec<R>>;
43
44#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
71#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
72#[auto_impl::auto_impl(&, &mut, Rc, Arc, Box)]
73pub trait Provider<N: Network = Ethereum>: Send + Sync {
74 fn root(&self) -> &RootProvider<N>;
76
77 fn builder() -> ProviderBuilder<Identity, Identity, N>
79 where
80 Self: Sized,
81 {
82 ProviderBuilder::default()
83 }
84
85 #[inline]
89 fn client(&self) -> ClientRef<'_> {
90 self.root().client()
91 }
92
93 #[inline]
97 fn weak_client(&self) -> WeakClient {
98 self.root().weak_client()
99 }
100
101 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
114 #[doc(alias = "boxed")]
115 fn erased(self) -> DynProvider<N>
116 where
117 Self: Sized + 'static,
118 {
119 DynProvider::new(self)
120 }
121
122 fn get_accounts(&self) -> ProviderCall<NoParams, Vec<Address>> {
125 self.client().request_noparams("eth_accounts").into()
126 }
127
128 fn get_blob_base_fee(&self) -> ProviderCall<NoParams, U128, u128> {
130 self.client()
131 .request_noparams("eth_blobBaseFee")
132 .map_resp(utils::convert_u128 as fn(U128) -> u128)
133 .into()
134 }
135
136 fn get_block_number(&self) -> ProviderCall<NoParams, U64, BlockNumber> {
138 self.client()
139 .request_noparams("eth_blockNumber")
140 .map_resp(utils::convert_u64 as fn(U64) -> u64)
141 .into()
142 }
143
144 async fn get_block_number_by_id(
149 &self,
150 block_id: BlockId,
151 ) -> TransportResult<Option<BlockNumber>> {
152 match block_id {
153 BlockId::Number(BlockNumberOrTag::Number(num)) => Ok(Some(num)),
154 BlockId::Number(BlockNumberOrTag::Latest) => self.get_block_number().await.map(Some),
155 _ => {
156 let block = self.get_block(block_id).await?;
157 Ok(block.map(|b| b.header().number()))
158 }
159 }
160 }
161
162 #[doc(alias = "eth_call")]
190 #[doc(alias = "call_with_overrides")]
191 fn call(&self, tx: N::TransactionRequest) -> EthCall<N, Bytes> {
192 EthCall::call(self.weak_client(), tx).block(BlockNumberOrTag::Pending.into())
193 }
194
195 #[doc(alias = "eth_callMany")]
204 fn call_many<'req>(
205 &self,
206 bundles: &'req [Bundle],
207 ) -> EthCallMany<'req, N, Vec<Vec<EthCallResponse>>> {
208 EthCallMany::new(self.weak_client(), bundles)
209 }
210
211 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
217 fn multicall(&self) -> MulticallBuilder<Empty, &Self, N>
218 where
219 Self: Sized,
220 {
221 MulticallBuilder::new(self)
222 }
223
224 #[doc(alias = "eth_simulateV1")]
228 fn simulate<'req>(
229 &self,
230 payload: &'req SimulatePayload,
231 ) -> RpcWithBlock<&'req SimulatePayload, Vec<SimulatedBlock<N::BlockResponse>>> {
232 self.client().request("eth_simulateV1", payload).into()
233 }
234
235 fn get_chain_id(&self) -> ProviderCall<NoParams, U64, u64> {
237 self.client()
238 .request_noparams("eth_chainId")
239 .map_resp(utils::convert_u64 as fn(U64) -> u64)
240 .into()
241 }
242
243 fn create_access_list<'a>(
247 &self,
248 request: &'a N::TransactionRequest,
249 ) -> RpcWithBlock<&'a N::TransactionRequest, AccessListResult> {
250 self.client().request("eth_createAccessList", request).into()
251 }
252
253 fn estimate_gas(&self, tx: N::TransactionRequest) -> EthCall<N, U64, u64> {
267 EthCall::gas_estimate(self.weak_client(), tx)
268 .block(BlockNumberOrTag::Pending.into())
269 .map_resp(utils::convert_u64)
270 }
271
272 async fn estimate_eip1559_fees_with(
277 &self,
278 estimator: Eip1559Estimator,
279 ) -> TransportResult<Eip1559Estimation> {
280 let fee_history = self
281 .get_fee_history(
282 utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
283 BlockNumberOrTag::Latest,
284 &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
285 )
286 .await?;
287
288 let base_fee_per_gas = match fee_history.latest_block_base_fee() {
291 Some(base_fee) if base_fee != 0 => base_fee,
292 _ => {
293 self.get_block_by_number(BlockNumberOrTag::Latest)
295 .await?
296 .ok_or(RpcError::NullResp)?
297 .header()
298 .as_ref()
299 .base_fee_per_gas()
300 .ok_or(RpcError::UnsupportedFeature("eip1559"))?
301 .into()
302 }
303 };
304
305 Ok(estimator.estimate(base_fee_per_gas, &fee_history.reward.unwrap_or_default()))
306 }
307
308 async fn estimate_eip1559_fees(&self) -> TransportResult<Eip1559Estimation> {
312 self.estimate_eip1559_fees_with(Eip1559Estimator::default()).await
313 }
314
315 async fn get_fee_history(
319 &self,
320 block_count: u64,
321 last_block: BlockNumberOrTag,
322 reward_percentiles: &[f64],
323 ) -> TransportResult<FeeHistory> {
324 self.client()
325 .request("eth_feeHistory", (U64::from(block_count), last_block, reward_percentiles))
326 .await
327 }
328
329 fn get_gas_price(&self) -> ProviderCall<NoParams, U128, u128> {
331 self.client()
332 .request_noparams("eth_gasPrice")
333 .map_resp(utils::convert_u128 as fn(U128) -> u128)
334 .into()
335 }
336
337 fn get_account_info(
343 &self,
344 address: Address,
345 ) -> RpcWithBlock<Address, alloy_rpc_types_eth::AccountInfo> {
346 self.client().request("eth_getAccountInfo", address).into()
347 }
348
349 fn get_account(&self, address: Address) -> RpcWithBlock<Address, alloy_consensus::Account> {
352 self.client().request("eth_getAccount", address).into()
353 }
354
355 fn get_balance(&self, address: Address) -> RpcWithBlock<Address, U256, U256> {
359 self.client().request("eth_getBalance", address).into()
360 }
361
362 fn get_block(&self, block: BlockId) -> EthGetBlock<N::BlockResponse> {
373 match block {
374 BlockId::Hash(hash) => EthGetBlock::by_hash(hash.block_hash, self.client()),
375 BlockId::Number(number) => EthGetBlock::by_number(number, self.client()),
376 }
377 }
378
379 fn get_block_by_hash(&self, hash: BlockHash) -> EthGetBlock<N::BlockResponse> {
404 EthGetBlock::by_hash(hash, self.client())
405 }
406
407 fn get_block_by_number(&self, number: BlockNumberOrTag) -> EthGetBlock<N::BlockResponse> {
432 EthGetBlock::by_number(number, self.client())
433 }
434
435 async fn get_block_transaction_count_by_hash(
437 &self,
438 hash: BlockHash,
439 ) -> TransportResult<Option<u64>> {
440 self.client()
441 .request("eth_getBlockTransactionCountByHash", (hash,))
442 .await
443 .map(|opt_count: Option<U64>| opt_count.map(|count| count.to::<u64>()))
444 }
445
446 async fn get_block_transaction_count_by_number(
448 &self,
449 block_number: BlockNumberOrTag,
450 ) -> TransportResult<Option<u64>> {
451 self.client()
452 .request("eth_getBlockTransactionCountByNumber", (block_number,))
453 .await
454 .map(|opt_count: Option<U64>| opt_count.map(|count| count.to::<u64>()))
455 }
456
457 fn get_block_receipts(
459 &self,
460 block: BlockId,
461 ) -> ProviderCall<(BlockId,), Option<Vec<N::ReceiptResponse>>> {
462 self.client().request("eth_getBlockReceipts", (block,)).into()
463 }
464
465 fn get_code_at(&self, address: Address) -> RpcWithBlock<Address, Bytes> {
467 self.client().request("eth_getCode", address).into()
468 }
469
470 async fn watch_blocks(&self) -> TransportResult<FilterPollerBuilder<B256>> {
493 let id = self.new_block_filter().await?;
494 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
495 }
496
497 async fn watch_full_blocks(&self) -> TransportResult<WatchBlocks<N::BlockResponse>> {
521 let id = self.new_block_filter().await?;
522 let poller = PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,));
523
524 Ok(WatchBlocks::new(poller))
525 }
526
527 async fn watch_pending_transactions(&self) -> TransportResult<FilterPollerBuilder<B256>> {
550 let id = self.new_pending_transactions_filter(false).await?;
551 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
552 }
553
554 async fn watch_logs(&self, filter: &Filter) -> TransportResult<FilterPollerBuilder<Log>> {
583 let id = self.new_filter(filter).await?;
584 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
585 }
586
587 async fn watch_full_pending_transactions(
614 &self,
615 ) -> TransportResult<FilterPollerBuilder<N::TransactionResponse>> {
616 let id = self.new_pending_transactions_filter(true).await?;
617 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
618 }
619
620 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
625 async fn get_filter_changes<R: RpcRecv>(&self, id: U256) -> TransportResult<Vec<R>>
626 where
627 Self: Sized,
628 {
629 self.client().request("eth_getFilterChanges", (id,)).await
630 }
631
632 async fn get_filter_changes_dyn(&self, id: U256) -> TransportResult<FilterChanges> {
637 self.client().request("eth_getFilterChanges", (id,)).await
638 }
639
640 async fn get_filter_logs(&self, id: U256) -> TransportResult<Vec<Log>> {
642 self.client().request("eth_getFilterLogs", (id,)).await
643 }
644
645 async fn uninstall_filter(&self, id: U256) -> TransportResult<bool> {
647 self.client().request("eth_uninstallFilter", (id,)).await
648 }
649
650 #[inline]
655 async fn watch_pending_transaction(
656 &self,
657 config: PendingTransactionConfig,
658 ) -> Result<PendingTransaction, PendingTransactionError> {
659 self.root().watch_pending_transaction(config).await
660 }
661
662 async fn get_logs(&self, filter: &Filter) -> TransportResult<Vec<Log>> {
664 self.client().request("eth_getLogs", (filter,)).await
665 }
666
667 fn get_proof(
671 &self,
672 address: Address,
673 keys: Vec<StorageKey>,
674 ) -> RpcWithBlock<(Address, Vec<StorageKey>), EIP1186AccountProofResponse> {
675 self.client().request("eth_getProof", (address, keys)).into()
676 }
677
678 fn get_storage_at(
680 &self,
681 address: Address,
682 key: U256,
683 ) -> RpcWithBlock<(Address, U256), StorageValue> {
684 self.client().request("eth_getStorageAt", (address, key)).into()
685 }
686
687 fn get_transaction_by_sender_nonce(
691 &self,
692 sender: Address,
693 nonce: u64,
694 ) -> ProviderCall<(Address, U64), Option<N::TransactionResponse>> {
695 self.client()
696 .request("eth_getTransactionBySenderAndNonce", (sender, U64::from(nonce)))
697 .into()
698 }
699
700 fn get_transaction_by_hash(
702 &self,
703 hash: TxHash,
704 ) -> ProviderCall<(TxHash,), Option<N::TransactionResponse>> {
705 self.client().request("eth_getTransactionByHash", (hash,)).into()
706 }
707
708 fn get_transaction_by_block_hash_and_index(
710 &self,
711 block_hash: B256,
712 index: usize,
713 ) -> ProviderCall<(B256, Index), Option<N::TransactionResponse>> {
714 self.client()
715 .request("eth_getTransactionByBlockHashAndIndex", (block_hash, Index(index)))
716 .into()
717 }
718
719 fn get_raw_transaction_by_block_hash_and_index(
721 &self,
722 block_hash: B256,
723 index: usize,
724 ) -> ProviderCall<(B256, Index), Option<Bytes>> {
725 self.client()
726 .request("eth_getRawTransactionByBlockHashAndIndex", (block_hash, Index(index)))
727 .into()
728 }
729
730 fn get_transaction_by_block_number_and_index(
732 &self,
733 block_number: BlockNumberOrTag,
734 index: usize,
735 ) -> ProviderCall<(BlockNumberOrTag, Index), Option<N::TransactionResponse>> {
736 self.client()
737 .request("eth_getTransactionByBlockNumberAndIndex", (block_number, Index(index)))
738 .into()
739 }
740
741 fn get_raw_transaction_by_block_number_and_index(
743 &self,
744 block_number: BlockNumberOrTag,
745 index: usize,
746 ) -> ProviderCall<(BlockNumberOrTag, Index), Option<Bytes>> {
747 self.client()
748 .request("eth_getRawTransactionByBlockNumberAndIndex", (block_number, Index(index)))
749 .into()
750 }
751
752 fn get_raw_transaction_by_hash(&self, hash: TxHash) -> ProviderCall<(TxHash,), Option<Bytes>> {
761 self.client().request("eth_getRawTransactionByHash", (hash,)).into()
762 }
763
764 #[doc(alias = "get_nonce")]
766 #[doc(alias = "get_account_nonce")]
767 fn get_transaction_count(
768 &self,
769 address: Address,
770 ) -> RpcWithBlock<Address, U64, u64, fn(U64) -> u64> {
771 self.client()
772 .request("eth_getTransactionCount", address)
773 .map_resp(utils::convert_u64 as fn(U64) -> u64)
774 .into()
775 }
776
777 fn get_transaction_receipt(
779 &self,
780 hash: TxHash,
781 ) -> ProviderCall<(TxHash,), Option<N::ReceiptResponse>> {
782 self.client().request("eth_getTransactionReceipt", (hash,)).into()
783 }
784
785 async fn get_uncle(&self, tag: BlockId, idx: u64) -> TransportResult<Option<N::BlockResponse>> {
787 let idx = U64::from(idx);
788 match tag {
789 BlockId::Hash(hash) => {
790 self.client()
791 .request("eth_getUncleByBlockHashAndIndex", (hash.block_hash, idx))
792 .await
793 }
794 BlockId::Number(number) => {
795 self.client().request("eth_getUncleByBlockNumberAndIndex", (number, idx)).await
796 }
797 }
798 }
799
800 async fn get_uncle_count(&self, tag: BlockId) -> TransportResult<u64> {
802 match tag {
803 BlockId::Hash(hash) => self
804 .client()
805 .request("eth_getUncleCountByBlockHash", (hash.block_hash,))
806 .await
807 .map(|count: U64| count.to::<u64>()),
808 BlockId::Number(number) => self
809 .client()
810 .request("eth_getUncleCountByBlockNumber", (number,))
811 .await
812 .map(|count: U64| count.to::<u64>()),
813 }
814 }
815
816 fn get_max_priority_fee_per_gas(&self) -> ProviderCall<NoParams, U128, u128> {
818 self.client()
819 .request_noparams("eth_maxPriorityFeePerGas")
820 .map_resp(utils::convert_u128 as fn(U128) -> u128)
821 .into()
822 }
823
824 async fn new_block_filter(&self) -> TransportResult<U256> {
830 self.client().request_noparams("eth_newBlockFilter").await
831 }
832
833 async fn new_filter(&self, filter: &Filter) -> TransportResult<U256> {
839 self.client().request("eth_newFilter", (filter,)).await
840 }
841
842 async fn new_pending_transactions_filter(&self, full: bool) -> TransportResult<U256> {
852 let param = if full { &[true][..] } else { &[] };
854 self.client().request("eth_newPendingTransactionFilter", param).await
855 }
856
857 async fn send_raw_transaction(
861 &self,
862 encoded_tx: &[u8],
863 ) -> TransportResult<PendingTransactionBuilder<N>> {
864 let rlp_hex = hex::encode_prefixed(encoded_tx);
865 let tx_hash = self.client().request("eth_sendRawTransaction", (rlp_hex,)).await?;
866 Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
867 }
868
869 async fn send_raw_transaction_conditional(
880 &self,
881 encoded_tx: &[u8],
882 conditional: TransactionConditional,
883 ) -> TransportResult<PendingTransactionBuilder<N>> {
884 let rlp_hex = hex::encode_prefixed(encoded_tx);
885 let tx_hash = self
886 .client()
887 .request("eth_sendRawTransactionConditional", (rlp_hex, conditional))
888 .await?;
889 Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
890 }
891
892 async fn send_transaction(
913 &self,
914 tx: N::TransactionRequest,
915 ) -> TransportResult<PendingTransactionBuilder<N>> {
916 self.send_transaction_internal(SendableTx::Builder(tx)).await
917 }
918
919 async fn send_tx_envelope(
924 &self,
925 tx: N::TxEnvelope,
926 ) -> TransportResult<PendingTransactionBuilder<N>> {
927 self.send_transaction_internal(SendableTx::Envelope(tx)).await
928 }
929
930 #[doc(hidden)]
938 async fn send_transaction_internal(
939 &self,
940 tx: SendableTx<N>,
941 ) -> TransportResult<PendingTransactionBuilder<N>> {
942 let _handle = self.root().get_heart();
945
946 match tx {
947 SendableTx::Builder(mut tx) => {
948 alloy_network::TransactionBuilder::prep_for_submission(&mut tx);
949 let tx_hash = self.client().request("eth_sendTransaction", (tx,)).await?;
950 Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
951 }
952 SendableTx::Envelope(tx) => {
953 let encoded_tx = tx.encoded_2718();
954 self.send_raw_transaction(&encoded_tx).await
955 }
956 }
957 }
958
959 async fn sign_transaction(&self, tx: N::TransactionRequest) -> TransportResult<Bytes> {
964 self.client().request("eth_signTransaction", (tx,)).await
965 }
966
967 #[cfg(feature = "pubsub")]
993 fn subscribe_blocks(&self) -> GetSubscription<(SubscriptionKind,), N::HeaderResponse> {
994 let rpc_call = self.client().request("eth_subscribe", (SubscriptionKind::NewHeads,));
995 GetSubscription::new(self.weak_client(), rpc_call)
996 }
997
998 #[cfg(feature = "pubsub")]
1022 fn subscribe_full_blocks(&self) -> SubFullBlocks<N> {
1023 SubFullBlocks::new(self.subscribe_blocks(), self.weak_client())
1024 }
1025
1026 #[cfg(feature = "pubsub")]
1052 fn subscribe_pending_transactions(&self) -> GetSubscription<(SubscriptionKind,), B256> {
1053 let rpc_call =
1054 self.client().request("eth_subscribe", (SubscriptionKind::NewPendingTransactions,));
1055 GetSubscription::new(self.weak_client(), rpc_call)
1056 }
1057
1058 #[cfg(feature = "pubsub")]
1089 fn subscribe_full_pending_transactions(
1090 &self,
1091 ) -> GetSubscription<(SubscriptionKind, Params), N::TransactionResponse> {
1092 let rpc_call = self.client().request(
1093 "eth_subscribe",
1094 (SubscriptionKind::NewPendingTransactions, Params::Bool(true)),
1095 );
1096 GetSubscription::new(self.weak_client(), rpc_call)
1097 }
1098
1099 #[cfg(feature = "pubsub")]
1130 fn subscribe_logs(&self, filter: &Filter) -> GetSubscription<(SubscriptionKind, Params), Log> {
1131 let rpc_call = self.client().request(
1132 "eth_subscribe",
1133 (SubscriptionKind::Logs, Params::Logs(Box::new(filter.clone()))),
1134 );
1135 GetSubscription::new(self.weak_client(), rpc_call)
1136 }
1137
1138 #[cfg(feature = "pubsub")]
1140 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
1141 fn subscribe<P, R>(&self, params: P) -> GetSubscription<P, R>
1142 where
1143 P: RpcSend,
1144 R: RpcRecv,
1145 Self: Sized,
1146 {
1147 let rpc_call = self.client().request("eth_subscribe", params);
1148 GetSubscription::new(self.weak_client(), rpc_call)
1149 }
1150
1151 #[cfg(feature = "pubsub")]
1153 async fn unsubscribe(&self, id: B256) -> TransportResult<()> {
1154 self.root().unsubscribe(id)
1155 }
1156
1157 fn syncing(&self) -> ProviderCall<NoParams, SyncStatus> {
1159 self.client().request_noparams("eth_syncing").into()
1160 }
1161
1162 #[doc(alias = "web3_client_version")]
1164 fn get_client_version(&self) -> ProviderCall<NoParams, String> {
1165 self.client().request_noparams("web3_clientVersion").into()
1166 }
1167
1168 #[doc(alias = "web3_sha3")]
1170 fn get_sha3(&self, data: &[u8]) -> ProviderCall<(String,), B256> {
1171 self.client().request("web3_sha3", (hex::encode_prefixed(data),)).into()
1172 }
1173
1174 fn get_net_version(&self) -> ProviderCall<NoParams, U64, u64> {
1176 self.client()
1177 .request_noparams("net_version")
1178 .map_resp(utils::convert_u64 as fn(U64) -> u64)
1179 .into()
1180 }
1181
1182 async fn raw_request<P, R>(&self, method: Cow<'static, str>, params: P) -> TransportResult<R>
1207 where
1208 P: RpcSend,
1209 R: RpcRecv,
1210 Self: Sized,
1211 {
1212 self.client().request(method, ¶ms).await
1213 }
1214
1215 async fn raw_request_dyn(
1238 &self,
1239 method: Cow<'static, str>,
1240 params: &RawValue,
1241 ) -> TransportResult<Box<RawValue>> {
1242 self.client().request(method, params).await
1243 }
1244
1245 #[inline]
1247 fn transaction_request(&self) -> N::TransactionRequest {
1248 Default::default()
1249 }
1250}
1251
1252#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
1253#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
1254impl<N: Network> Provider<N> for RootProvider<N> {
1255 #[inline]
1256 fn root(&self) -> &Self {
1257 self
1258 }
1259
1260 #[inline]
1261 fn client(&self) -> ClientRef<'_> {
1262 self.inner.client_ref()
1263 }
1264
1265 #[inline]
1266 fn weak_client(&self) -> WeakClient {
1267 self.inner.weak_client()
1268 }
1269
1270 #[inline]
1271 async fn watch_pending_transaction(
1272 &self,
1273 config: PendingTransactionConfig,
1274 ) -> Result<PendingTransaction, PendingTransactionError> {
1275 let block_number =
1276 if let Some(receipt) = self.get_transaction_receipt(*config.tx_hash()).await? {
1277 if config.required_confirmations() <= 1 {
1279 return Ok(PendingTransaction::ready(*config.tx_hash()));
1280 }
1281 receipt.block_number()
1284 } else {
1285 None
1286 };
1287
1288 self.get_heart()
1289 .watch_tx(config, block_number)
1290 .await
1291 .map_err(|_| PendingTransactionError::FailedToRegister)
1292 }
1293}
1294
1295#[cfg(test)]
1296mod tests {
1297 use super::*;
1298 use crate::{builder, ext::test::async_ci_only, ProviderBuilder, WalletProvider};
1299 use alloy_consensus::{Transaction, TxEnvelope};
1300 use alloy_network::{AnyNetwork, EthereumWallet, TransactionBuilder};
1301 use alloy_node_bindings::{utils::run_with_tempdir, Anvil, Reth};
1302 use alloy_primitives::{address, b256, bytes, keccak256};
1303 use alloy_rlp::Decodable;
1304 use alloy_rpc_client::{BuiltInConnectionString, RpcClient};
1305 use alloy_rpc_types_eth::{request::TransactionRequest, Block};
1306 use alloy_signer_local::PrivateKeySigner;
1307 use alloy_transport::layers::{RetryBackoffLayer, RetryPolicy};
1308 use std::{io::Read, str::FromStr, time::Duration};
1309
1310 use alloy_consensus::transaction::SignerRecoverable;
1312 #[cfg(feature = "hyper")]
1313 use alloy_transport_http::{
1314 hyper,
1315 hyper::body::Bytes as HyperBytes,
1316 hyper_util::{
1317 client::legacy::{Client, Error},
1318 rt::TokioExecutor,
1319 },
1320 HyperResponse, HyperResponseFut,
1321 };
1322 #[cfg(feature = "hyper")]
1323 use http_body_util::Full;
1324 #[cfg(feature = "hyper")]
1325 use tower::{Layer, Service};
1326
1327 #[tokio::test]
1328 async fn test_provider_builder() {
1329 let provider =
1330 RootProvider::<Ethereum>::builder().with_recommended_fillers().connect_anvil();
1331 let num = provider.get_block_number().await.unwrap();
1332 assert_eq!(0, num);
1333 }
1334
1335 #[tokio::test]
1336 async fn test_builder_helper_fn() {
1337 let provider = builder::<Ethereum>().with_recommended_fillers().connect_anvil();
1338 let num = provider.get_block_number().await.unwrap();
1339 assert_eq!(0, num);
1340 }
1341
1342 #[cfg(feature = "hyper")]
1343 #[tokio::test]
1344 async fn test_default_hyper_transport() {
1345 let anvil = Anvil::new().spawn();
1346 let hyper_t = alloy_transport_http::HyperTransport::new_hyper(anvil.endpoint_url());
1347
1348 let rpc_client = alloy_rpc_client::RpcClient::new(hyper_t, true);
1349
1350 let provider = RootProvider::<Ethereum>::new(rpc_client);
1351 let num = provider.get_block_number().await.unwrap();
1352 assert_eq!(0, num);
1353 }
1354
1355 #[cfg(feature = "hyper")]
1356 #[tokio::test]
1357 async fn test_hyper_layer_transport() {
1358 struct LoggingLayer;
1359
1360 impl<S> Layer<S> for LoggingLayer {
1361 type Service = LoggingService<S>;
1362
1363 fn layer(&self, inner: S) -> Self::Service {
1364 LoggingService { inner }
1365 }
1366 }
1367
1368 #[derive(Clone)] struct LoggingService<S> {
1370 inner: S,
1371 }
1372
1373 impl<S, B> Service<hyper::Request<B>> for LoggingService<S>
1374 where
1375 S: Service<hyper::Request<B>, Response = HyperResponse, Error = Error>
1376 + Clone
1377 + Send
1378 + Sync
1379 + 'static,
1380 S::Future: Send,
1381 S::Error: std::error::Error + Send + Sync + 'static,
1382 B: From<Vec<u8>> + Send + 'static + Clone + Sync + std::fmt::Debug,
1383 {
1384 type Response = HyperResponse;
1385 type Error = Error;
1386 type Future = HyperResponseFut;
1387
1388 fn poll_ready(
1389 &mut self,
1390 cx: &mut std::task::Context<'_>,
1391 ) -> std::task::Poll<Result<(), Self::Error>> {
1392 self.inner.poll_ready(cx)
1393 }
1394
1395 fn call(&mut self, req: hyper::Request<B>) -> Self::Future {
1396 println!("Logging Layer - HyperRequest {req:?}");
1397
1398 let fut = self.inner.call(req);
1399
1400 Box::pin(fut)
1401 }
1402 }
1403 use http::header::{self, HeaderValue};
1404 use tower_http::{
1405 sensitive_headers::SetSensitiveRequestHeadersLayer, set_header::SetRequestHeaderLayer,
1406 };
1407 let anvil = Anvil::new().spawn();
1408 let hyper_client = Client::builder(TokioExecutor::new()).build_http::<Full<HyperBytes>>();
1409
1410 let service = tower::ServiceBuilder::new()
1412 .layer(SetRequestHeaderLayer::if_not_present(
1413 header::USER_AGENT,
1414 HeaderValue::from_static("alloy app"),
1415 ))
1416 .layer(SetRequestHeaderLayer::overriding(
1417 header::AUTHORIZATION,
1418 HeaderValue::from_static("some-jwt-token"),
1419 ))
1420 .layer(SetRequestHeaderLayer::appending(
1421 header::SET_COOKIE,
1422 HeaderValue::from_static("cookie-value"),
1423 ))
1424 .layer(SetSensitiveRequestHeadersLayer::new([header::AUTHORIZATION])) .layer(LoggingLayer)
1426 .service(hyper_client);
1427
1428 let layer_transport = alloy_transport_http::HyperClient::with_service(service);
1429
1430 let http_hyper =
1431 alloy_transport_http::Http::with_client(layer_transport, anvil.endpoint_url());
1432
1433 let rpc_client = alloy_rpc_client::RpcClient::new(http_hyper, true);
1434
1435 let provider = RootProvider::<Ethereum>::new(rpc_client);
1436 let num = provider.get_block_number().await.unwrap();
1437 assert_eq!(0, num);
1438
1439 let cloned_t = provider.client().transport().clone();
1441
1442 let rpc_client = alloy_rpc_client::RpcClient::new(cloned_t, true);
1443
1444 let provider = RootProvider::<Ethereum>::new(rpc_client);
1445 let num = provider.get_block_number().await.unwrap();
1446 assert_eq!(0, num);
1447 }
1448
1449 #[cfg(feature = "hyper")]
1450 #[tokio::test]
1451 #[cfg_attr(windows, ignore = "no reth on windows")]
1452 async fn test_auth_layer_transport() {
1453 crate::ext::test::async_ci_only(|| async move {
1454 use alloy_node_bindings::Reth;
1455 use alloy_rpc_types_engine::JwtSecret;
1456 use alloy_transport_http::{AuthLayer, AuthService, Http, HyperClient};
1457
1458 let secret = JwtSecret::random();
1459
1460 let reth =
1461 Reth::new().arg("--rpc.jwtsecret").arg(hex::encode(secret.as_bytes())).spawn();
1462
1463 let hyper_client =
1464 Client::builder(TokioExecutor::new()).build_http::<Full<HyperBytes>>();
1465
1466 let service =
1467 tower::ServiceBuilder::new().layer(AuthLayer::new(secret)).service(hyper_client);
1468
1469 let layer_transport: HyperClient<
1470 Full<HyperBytes>,
1471 AuthService<
1472 Client<
1473 alloy_transport_http::hyper_util::client::legacy::connect::HttpConnector,
1474 Full<HyperBytes>,
1475 >,
1476 >,
1477 > = HyperClient::with_service(service);
1478
1479 let http_hyper = Http::with_client(layer_transport, reth.endpoint_url());
1480
1481 let rpc_client = alloy_rpc_client::RpcClient::new(http_hyper, true);
1482
1483 let provider = RootProvider::<Ethereum>::new(rpc_client);
1484
1485 let num = provider.get_block_number().await.unwrap();
1486 assert_eq!(0, num);
1487 })
1488 .await;
1489 }
1490
1491 #[tokio::test]
1492 async fn test_builder_helper_fn_any_network() {
1493 let anvil = Anvil::new().spawn();
1494 let provider =
1495 builder::<AnyNetwork>().with_recommended_fillers().connect_http(anvil.endpoint_url());
1496 let num = provider.get_block_number().await.unwrap();
1497 assert_eq!(0, num);
1498 }
1499
1500 #[cfg(feature = "reqwest")]
1501 #[tokio::test]
1502 async fn object_safety() {
1503 let provider = ProviderBuilder::new().connect_anvil();
1504
1505 let refdyn = &provider as &dyn Provider<_>;
1506 let num = refdyn.get_block_number().await.unwrap();
1507 assert_eq!(0, num);
1508 }
1509
1510 #[cfg(feature = "ws")]
1511 #[tokio::test]
1512 async fn subscribe_blocks_http() {
1513 let provider = ProviderBuilder::new().connect_anvil_with_config(|a| a.block_time(1));
1514
1515 let err = provider.subscribe_blocks().await.unwrap_err();
1516 let alloy_json_rpc::RpcError::Transport(
1517 alloy_transport::TransportErrorKind::PubsubUnavailable,
1518 ) = err
1519 else {
1520 panic!("{err:?}");
1521 };
1522 }
1523
1524 #[cfg(feature = "ws")]
1526 #[tokio::test]
1527 async fn websocket_tls_setup() {
1528 for url in ["wss://mainnet.infura.io/ws/v3/b0f825787ba840af81e46c6a64d20754"] {
1529 let _ = ProviderBuilder::<_, _, Ethereum>::default().connect(url).await.unwrap();
1530 }
1531 }
1532
1533 #[cfg(feature = "ws")]
1534 #[tokio::test]
1535 async fn subscribe_blocks_ws() {
1536 use futures::stream::StreamExt;
1537
1538 let anvil = Anvil::new().block_time_f64(0.2).spawn();
1539 let ws = alloy_rpc_client::WsConnect::new(anvil.ws_endpoint());
1540 let client = alloy_rpc_client::RpcClient::connect_pubsub(ws).await.unwrap();
1541 let provider = RootProvider::<Ethereum>::new(client);
1542
1543 let sub = provider.subscribe_blocks().await.unwrap();
1544 let mut stream = sub.into_stream().take(5);
1545 let mut next = None;
1546 while let Some(header) = stream.next().await {
1547 if let Some(next) = &mut next {
1548 assert_eq!(header.number, *next);
1549 *next += 1;
1550 } else {
1551 next = Some(header.number + 1);
1552 }
1553 }
1554 }
1555
1556 #[cfg(feature = "ws")]
1557 #[tokio::test]
1558 async fn subscribe_full_blocks() {
1559 use futures::StreamExt;
1560
1561 let anvil = Anvil::new().block_time_f64(0.2).spawn();
1562 let ws = alloy_rpc_client::WsConnect::new(anvil.ws_endpoint());
1563 let client = alloy_rpc_client::RpcClient::connect_pubsub(ws).await.unwrap();
1564
1565 let provider = RootProvider::<Ethereum>::new(client);
1566
1567 let sub = provider.subscribe_full_blocks().hashes().channel_size(10);
1568
1569 let mut stream = sub.into_stream().await.unwrap().take(5);
1570
1571 let mut next = None;
1572 while let Some(Ok(block)) = stream.next().await {
1573 if let Some(next) = &mut next {
1574 assert_eq!(block.header().number, *next);
1575 *next += 1;
1576 } else {
1577 next = Some(block.header().number + 1);
1578 }
1579 }
1580 }
1581
1582 #[tokio::test]
1583 #[cfg(feature = "ws")]
1584 async fn subscribe_blocks_ws_remote() {
1585 use futures::stream::StreamExt;
1586
1587 let url = "wss://eth-mainnet.g.alchemy.com/v2/viFmeVzhg6bWKVMIWWS8MhmzREB-D4f7";
1588 let ws = alloy_rpc_client::WsConnect::new(url);
1589 let Ok(client) = alloy_rpc_client::RpcClient::connect_pubsub(ws).await else { return };
1590 let provider = RootProvider::<Ethereum>::new(client);
1591 let sub = provider.subscribe_blocks().await.unwrap();
1592 let mut stream = sub.into_stream().take(1);
1593 while let Some(header) = stream.next().await {
1594 println!("New block {header:?}");
1595 assert!(header.number > 0);
1596 }
1597 }
1598
1599 #[tokio::test]
1600 async fn test_custom_retry_policy() {
1601 #[derive(Debug, Clone)]
1602 struct CustomPolicy;
1603 impl RetryPolicy for CustomPolicy {
1604 fn should_retry(&self, _err: &alloy_transport::TransportError) -> bool {
1605 true
1606 }
1607
1608 fn backoff_hint(
1609 &self,
1610 _error: &alloy_transport::TransportError,
1611 ) -> Option<std::time::Duration> {
1612 None
1613 }
1614 }
1615
1616 let retry_layer = RetryBackoffLayer::new_with_policy(10, 100, 10000, CustomPolicy);
1617 let anvil = Anvil::new().spawn();
1618 let client = RpcClient::builder().layer(retry_layer).http(anvil.endpoint_url());
1619
1620 let provider = RootProvider::<Ethereum>::new(client);
1621 let num = provider.get_block_number().await.unwrap();
1622 assert_eq!(0, num);
1623 }
1624
1625 #[tokio::test]
1626 async fn test_send_tx() {
1627 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
1628 let tx = TransactionRequest {
1629 value: Some(U256::from(100)),
1630 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1631 gas_price: Some(20e9 as u128),
1632 gas: Some(21000),
1633 ..Default::default()
1634 };
1635
1636 let builder = provider.send_transaction(tx.clone()).await.expect("failed to send tx");
1637 let hash1 = *builder.tx_hash();
1638 let hash2 = builder.watch().await.expect("failed to await pending tx");
1639 assert_eq!(hash1, hash2);
1640
1641 let builder = provider.send_transaction(tx).await.expect("failed to send tx");
1642 let hash1 = *builder.tx_hash();
1643 let hash2 =
1644 builder.get_receipt().await.expect("failed to await pending tx").transaction_hash;
1645 assert_eq!(hash1, hash2);
1646 }
1647
1648 #[tokio::test]
1649 async fn test_watch_confirmed_tx() {
1650 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
1651 let tx = TransactionRequest {
1652 value: Some(U256::from(100)),
1653 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1654 gas_price: Some(20e9 as u128),
1655 gas: Some(21000),
1656 ..Default::default()
1657 };
1658
1659 let builder = provider.send_transaction(tx.clone()).await.expect("failed to send tx");
1660 let hash1 = *builder.tx_hash();
1661
1662 loop {
1664 if provider
1665 .get_transaction_receipt(hash1)
1666 .await
1667 .expect("failed to await pending tx")
1668 .is_some()
1669 {
1670 break;
1671 }
1672 }
1673
1674 let tx2 = TransactionRequest {
1676 value: Some(U256::from(100)),
1677 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1678 gas_price: Some(20e9 as u128),
1679 gas: Some(21000),
1680 ..Default::default()
1681 };
1682 provider.send_transaction(tx2).await.expect("failed to send tx").watch().await.unwrap();
1683
1684 let watch = builder.watch();
1686 let watch_with_timeout = tokio::time::timeout(Duration::from_secs(1), watch);
1688 let hash2 = watch_with_timeout
1689 .await
1690 .expect("Watching tx timed out")
1691 .expect("failed to await pending tx");
1692 assert_eq!(hash1, hash2);
1693 }
1694
1695 #[tokio::test]
1696 async fn gets_block_number() {
1697 let provider = ProviderBuilder::new().connect_anvil();
1698 let num = provider.get_block_number().await.unwrap();
1699 assert_eq!(0, num)
1700 }
1701
1702 #[tokio::test]
1703 async fn gets_block_number_for_id() {
1704 let provider = ProviderBuilder::new().connect_anvil();
1705
1706 let block_num = provider
1707 .get_block_number_by_id(BlockId::Number(BlockNumberOrTag::Number(0)))
1708 .await
1709 .unwrap();
1710 assert_eq!(block_num, Some(0));
1711
1712 let block_num = provider
1713 .get_block_number_by_id(BlockId::Number(BlockNumberOrTag::Latest))
1714 .await
1715 .unwrap();
1716 assert_eq!(block_num, Some(0));
1717
1718 let block =
1719 provider.get_block_by_number(BlockNumberOrTag::Number(0)).await.unwrap().unwrap();
1720 let hash = block.header.hash;
1721 let block_num = provider.get_block_number_by_id(BlockId::Hash(hash.into())).await.unwrap();
1722 assert_eq!(block_num, Some(0));
1723 }
1724
1725 #[tokio::test]
1726 async fn gets_block_number_with_raw_req() {
1727 let provider = ProviderBuilder::new().connect_anvil();
1728 let num: U64 =
1729 provider.raw_request("eth_blockNumber".into(), NoParams::default()).await.unwrap();
1730 assert_eq!(0, num.to::<u64>())
1731 }
1732
1733 #[cfg(feature = "anvil-api")]
1734 #[tokio::test]
1735 async fn gets_transaction_count() {
1736 let provider = ProviderBuilder::new().connect_anvil();
1737 let accounts = provider.get_accounts().await.unwrap();
1738 let sender = accounts[0];
1739
1740 let count = provider.get_transaction_count(sender).await.unwrap();
1742 assert_eq!(count, 0);
1743
1744 let tx = TransactionRequest {
1746 value: Some(U256::from(100)),
1747 from: Some(sender),
1748 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
1749 gas_price: Some(20e9 as u128),
1750 gas: Some(21000),
1751 ..Default::default()
1752 };
1753 let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await;
1754
1755 let count = provider.get_transaction_count(sender).await.unwrap();
1757 assert_eq!(count, 1);
1758
1759 let count = provider.get_transaction_count(sender).block_id(0.into()).await.unwrap();
1761 assert_eq!(count, 0);
1762 }
1763
1764 #[tokio::test]
1765 async fn gets_block_by_hash() {
1766 let provider = ProviderBuilder::new().connect_anvil();
1767 let num = 0;
1768 let tag: BlockNumberOrTag = num.into();
1769 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
1770 let hash = block.header.hash;
1771 let block = provider.get_block_by_hash(hash).full().await.unwrap().unwrap();
1772 assert_eq!(block.header.hash, hash);
1773 }
1774
1775 #[tokio::test]
1776 async fn gets_block_by_hash_with_raw_req() {
1777 let provider = ProviderBuilder::new().connect_anvil();
1778 let num = 0;
1779 let tag: BlockNumberOrTag = num.into();
1780 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
1781 let hash = block.header.hash;
1782 let block: Block = provider
1783 .raw_request::<(B256, bool), Block>("eth_getBlockByHash".into(), (hash, true))
1784 .await
1785 .unwrap();
1786 assert_eq!(block.header.hash, hash);
1787 }
1788
1789 #[tokio::test]
1790 async fn gets_block_by_number_full() {
1791 let provider = ProviderBuilder::new().connect_anvil();
1792 let num = 0;
1793 let tag: BlockNumberOrTag = num.into();
1794 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
1795 assert_eq!(block.header.number, num);
1796 }
1797
1798 #[tokio::test]
1799 async fn gets_block_by_number() {
1800 let provider = ProviderBuilder::new().connect_anvil();
1801 let num = 0;
1802 let tag: BlockNumberOrTag = num.into();
1803 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
1804 assert_eq!(block.header.number, num);
1805 }
1806
1807 #[tokio::test]
1808 async fn gets_client_version() {
1809 let provider = ProviderBuilder::new().connect_anvil();
1810 let version = provider.get_client_version().await.unwrap();
1811 assert!(version.contains("anvil"), "{version}");
1812 }
1813
1814 #[tokio::test]
1815 async fn gets_sha3() {
1816 let provider = ProviderBuilder::new().connect_anvil();
1817 let data = b"alloy";
1818 let hash = provider.get_sha3(data).await.unwrap();
1819 assert_eq!(hash, keccak256(data));
1820 }
1821
1822 #[tokio::test]
1823 async fn gets_chain_id() {
1824 let dev_chain_id: u64 = 13371337;
1825
1826 let provider =
1827 ProviderBuilder::new().connect_anvil_with_config(|a| a.chain_id(dev_chain_id));
1828
1829 let chain_id = provider.get_chain_id().await.unwrap();
1830 assert_eq!(chain_id, dev_chain_id);
1831 }
1832
1833 #[tokio::test]
1834 async fn gets_network_id() {
1835 let dev_chain_id: u64 = 13371337;
1836 let provider =
1837 ProviderBuilder::new().connect_anvil_with_config(|a| a.chain_id(dev_chain_id));
1838
1839 let chain_id = provider.get_net_version().await.unwrap();
1840 assert_eq!(chain_id, dev_chain_id);
1841 }
1842
1843 #[tokio::test]
1844 async fn gets_storage_at() {
1845 let provider = ProviderBuilder::new().connect_anvil();
1846 let addr = Address::with_last_byte(16);
1847 let storage = provider.get_storage_at(addr, U256::ZERO).await.unwrap();
1848 assert_eq!(storage, U256::ZERO);
1849 }
1850
1851 #[tokio::test]
1852 async fn gets_transaction_by_hash_not_found() {
1853 let provider = ProviderBuilder::new().connect_anvil();
1854 let tx_hash = b256!("5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95");
1855 let tx = provider.get_transaction_by_hash(tx_hash).await.expect("failed to fetch tx");
1856
1857 assert!(tx.is_none());
1858 }
1859
1860 #[tokio::test]
1861 async fn gets_transaction_by_hash() {
1862 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
1863
1864 let req = TransactionRequest::default()
1865 .from(provider.default_signer_address())
1866 .to(Address::repeat_byte(5))
1867 .value(U256::ZERO)
1868 .input(bytes!("deadbeef").into());
1869
1870 let tx_hash = *provider.send_transaction(req).await.expect("failed to send tx").tx_hash();
1871
1872 let tx = provider
1873 .get_transaction_by_hash(tx_hash)
1874 .await
1875 .expect("failed to fetch tx")
1876 .expect("tx not included");
1877 assert_eq!(tx.input(), &bytes!("deadbeef"));
1878 }
1879
1880 #[tokio::test]
1881 #[ignore]
1882 async fn gets_logs() {
1883 let provider = ProviderBuilder::new().connect_anvil();
1884 let filter = Filter::new()
1885 .at_block_hash(b256!(
1886 "b20e6f35d4b46b3c4cd72152faec7143da851a0dc281d390bdd50f58bfbdb5d3"
1887 ))
1888 .event_signature(b256!(
1889 "e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"
1890 ));
1891 let logs = provider.get_logs(&filter).await.unwrap();
1892 assert_eq!(logs.len(), 1);
1893 }
1894
1895 #[tokio::test]
1896 #[ignore]
1897 async fn gets_tx_receipt() {
1898 let provider = ProviderBuilder::new().connect_anvil();
1899 let receipt = provider
1900 .get_transaction_receipt(b256!(
1901 "5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95"
1902 ))
1903 .await
1904 .unwrap();
1905 assert!(receipt.is_some());
1906 let receipt = receipt.unwrap();
1907 assert_eq!(
1908 receipt.transaction_hash,
1909 b256!("5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95")
1910 );
1911 }
1912
1913 #[tokio::test]
1914 async fn gets_max_priority_fee_per_gas() {
1915 let provider = ProviderBuilder::new().connect_anvil();
1916 let _fee = provider.get_max_priority_fee_per_gas().await.unwrap();
1917 }
1918
1919 #[tokio::test]
1920 async fn gets_fee_history() {
1921 let provider = ProviderBuilder::new().connect_anvil();
1922 let block_number = provider.get_block_number().await.unwrap();
1923 let fee_history = provider
1924 .get_fee_history(
1925 utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
1926 BlockNumberOrTag::Number(block_number),
1927 &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
1928 )
1929 .await
1930 .unwrap();
1931 assert_eq!(fee_history.oldest_block, 0_u64);
1932 }
1933
1934 #[tokio::test]
1935 async fn gets_block_transaction_count_by_hash() {
1936 let provider = ProviderBuilder::new().connect_anvil();
1937 let block = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
1938 let hash = block.header.hash;
1939 let tx_count = provider.get_block_transaction_count_by_hash(hash).await.unwrap();
1940 assert!(tx_count.is_some());
1941 }
1942
1943 #[tokio::test]
1944 async fn gets_block_transaction_count_by_number() {
1945 let provider = ProviderBuilder::new().connect_anvil();
1946 let tx_count =
1947 provider.get_block_transaction_count_by_number(BlockNumberOrTag::Latest).await.unwrap();
1948 assert!(tx_count.is_some());
1949 }
1950
1951 #[tokio::test]
1952 async fn gets_block_receipts() {
1953 let provider = ProviderBuilder::new().connect_anvil();
1954 let receipts =
1955 provider.get_block_receipts(BlockId::Number(BlockNumberOrTag::Latest)).await.unwrap();
1956 assert!(receipts.is_some());
1957 }
1958
1959 #[tokio::test]
1960 async fn sends_raw_transaction() {
1961 let provider = ProviderBuilder::new().connect_anvil();
1962 let pending = provider
1963 .send_raw_transaction(
1964 bytes!("f865808477359400825208940000000000000000000000000000000000000000018082f4f5a00505e227c1c636c76fac55795db1a40a4d24840d81b40d2fe0cc85767f6bd202a01e91b437099a8a90234ac5af3cb7ca4fb1432e133f75f9a91678eaf5f487c74b").as_ref()
1966 )
1967 .await.unwrap();
1968 assert_eq!(
1969 pending.tx_hash().to_string(),
1970 "0x9dae5cf33694a02e8a7d5de3fe31e9d05ca0ba6e9180efac4ab20a06c9e598a3"
1971 );
1972 }
1973
1974 #[tokio::test]
1975 async fn connect_boxed() {
1976 let anvil = Anvil::new().spawn();
1977
1978 let provider = RootProvider::<Ethereum>::connect(anvil.endpoint().as_str()).await;
1979
1980 match provider {
1981 Ok(provider) => {
1982 let num = provider.get_block_number().await.unwrap();
1983 assert_eq!(0, num);
1984 }
1985 Err(e) => {
1986 assert_eq!(
1987 format!("{e}"),
1988 "hyper not supported by BuiltinConnectionString. Please instantiate a hyper client manually"
1989 );
1990 }
1991 }
1992 }
1993
1994 #[tokio::test]
1995 async fn any_network_wallet_filler() {
1996 use alloy_serde::WithOtherFields;
1997 let anvil = Anvil::new().spawn();
1998 let signer: PrivateKeySigner =
1999 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
2000 let wallet = EthereumWallet::from(signer);
2001
2002 let provider = ProviderBuilder::new()
2003 .network::<AnyNetwork>()
2004 .wallet(wallet)
2005 .connect_http(anvil.endpoint_url());
2006
2007 let tx = TransactionRequest::default()
2008 .with_to(address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"))
2009 .value(U256::from(325235));
2010
2011 let tx = WithOtherFields::new(tx);
2012
2013 let builder = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
2014
2015 assert!(builder.status());
2016 }
2017
2018 #[tokio::test]
2019 async fn builtin_connect_boxed() {
2020 let anvil = Anvil::new().spawn();
2021
2022 let conn: BuiltInConnectionString = anvil.endpoint().parse().unwrap();
2023
2024 let transport = conn.connect_boxed().await.unwrap();
2025
2026 let client = alloy_rpc_client::RpcClient::new(transport, true);
2027
2028 let provider = RootProvider::<Ethereum>::new(client);
2029
2030 let num = provider.get_block_number().await.unwrap();
2031 assert_eq!(0, num);
2032 }
2033
2034 #[tokio::test]
2035 async fn test_uncle_count() {
2036 let provider = ProviderBuilder::new().connect_anvil();
2037
2038 let count = provider.get_uncle_count(0.into()).await.unwrap();
2039 assert_eq!(count, 0);
2040 }
2041
2042 #[tokio::test]
2043 #[cfg(any(
2044 feature = "reqwest-default-tls",
2045 feature = "reqwest-rustls-tls",
2046 feature = "reqwest-native-tls",
2047 ))]
2048 #[ignore = "ignore until <https://github.com/paradigmxyz/reth/pull/14727> is in"]
2049 async fn call_mainnet() {
2050 use alloy_network::TransactionBuilder;
2051 use alloy_sol_types::SolValue;
2052
2053 let url = "https://docs-demo.quiknode.pro/";
2054 let provider = ProviderBuilder::new().connect_http(url.parse().unwrap());
2055 let req = TransactionRequest::default()
2056 .with_to(address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")) .with_input(bytes!("06fdde03")); let result = provider.call(req.clone()).await.unwrap();
2059 assert_eq!(String::abi_decode(&result).unwrap(), "Wrapped Ether");
2060
2061 let result = provider.call(req).block(0.into()).await.unwrap();
2062 assert_eq!(result.to_string(), "0x");
2063 }
2064
2065 #[tokio::test]
2066 async fn call_many_mainnet() {
2067 use alloy_rpc_types_eth::{BlockOverrides, StateContext};
2068
2069 let url = "https://docs-demo.quiknode.pro/";
2070 let provider = ProviderBuilder::new().connect_http(url.parse().unwrap());
2071 let tx1 = TransactionRequest::default()
2072 .with_to(address!("6b175474e89094c44da98b954eedeac495271d0f"))
2073 .with_gas_limit(1000000)
2074 .with_gas_price(2023155498)
2075 .with_input(hex!("a9059cbb000000000000000000000000bc0E63965946815d105E7591407704e6e1964E590000000000000000000000000000000000000000000000000000000005f5e100"));
2076 let tx2 = TransactionRequest::default()
2077 .with_to(address!("833589fcd6edb6e08f4c7c32d4f71b54bda02913"))
2078 .with_gas_price(2023155498)
2079 .with_input(hex!(
2080 "70a08231000000000000000000000000bc0E63965946815d105E7591407704e6e1964E59"
2081 ));
2082
2083 let transactions = vec![tx1.clone(), tx2.clone()];
2084
2085 let block_override =
2086 BlockOverrides { number: Some(U256::from(12279785)), ..Default::default() };
2087
2088 let bundles = vec![Bundle { transactions, block_override: Some(block_override.clone()) }];
2089
2090 let context = StateContext {
2091 block_number: Some(BlockId::number(12279785)),
2092 transaction_index: Some(1.into()),
2093 };
2094
2095 let results = provider.call_many(&bundles).context(&context).await.unwrap();
2096
2097 let tx1_res = EthCallResponse {
2098 value: Some(
2099 hex!("0000000000000000000000000000000000000000000000000000000000000001").into(),
2100 ),
2101 error: None,
2102 };
2103 let tx2_res = EthCallResponse { value: Some(Bytes::new()), error: None };
2104 let expected = vec![vec![tx1_res.clone(), tx2_res.clone()]];
2105
2106 assert_eq!(results, expected);
2107
2108 let bundles = vec![
2110 Bundle {
2111 transactions: vec![tx1.clone()],
2112 block_override: Some(block_override.clone()),
2113 },
2114 Bundle {
2115 transactions: vec![tx2.clone()],
2116 block_override: Some(block_override.clone()),
2117 },
2118 ];
2119
2120 let results = provider.call_many(&bundles).context(&context).await.unwrap();
2121 let expected = vec![vec![tx1_res.clone()], vec![tx2_res.clone()]];
2122 assert_eq!(results, expected);
2123
2124 let b1 =
2126 vec![Bundle { transactions: vec![tx1], block_override: Some(block_override.clone()) }];
2127 let b2 = vec![Bundle { transactions: vec![tx2], block_override: Some(block_override) }];
2128
2129 let results = provider.call_many(&b1).context(&context).extend_bundles(&b2).await.unwrap();
2130 assert_eq!(results, expected);
2131 }
2132
2133 #[tokio::test]
2134 #[cfg(feature = "hyper-tls")]
2135 async fn hyper_https() {
2136 let url = "https://reth-ethereum.ithaca.xyz/rpc";
2137
2138 let provider = ProviderBuilder::new().connect(url).await.unwrap();
2141
2142 let _num = provider.get_block_number().await.unwrap();
2143 }
2144
2145 #[tokio::test]
2146 async fn test_empty_transactions() {
2147 let provider = ProviderBuilder::new().connect_anvil();
2148
2149 let block = provider.get_block_by_number(0.into()).await.unwrap().unwrap();
2150 assert!(block.transactions.is_hashes());
2151 }
2152
2153 #[tokio::test]
2154 async fn disable_test() {
2155 let provider = ProviderBuilder::new()
2156 .disable_recommended_fillers()
2157 .with_cached_nonce_management()
2158 .connect_anvil();
2159
2160 let tx = TransactionRequest::default()
2161 .with_kind(alloy_primitives::TxKind::Create)
2162 .value(U256::from(1235))
2163 .with_input(Bytes::from_str("ffffffffffffff").unwrap());
2164
2165 let err = provider.send_transaction(tx).await.unwrap_err().to_string();
2166 assert!(err.contains("missing properties: [(\"NonceManager\", [\"from\"])]"));
2167 }
2168
2169 #[tokio::test]
2170 async fn capture_anvil_logs() {
2171 let mut anvil = Anvil::new().keep_stdout().spawn();
2172
2173 let provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
2174
2175 let tx = TransactionRequest::default()
2176 .with_from(address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"))
2177 .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2178 .value(U256::from(100));
2179
2180 let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
2181
2182 anvil.child_mut().kill().unwrap();
2183
2184 let mut output = String::new();
2185 anvil.child_mut().stdout.take().unwrap().read_to_string(&mut output).unwrap();
2186
2187 assert_eq!(anvil.chain_id(), 31337);
2188 assert_eq!(anvil.addresses().len(), 10);
2189 assert_eq!(anvil.keys().len(), 10);
2190
2191 assert!(output.contains("eth_sendTransaction"));
2192 assert!(output.contains("Block Number: 1"))
2193 }
2194
2195 #[tokio::test]
2196 async fn custom_estimator() {
2197 let provider = ProviderBuilder::new()
2198 .disable_recommended_fillers()
2199 .with_cached_nonce_management()
2200 .connect_anvil();
2201
2202 let _ = provider
2203 .estimate_eip1559_fees_with(Eip1559Estimator::new(|_fee, _rewards| Eip1559Estimation {
2204 max_fee_per_gas: 0,
2205 max_priority_fee_per_gas: 0,
2206 }))
2207 .await;
2208 }
2209
2210 #[tokio::test]
2211 #[cfg(not(windows))]
2212 async fn eth_sign_transaction() {
2213 async_ci_only(|| async {
2214 run_with_tempdir("reth-sign-tx", |dir| async {
2215 let reth = Reth::new().dev().disable_discovery().data_dir(dir).spawn();
2216 let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
2217
2218 let accounts = provider.get_accounts().await.unwrap();
2219 let from = accounts[0];
2220
2221 let tx = TransactionRequest::default()
2222 .from(from)
2223 .to(Address::random())
2224 .value(U256::from(100))
2225 .gas_limit(21000);
2226
2227 let signed_tx = provider.sign_transaction(tx).await.unwrap().to_vec();
2228
2229 let tx = TxEnvelope::decode(&mut signed_tx.as_slice()).unwrap();
2230
2231 let signer = tx.recover_signer().unwrap();
2232
2233 assert_eq!(signer, from);
2234 })
2235 .await
2236 })
2237 .await;
2238 }
2239
2240 #[cfg(feature = "throttle")]
2241 use alloy_transport::layers::ThrottleLayer;
2242
2243 #[cfg(feature = "throttle")]
2244 #[tokio::test]
2245 async fn test_throttled_provider() {
2246 let request_per_second = 10;
2247 let throttle_layer = ThrottleLayer::new(request_per_second);
2248
2249 let anvil = Anvil::new().spawn();
2250 let client = RpcClient::builder().layer(throttle_layer).http(anvil.endpoint_url());
2251 let provider = RootProvider::<Ethereum>::new(client);
2252
2253 let num_requests = 10;
2254 let start = std::time::Instant::now();
2255 for _ in 0..num_requests {
2256 provider.get_block_number().await.unwrap();
2257 }
2258
2259 let elapsed = start.elapsed();
2260 assert_eq!(elapsed.as_secs_f64().round() as u32, 1);
2261 }
2262
2263 #[tokio::test]
2264 #[cfg(feature = "hyper")]
2265 async fn test_connect_hyper_tls() {
2266 let p =
2267 ProviderBuilder::new().connect("https://reth-ethereum.ithaca.xyz/rpc").await.unwrap();
2268
2269 let _num = p.get_block_number().await.unwrap();
2270
2271 let anvil = Anvil::new().spawn();
2272 let p = ProviderBuilder::new().connect(&anvil.endpoint()).await.unwrap();
2273
2274 let _num = p.get_block_number().await.unwrap();
2275 }
2276}