1#![allow(unknown_lints, mismatched_lifetime_syntaxes)]
4
5#[cfg(feature = "pubsub")]
6use super::get_block::SubFullBlocks;
7use super::{
8 DynProvider, Empty, EthCallMany, MulticallBuilder, WatchBlocks, WatchBlocksFrom,
9 WatchCanonicalBlocksFrom, WatchCanonicalLogsFrom, WatchHeaders, WatchLogsFrom,
10};
11#[cfg(feature = "pubsub")]
12use crate::GetSubscription;
13use crate::{
14 heart::PendingTransactionError,
15 utils::{self, Eip1559Estimation, Eip1559Estimator},
16 EthCall, EthGetBlock, Identity, PendingTransaction, PendingTransactionBuilder,
17 PendingTransactionConfig, ProviderBuilder, ProviderCall, RootProvider, RpcWithBlock,
18 SendableTx,
19};
20use alloy_consensus::BlockHeader;
21use alloy_eips::{eip2718::Encodable2718, eip7928::BlockAccessList};
22use alloy_json_rpc::{RpcError, RpcRecv, RpcSend};
23use alloy_network::{Ethereum, Network};
24use alloy_network_primitives::{BlockResponse, ReceiptResponse};
25use alloy_primitives::{
26 hex, Address, BlockHash, BlockNumber, Bytes, StorageKey, StorageValue, TxHash, B256, U128,
27 U256, U64,
28};
29use alloy_rpc_client::{ClientRef, NoParams, PollerBuilder, WeakClient};
30#[cfg(feature = "pubsub")]
31use alloy_rpc_types_eth::pubsub::{Params, SubscriptionKind};
32use alloy_rpc_types_eth::{
33 erc4337::TransactionConditional,
34 simulate::{SimulatePayload, SimulatedBlock},
35 AccessListResult, BlockId, BlockNumberOrTag, Bundle, EIP1186AccountProofResponse,
36 EthCallResponse, FeeHistory, FillTransaction, Filter, FilterChanges, Index, Log,
37 StorageValuesRequest, StorageValuesResponse, SyncStatus,
38};
39use alloy_transport::TransportResult;
40use serde_json::value::RawValue;
41use std::borrow::Cow;
42
43pub type FilterPollerBuilder<R> = PollerBuilder<(U256,), Vec<R>>;
47
48#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
75#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
76#[auto_impl::auto_impl(&, &mut, Rc, Arc, Box)]
77pub trait Provider<N: Network = Ethereum>: Send + Sync {
78 fn root(&self) -> &RootProvider<N>;
80
81 fn builder() -> ProviderBuilder<Identity, Identity, N>
83 where
84 Self: Sized,
85 {
86 ProviderBuilder::default()
87 }
88
89 #[inline]
93 fn client(&self) -> ClientRef<'_> {
94 self.root().client()
95 }
96
97 #[inline]
101 fn weak_client(&self) -> WeakClient {
102 self.root().weak_client()
103 }
104
105 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
118 #[doc(alias = "boxed")]
119 fn erased(self) -> DynProvider<N>
120 where
121 Self: Sized + 'static,
122 {
123 DynProvider::new(self)
124 }
125
126 fn get_accounts(&self) -> ProviderCall<NoParams, Vec<Address>> {
129 self.client().request_noparams("eth_accounts").into()
130 }
131
132 fn get_blob_base_fee(&self) -> ProviderCall<NoParams, U128, u128> {
134 self.client()
135 .request_noparams("eth_blobBaseFee")
136 .map_resp(utils::convert_u128 as fn(U128) -> u128)
137 .into()
138 }
139
140 fn get_block_number(&self) -> ProviderCall<NoParams, U64, BlockNumber> {
142 self.client()
143 .request_noparams("eth_blockNumber")
144 .map_resp(utils::convert_u64 as fn(U64) -> u64)
145 .into()
146 }
147
148 async fn get_block_number_by_id(
153 &self,
154 block_id: BlockId,
155 ) -> TransportResult<Option<BlockNumber>> {
156 match block_id {
157 BlockId::Number(BlockNumberOrTag::Number(num)) => Ok(Some(num)),
158 BlockId::Number(BlockNumberOrTag::Latest) => self.get_block_number().await.map(Some),
159 _ => Ok(self.get_header(block_id).await?.map(|h| h.number())),
160 }
161 }
162
163 #[doc(alias = "eth_call")]
191 #[doc(alias = "call_with_overrides")]
192 fn call(&self, tx: N::TransactionRequest) -> EthCall<N, Bytes> {
193 EthCall::call(self.weak_client(), tx).block(BlockNumberOrTag::Pending.into())
194 }
195
196 #[doc(alias = "eth_callMany")]
205 fn call_many<'req>(
206 &self,
207 bundles: &'req [Bundle],
208 ) -> EthCallMany<'req, N, Vec<Vec<EthCallResponse>>> {
209 EthCallMany::new(self.weak_client(), bundles)
210 }
211
212 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
218 fn multicall(&self) -> MulticallBuilder<Empty, &Self, N>
219 where
220 Self: Sized,
221 {
222 MulticallBuilder::new(self)
223 }
224
225 #[doc(alias = "eth_simulateV1")]
229 fn simulate<'req>(
230 &self,
231 payload: &'req SimulatePayload,
232 ) -> RpcWithBlock<&'req SimulatePayload, Vec<SimulatedBlock<N::BlockResponse>>> {
233 self.client().request("eth_simulateV1", payload).into()
234 }
235
236 fn get_chain_id(&self) -> ProviderCall<NoParams, U64, u64> {
238 self.client()
239 .request_noparams("eth_chainId")
240 .map_resp(utils::convert_u64 as fn(U64) -> u64)
241 .into()
242 }
243
244 fn create_access_list<'a>(
248 &self,
249 request: &'a N::TransactionRequest,
250 ) -> RpcWithBlock<&'a N::TransactionRequest, AccessListResult> {
251 self.client().request("eth_createAccessList", request).into()
252 }
253
254 fn estimate_gas(&self, tx: N::TransactionRequest) -> EthCall<N, U64, u64> {
268 EthCall::gas_estimate(self.weak_client(), tx)
269 .block(BlockNumberOrTag::Pending.into())
270 .map_resp(utils::convert_u64)
271 }
272
273 async fn estimate_eip1559_fees_with(
280 &self,
281 estimator: Eip1559Estimator,
282 ) -> TransportResult<Eip1559Estimation> {
283 let fee_history = self
284 .get_fee_history(
285 utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
286 BlockNumberOrTag::Latest,
287 &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
288 )
289 .await?;
290
291 let base_fee_per_gas = match fee_history.latest_block_base_fee() {
294 Some(base_fee) if base_fee != 0 => base_fee,
295 _ => {
296 self.get_block_by_number(BlockNumberOrTag::Latest)
298 .await?
299 .ok_or(RpcError::NullResp)?
300 .header()
301 .as_ref()
302 .base_fee_per_gas()
303 .ok_or(RpcError::UnsupportedFeature("eip1559"))?
304 .into()
305 }
306 };
307
308 Ok(estimator.estimate(base_fee_per_gas, &fee_history.reward.unwrap_or_default()))
309 }
310
311 async fn estimate_eip1559_fees(&self) -> TransportResult<Eip1559Estimation> {
317 self.estimate_eip1559_fees_with(Eip1559Estimator::default()).await
318 }
319
320 async fn get_fee_history(
326 &self,
327 block_count: u64,
328 last_block: BlockNumberOrTag,
329 reward_percentiles: &[f64],
330 ) -> TransportResult<FeeHistory> {
331 self.client()
332 .request("eth_feeHistory", (U64::from(block_count), last_block, reward_percentiles))
333 .await
334 }
335
336 fn get_gas_price(&self) -> ProviderCall<NoParams, U128, u128> {
338 self.client()
339 .request_noparams("eth_gasPrice")
340 .map_resp(utils::convert_u128 as fn(U128) -> u128)
341 .into()
342 }
343
344 fn get_account_info(
350 &self,
351 address: Address,
352 ) -> RpcWithBlock<Address, alloy_rpc_types_eth::AccountInfo> {
353 self.client().request("eth_getAccountInfo", address).into()
354 }
355
356 fn get_account(&self, address: Address) -> RpcWithBlock<Address, alloy_consensus::TrieAccount> {
359 self.client().request("eth_getAccount", address).into()
360 }
361
362 fn get_balance(&self, address: Address) -> RpcWithBlock<Address, U256, U256> {
366 self.client().request("eth_getBalance", address).into()
367 }
368
369 fn get_block(&self, block: BlockId) -> EthGetBlock<N::BlockResponse> {
380 match block {
381 BlockId::Hash(hash) => EthGetBlock::by_hash(hash.block_hash, self.client()),
382 BlockId::Number(number) => EthGetBlock::by_number(number, self.client()),
383 }
384 }
385
386 fn get_block_by_hash(&self, hash: BlockHash) -> EthGetBlock<N::BlockResponse> {
411 EthGetBlock::by_hash(hash, self.client())
412 }
413
414 fn get_block_by_number(&self, number: BlockNumberOrTag) -> EthGetBlock<N::BlockResponse> {
439 EthGetBlock::by_number(number, self.client())
440 }
441
442 async fn get_block_transaction_count_by_hash(
444 &self,
445 hash: BlockHash,
446 ) -> TransportResult<Option<u64>> {
447 self.client()
448 .request("eth_getBlockTransactionCountByHash", (hash,))
449 .await
450 .map(|opt_count: Option<U64>| opt_count.map(|count| count.to::<u64>()))
451 }
452
453 async fn get_block_transaction_count_by_number(
455 &self,
456 block_number: BlockNumberOrTag,
457 ) -> TransportResult<Option<u64>> {
458 self.client()
459 .request("eth_getBlockTransactionCountByNumber", (block_number,))
460 .await
461 .map(|opt_count: Option<U64>| opt_count.map(|count| count.to::<u64>()))
462 }
463
464 fn get_block_receipts(
466 &self,
467 block: BlockId,
468 ) -> ProviderCall<(BlockId,), Option<Vec<N::ReceiptResponse>>> {
469 self.client().request("eth_getBlockReceipts", (block,)).into()
470 }
471
472 async fn get_block_access_list(
476 &self,
477 block: BlockId,
478 ) -> TransportResult<Option<BlockAccessList>> {
479 match block {
480 BlockId::Hash(hash) => self.get_block_access_list_by_hash(hash.block_hash).await,
481 BlockId::Number(number) => self.get_block_access_list_by_number(number).await,
482 }
483 }
484
485 async fn get_block_access_list_by_hash(
489 &self,
490 hash: BlockHash,
491 ) -> TransportResult<Option<BlockAccessList>> {
492 self.client().request("eth_getBlockAccessListByBlockHash", (hash,)).await
493 }
494
495 async fn get_block_access_list_by_number(
499 &self,
500 number: BlockNumberOrTag,
501 ) -> TransportResult<Option<BlockAccessList>> {
502 self.client().request("eth_getBlockAccessListByBlockNumber", (number,)).await
503 }
504
505 async fn get_block_access_list_raw(&self, block: BlockId) -> TransportResult<Option<Bytes>> {
509 self.client().request("eth_getBlockAccessListRaw", (block,)).await
510 }
511
512 async fn get_header(&self, block: BlockId) -> TransportResult<Option<N::HeaderResponse>> {
533 match block {
534 BlockId::Hash(hash) => self.get_header_by_hash(hash.block_hash).await,
535 BlockId::Number(number) => self.get_header_by_number(number).await,
536 }
537 }
538
539 async fn get_header_by_hash(
558 &self,
559 hash: BlockHash,
560 ) -> TransportResult<Option<N::HeaderResponse>> {
561 match self.client().request("eth_getHeaderByHash", (hash,)).await {
562 Ok(header) => Ok(header),
563 Err(err) if err.as_error_resp().is_some_and(|e| e.code == -32601) => {
565 Ok(self.get_block_by_hash(hash).await?.map(|b| b.header().clone()))
566 }
567 Err(err) => Err(err),
568 }
569 }
570
571 async fn get_header_by_number(
592 &self,
593 number: BlockNumberOrTag,
594 ) -> TransportResult<Option<N::HeaderResponse>> {
595 match self.client().request("eth_getHeaderByNumber", (number,)).await {
596 Ok(header) => Ok(header),
597 Err(err) if err.as_error_resp().is_some_and(|e| e.code == -32601) => {
599 Ok(self.get_block_by_number(number).await?.map(|b| b.header().clone()))
600 }
601 Err(err) => Err(err),
602 }
603 }
604
605 fn get_code_at(&self, address: Address) -> RpcWithBlock<Address, Bytes> {
607 self.client().request("eth_getCode", address).into()
608 }
609
610 async fn watch_blocks(&self) -> TransportResult<FilterPollerBuilder<B256>> {
633 let id = self.new_block_filter().await?;
634 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
635 }
636
637 async fn watch_full_blocks(&self) -> TransportResult<WatchBlocks<N::BlockResponse>> {
661 let id = self.new_block_filter().await?;
662 let poller = PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,));
663
664 Ok(WatchBlocks::new(poller))
665 }
666
667 async fn watch_headers(&self) -> TransportResult<WatchHeaders<N::HeaderResponse>> {
694 let id = self.new_block_filter().await?;
695 let poller = PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,));
696
697 Ok(WatchHeaders::new(poller))
698 }
699
700 async fn watch_pending_transactions(&self) -> TransportResult<FilterPollerBuilder<B256>> {
723 let id = self.new_pending_transactions_filter(false).await?;
724 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
725 }
726
727 async fn watch_logs(&self, filter: &Filter) -> TransportResult<FilterPollerBuilder<Log>> {
756 let id = self.new_filter(filter).await?;
757 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
758 }
759
760 fn watch_blocks_from(&self, start_block: u64) -> WatchBlocksFrom<N> {
827 WatchBlocksFrom::new(self.weak_client(), start_block)
828 }
829
830 fn watch_canonical_blocks_from(&self, start_block: u64) -> WatchCanonicalBlocksFrom<N> {
883 self.watch_blocks_from(start_block).canonical()
884 }
885
886 fn watch_logs_from(&self, start_block: u64, filter: &Filter) -> WatchLogsFrom<N> {
929 WatchLogsFrom::new(self.weak_client(), start_block, filter.clone())
930 }
931
932 fn watch_canonical_logs_from(
979 &self,
980 start_block: u64,
981 filter: &Filter,
982 ) -> WatchCanonicalLogsFrom<N> {
983 self.watch_logs_from(start_block, filter).canonical()
984 }
985
986 async fn watch_full_pending_transactions(
1013 &self,
1014 ) -> TransportResult<FilterPollerBuilder<N::TransactionResponse>> {
1015 let id = self.new_pending_transactions_filter(true).await?;
1016 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
1017 }
1018
1019 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
1024 async fn get_filter_changes<R: RpcRecv>(&self, id: U256) -> TransportResult<Vec<R>>
1025 where
1026 Self: Sized,
1027 {
1028 self.client().request("eth_getFilterChanges", (id,)).await
1029 }
1030
1031 async fn get_filter_changes_dyn(&self, id: U256) -> TransportResult<FilterChanges> {
1036 self.client().request("eth_getFilterChanges", (id,)).await
1037 }
1038
1039 async fn get_filter_logs(&self, id: U256) -> TransportResult<Vec<Log>> {
1041 self.client().request("eth_getFilterLogs", (id,)).await
1042 }
1043
1044 async fn uninstall_filter(&self, id: U256) -> TransportResult<bool> {
1046 self.client().request("eth_uninstallFilter", (id,)).await
1047 }
1048
1049 #[inline]
1054 async fn watch_pending_transaction(
1055 &self,
1056 config: PendingTransactionConfig,
1057 ) -> Result<PendingTransaction, PendingTransactionError> {
1058 self.root().watch_pending_transaction(config).await
1059 }
1060
1061 async fn get_logs(&self, filter: &Filter) -> TransportResult<Vec<Log>> {
1063 self.client().request("eth_getLogs", (filter,)).await
1064 }
1065
1066 fn get_proof(
1070 &self,
1071 address: Address,
1072 keys: Vec<StorageKey>,
1073 ) -> RpcWithBlock<(Address, Vec<StorageKey>), EIP1186AccountProofResponse> {
1074 self.client().request("eth_getProof", (address, keys)).into()
1075 }
1076
1077 fn get_storage_at(
1079 &self,
1080 address: Address,
1081 key: U256,
1082 ) -> RpcWithBlock<(Address, U256), StorageValue> {
1083 self.client().request("eth_getStorageAt", (address, key)).into()
1084 }
1085
1086 fn get_storage_values(
1090 &self,
1091 requests: StorageValuesRequest,
1092 ) -> RpcWithBlock<(StorageValuesRequest,), StorageValuesResponse> {
1093 self.client().request("eth_getStorageValues", (requests,)).into()
1094 }
1095
1096 fn get_transaction_by_sender_nonce(
1100 &self,
1101 sender: Address,
1102 nonce: u64,
1103 ) -> ProviderCall<(Address, U64), Option<N::TransactionResponse>> {
1104 self.client()
1105 .request("eth_getTransactionBySenderAndNonce", (sender, U64::from(nonce)))
1106 .into()
1107 }
1108
1109 fn get_transaction_by_hash(
1111 &self,
1112 hash: TxHash,
1113 ) -> ProviderCall<(TxHash,), Option<N::TransactionResponse>> {
1114 self.client().request("eth_getTransactionByHash", (hash,)).into()
1115 }
1116
1117 fn get_transaction_by_block_hash_and_index(
1119 &self,
1120 block_hash: B256,
1121 index: usize,
1122 ) -> ProviderCall<(B256, Index), Option<N::TransactionResponse>> {
1123 self.client()
1124 .request("eth_getTransactionByBlockHashAndIndex", (block_hash, Index(index)))
1125 .into()
1126 }
1127
1128 fn get_raw_transaction_by_block_hash_and_index(
1130 &self,
1131 block_hash: B256,
1132 index: usize,
1133 ) -> ProviderCall<(B256, Index), Option<Bytes>> {
1134 self.client()
1135 .request("eth_getRawTransactionByBlockHashAndIndex", (block_hash, Index(index)))
1136 .into()
1137 }
1138
1139 fn get_transaction_by_block_number_and_index(
1141 &self,
1142 block_number: BlockNumberOrTag,
1143 index: usize,
1144 ) -> ProviderCall<(BlockNumberOrTag, Index), Option<N::TransactionResponse>> {
1145 self.client()
1146 .request("eth_getTransactionByBlockNumberAndIndex", (block_number, Index(index)))
1147 .into()
1148 }
1149
1150 fn get_raw_transaction_by_block_number_and_index(
1152 &self,
1153 block_number: BlockNumberOrTag,
1154 index: usize,
1155 ) -> ProviderCall<(BlockNumberOrTag, Index), Option<Bytes>> {
1156 self.client()
1157 .request("eth_getRawTransactionByBlockNumberAndIndex", (block_number, Index(index)))
1158 .into()
1159 }
1160
1161 fn get_raw_transaction_by_hash(&self, hash: TxHash) -> ProviderCall<(TxHash,), Option<Bytes>> {
1173 self.client().request("eth_getRawTransactionByHash", (hash,)).into()
1174 }
1175
1176 #[doc(alias = "get_nonce")]
1178 #[doc(alias = "get_account_nonce")]
1179 fn get_transaction_count(
1180 &self,
1181 address: Address,
1182 ) -> RpcWithBlock<Address, U64, u64, fn(U64) -> u64> {
1183 self.client()
1184 .request("eth_getTransactionCount", address)
1185 .map_resp(utils::convert_u64 as fn(U64) -> u64)
1186 .into()
1187 }
1188
1189 fn get_transaction_receipt(
1191 &self,
1192 hash: TxHash,
1193 ) -> ProviderCall<(TxHash,), Option<N::ReceiptResponse>> {
1194 self.client().request("eth_getTransactionReceipt", (hash,)).into()
1195 }
1196
1197 async fn get_uncle(&self, tag: BlockId, idx: u64) -> TransportResult<Option<N::BlockResponse>> {
1199 let idx = U64::from(idx);
1200 match tag {
1201 BlockId::Hash(hash) => {
1202 self.client()
1203 .request("eth_getUncleByBlockHashAndIndex", (hash.block_hash, idx))
1204 .await
1205 }
1206 BlockId::Number(number) => {
1207 self.client().request("eth_getUncleByBlockNumberAndIndex", (number, idx)).await
1208 }
1209 }
1210 }
1211
1212 async fn get_uncle_count(&self, tag: BlockId) -> TransportResult<u64> {
1214 match tag {
1215 BlockId::Hash(hash) => self
1216 .client()
1217 .request("eth_getUncleCountByBlockHash", (hash.block_hash,))
1218 .await
1219 .map(|count: U64| count.to::<u64>()),
1220 BlockId::Number(number) => self
1221 .client()
1222 .request("eth_getUncleCountByBlockNumber", (number,))
1223 .await
1224 .map(|count: U64| count.to::<u64>()),
1225 }
1226 }
1227
1228 fn get_max_priority_fee_per_gas(&self) -> ProviderCall<NoParams, U128, u128> {
1230 self.client()
1231 .request_noparams("eth_maxPriorityFeePerGas")
1232 .map_resp(utils::convert_u128 as fn(U128) -> u128)
1233 .into()
1234 }
1235
1236 async fn new_block_filter(&self) -> TransportResult<U256> {
1242 self.client().request_noparams("eth_newBlockFilter").await
1243 }
1244
1245 async fn new_filter(&self, filter: &Filter) -> TransportResult<U256> {
1251 self.client().request("eth_newFilter", (filter,)).await
1252 }
1253
1254 async fn new_pending_transactions_filter(&self, full: bool) -> TransportResult<U256> {
1264 let param = if full { &[true][..] } else { &[] };
1266 self.client().request("eth_newPendingTransactionFilter", param).await
1267 }
1268
1269 async fn send_raw_transaction(
1273 &self,
1274 encoded_tx: &[u8],
1275 ) -> TransportResult<PendingTransactionBuilder<N>> {
1276 let rlp_hex = hex::encode_prefixed(encoded_tx);
1277 let tx_hash = self.client().request("eth_sendRawTransaction", (rlp_hex,)).await?;
1278 Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
1279 }
1280
1281 async fn send_raw_transaction_sync(
1319 &self,
1320 encoded_tx: &[u8],
1321 ) -> TransportResult<N::ReceiptResponse> {
1322 let rlp_hex = hex::encode_prefixed(encoded_tx);
1323 self.client().request("eth_sendRawTransactionSync", (rlp_hex,)).await
1324 }
1325
1326 async fn send_raw_transaction_conditional(
1337 &self,
1338 encoded_tx: &[u8],
1339 conditional: TransactionConditional,
1340 ) -> TransportResult<PendingTransactionBuilder<N>> {
1341 let rlp_hex = hex::encode_prefixed(encoded_tx);
1342 let tx_hash = self
1343 .client()
1344 .request("eth_sendRawTransactionConditional", (rlp_hex, conditional))
1345 .await?;
1346 Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
1347 }
1348
1349 async fn send_transaction(
1370 &self,
1371 tx: N::TransactionRequest,
1372 ) -> TransportResult<PendingTransactionBuilder<N>> {
1373 self.send_transaction_internal(SendableTx::Builder(tx)).await
1374 }
1375
1376 async fn send_tx_envelope(
1381 &self,
1382 tx: N::TxEnvelope,
1383 ) -> TransportResult<PendingTransactionBuilder<N>> {
1384 self.send_transaction_internal(SendableTx::Envelope(tx)).await
1385 }
1386
1387 #[doc(hidden)]
1394 async fn send_transaction_internal(
1395 &self,
1396 tx: SendableTx<N>,
1397 ) -> TransportResult<PendingTransactionBuilder<N>> {
1398 let _handle = self.root().get_heart();
1401
1402 match tx {
1403 SendableTx::Builder(mut tx) => {
1404 alloy_network::NetworkTransactionBuilder::prep_for_submission(&mut tx);
1405 let tx_hash = self.client().request("eth_sendTransaction", (tx,)).await?;
1406 Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
1407 }
1408 SendableTx::Envelope(tx) => {
1409 let encoded_tx = tx.encoded_2718();
1410 self.send_raw_transaction(&encoded_tx).await
1411 }
1412 }
1413 }
1414
1415 async fn send_transaction_sync(
1455 &self,
1456 tx: N::TransactionRequest,
1457 ) -> TransportResult<N::ReceiptResponse> {
1458 self.send_transaction_sync_internal(SendableTx::Builder(tx)).await
1459 }
1460
1461 #[doc(hidden)]
1471 async fn send_transaction_sync_internal(
1472 &self,
1473 tx: SendableTx<N>,
1474 ) -> TransportResult<N::ReceiptResponse> {
1475 let _handle = self.root().get_heart();
1478
1479 match tx {
1480 SendableTx::Builder(mut tx) => {
1481 alloy_network::NetworkTransactionBuilder::prep_for_submission(&mut tx);
1482 let receipt = self.client().request("eth_sendTransactionSync", (tx,)).await?;
1483 Ok(receipt)
1484 }
1485 SendableTx::Envelope(tx) => {
1486 let encoded_tx = tx.encoded_2718();
1487 self.send_raw_transaction_sync(&encoded_tx).await
1488 }
1489 }
1490 }
1491
1492 async fn sign_transaction(&self, tx: N::TransactionRequest) -> TransportResult<Bytes> {
1497 self.client().request("eth_signTransaction", (tx,)).await
1498 }
1499
1500 async fn fill_transaction(
1506 &self,
1507 tx: N::TransactionRequest,
1508 ) -> TransportResult<FillTransaction<N::TxEnvelope>>
1509 where
1510 N::TxEnvelope: RpcRecv,
1511 {
1512 self.client().request("eth_fillTransaction", (tx,)).await
1513 }
1514
1515 #[cfg(feature = "pubsub")]
1541 fn subscribe_blocks(&self) -> GetSubscription<(SubscriptionKind,), N::HeaderResponse> {
1542 let rpc_call = self.client().request("eth_subscribe", (SubscriptionKind::NewHeads,));
1543 GetSubscription::new(self.weak_client(), rpc_call)
1544 }
1545
1546 #[cfg(feature = "pubsub")]
1570 fn subscribe_full_blocks(&self) -> SubFullBlocks<N> {
1571 SubFullBlocks::new(self.subscribe_blocks(), self.weak_client())
1572 }
1573
1574 #[cfg(feature = "pubsub")]
1600 fn subscribe_pending_transactions(&self) -> GetSubscription<(SubscriptionKind,), B256> {
1601 let rpc_call =
1602 self.client().request("eth_subscribe", (SubscriptionKind::NewPendingTransactions,));
1603 GetSubscription::new(self.weak_client(), rpc_call)
1604 }
1605
1606 #[cfg(feature = "pubsub")]
1637 fn subscribe_full_pending_transactions(
1638 &self,
1639 ) -> GetSubscription<(SubscriptionKind, Params), N::TransactionResponse> {
1640 let rpc_call = self.client().request(
1641 "eth_subscribe",
1642 (SubscriptionKind::NewPendingTransactions, Params::Bool(true)),
1643 );
1644 GetSubscription::new(self.weak_client(), rpc_call)
1645 }
1646
1647 #[cfg(feature = "pubsub")]
1678 fn subscribe_logs(&self, filter: &Filter) -> GetSubscription<(SubscriptionKind, Params), Log> {
1679 let rpc_call = self.client().request(
1680 "eth_subscribe",
1681 (SubscriptionKind::Logs, Params::Logs(Box::new(filter.clone()))),
1682 );
1683 GetSubscription::new(self.weak_client(), rpc_call)
1684 }
1685
1686 #[cfg(feature = "pubsub")]
1688 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
1689 fn subscribe<P, R>(&self, params: P) -> GetSubscription<P, R>
1690 where
1691 P: RpcSend,
1692 R: RpcRecv,
1693 Self: Sized,
1694 {
1695 let rpc_call = self.client().request("eth_subscribe", params);
1696 GetSubscription::new(self.weak_client(), rpc_call)
1697 }
1698
1699 #[cfg(feature = "pubsub")]
1720 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
1721 fn subscribe_to<R>(&self, method: &'static str) -> GetSubscription<NoParams, R>
1722 where
1723 R: RpcRecv,
1724 Self: Sized,
1725 {
1726 let mut rpc_call = self.client().request_noparams(method);
1727 rpc_call.set_is_subscription();
1728 GetSubscription::new(self.weak_client(), rpc_call)
1729 }
1730
1731 #[cfg(feature = "pubsub")]
1733 async fn unsubscribe(&self, id: B256) -> TransportResult<()> {
1734 self.root().unsubscribe(id)
1735 }
1736
1737 fn syncing(&self) -> ProviderCall<NoParams, SyncStatus> {
1739 self.client().request_noparams("eth_syncing").into()
1740 }
1741
1742 #[doc(alias = "web3_client_version")]
1744 fn get_client_version(&self) -> ProviderCall<NoParams, String> {
1745 self.client().request_noparams("web3_clientVersion").into()
1746 }
1747
1748 #[doc(alias = "web3_sha3")]
1750 fn get_sha3(&self, data: &[u8]) -> ProviderCall<(String,), B256> {
1751 self.client().request("web3_sha3", (hex::encode_prefixed(data),)).into()
1752 }
1753
1754 fn get_net_version(&self) -> ProviderCall<NoParams, U64, u64> {
1756 self.client()
1757 .request_noparams("net_version")
1758 .map_resp(utils::convert_u64 as fn(U64) -> u64)
1759 .into()
1760 }
1761
1762 async fn raw_request<P, R>(&self, method: Cow<'static, str>, params: P) -> TransportResult<R>
1787 where
1788 P: RpcSend,
1789 R: RpcRecv,
1790 Self: Sized,
1791 {
1792 self.client().request(method, ¶ms).await
1793 }
1794
1795 async fn raw_request_dyn(
1818 &self,
1819 method: Cow<'static, str>,
1820 params: &RawValue,
1821 ) -> TransportResult<Box<RawValue>> {
1822 self.client().request(method, params).await
1823 }
1824
1825 #[inline]
1827 fn transaction_request(&self) -> N::TransactionRequest {
1828 Default::default()
1829 }
1830}
1831
1832#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
1833#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
1834impl<N: Network> Provider<N> for RootProvider<N> {
1835 #[inline]
1836 fn root(&self) -> &Self {
1837 self
1838 }
1839
1840 #[inline]
1841 fn client(&self) -> ClientRef<'_> {
1842 self.inner.client_ref()
1843 }
1844
1845 #[inline]
1846 fn weak_client(&self) -> WeakClient {
1847 self.inner.weak_client()
1848 }
1849
1850 #[inline]
1851 async fn watch_pending_transaction(
1852 &self,
1853 config: PendingTransactionConfig,
1854 ) -> Result<PendingTransaction, PendingTransactionError> {
1855 let block_number =
1856 if let Some(receipt) = self.get_transaction_receipt(*config.tx_hash()).await? {
1857 if config.required_confirmations() <= 1 {
1859 return Ok(PendingTransaction::ready(*config.tx_hash()));
1860 }
1861 receipt.block_number()
1864 } else {
1865 None
1866 };
1867
1868 self.get_heart()
1869 .watch_tx(config, block_number)
1870 .await
1871 .map_err(|_| PendingTransactionError::FailedToRegister)
1872 }
1873}
1874
1875#[cfg(test)]
1876mod tests {
1877 use super::*;
1878 use crate::{builder, ext::test::async_ci_only, ProviderBuilder, WalletProvider};
1879 use alloy_consensus::{Transaction, TxEnvelope};
1880 use alloy_network::{
1881 AnyNetwork, EthereumWallet, NetworkTransactionBuilder, TransactionBuilder,
1882 };
1883 use alloy_node_bindings::{utils::run_with_tempdir, Anvil, Reth};
1884 use alloy_primitives::{address, b256, bytes, keccak256};
1885 use alloy_rlp::Decodable;
1886 use alloy_rpc_client::{BuiltInConnectionString, RpcClient};
1887 use alloy_rpc_types_eth::{request::TransactionRequest, Block};
1888 use alloy_signer_local::PrivateKeySigner;
1889 use alloy_transport::layers::{RetryBackoffLayer, RetryPolicy};
1890 use std::{io::Read, str::FromStr, time::Duration};
1891
1892 use alloy_consensus::transaction::SignerRecoverable;
1894 #[cfg(feature = "hyper")]
1895 use alloy_transport_http::{
1896 hyper,
1897 hyper::body::Bytes as HyperBytes,
1898 hyper_util::{
1899 client::legacy::{Client, Error},
1900 rt::TokioExecutor,
1901 },
1902 HyperResponse, HyperResponseFut,
1903 };
1904 #[cfg(feature = "hyper")]
1905 use http_body_util::Full;
1906 #[cfg(feature = "hyper")]
1907 use tower::{Layer, Service};
1908
1909 #[tokio::test]
1910 async fn test_provider_builder() {
1911 let provider =
1912 RootProvider::<Ethereum>::builder().with_recommended_fillers().connect_anvil();
1913 let num = provider.get_block_number().await.unwrap();
1914 assert_eq!(0, num);
1915 }
1916
1917 #[tokio::test]
1918 async fn test_builder_helper_fn() {
1919 let provider = builder::<Ethereum>().with_recommended_fillers().connect_anvil();
1920 let num = provider.get_block_number().await.unwrap();
1921 assert_eq!(0, num);
1922 }
1923
1924 #[cfg(feature = "hyper")]
1925 #[tokio::test]
1926 async fn test_default_hyper_transport() {
1927 let anvil = Anvil::new().spawn();
1928 let hyper_t = alloy_transport_http::HyperTransport::new_hyper(anvil.endpoint_url());
1929
1930 let rpc_client = alloy_rpc_client::RpcClient::new(hyper_t, true);
1931
1932 let provider = RootProvider::<Ethereum>::new(rpc_client);
1933 let num = provider.get_block_number().await.unwrap();
1934 assert_eq!(0, num);
1935 }
1936
1937 #[cfg(feature = "hyper")]
1938 #[tokio::test]
1939 async fn test_hyper_layer_transport() {
1940 struct LoggingLayer;
1941
1942 impl<S> Layer<S> for LoggingLayer {
1943 type Service = LoggingService<S>;
1944
1945 fn layer(&self, inner: S) -> Self::Service {
1946 LoggingService { inner }
1947 }
1948 }
1949
1950 #[derive(Clone)] struct LoggingService<S> {
1952 inner: S,
1953 }
1954
1955 impl<S, B> Service<hyper::Request<B>> for LoggingService<S>
1956 where
1957 S: Service<hyper::Request<B>, Response = HyperResponse, Error = Error>
1958 + Clone
1959 + Send
1960 + Sync
1961 + 'static,
1962 S::Future: Send,
1963 S::Error: std::error::Error + Send + Sync + 'static,
1964 B: From<Vec<u8>> + Send + 'static + Clone + Sync + std::fmt::Debug,
1965 {
1966 type Response = HyperResponse;
1967 type Error = Error;
1968 type Future = HyperResponseFut;
1969
1970 fn poll_ready(
1971 &mut self,
1972 cx: &mut std::task::Context<'_>,
1973 ) -> std::task::Poll<Result<(), Self::Error>> {
1974 self.inner.poll_ready(cx)
1975 }
1976
1977 fn call(&mut self, req: hyper::Request<B>) -> Self::Future {
1978 println!("Logging Layer - HyperRequest {req:?}");
1979
1980 let fut = self.inner.call(req);
1981
1982 Box::pin(fut)
1983 }
1984 }
1985 use http::header::{self, HeaderValue};
1986 use tower_http::{
1987 sensitive_headers::SetSensitiveRequestHeadersLayer, set_header::SetRequestHeaderLayer,
1988 };
1989 let anvil = Anvil::new().spawn();
1990 let hyper_client = Client::builder(TokioExecutor::new()).build_http::<Full<HyperBytes>>();
1991
1992 let service = tower::ServiceBuilder::new()
1994 .layer(SetRequestHeaderLayer::if_not_present(
1995 header::USER_AGENT,
1996 HeaderValue::from_static("alloy app"),
1997 ))
1998 .layer(SetRequestHeaderLayer::overriding(
1999 header::AUTHORIZATION,
2000 HeaderValue::from_static("some-jwt-token"),
2001 ))
2002 .layer(SetRequestHeaderLayer::appending(
2003 header::SET_COOKIE,
2004 HeaderValue::from_static("cookie-value"),
2005 ))
2006 .layer(SetSensitiveRequestHeadersLayer::new([header::AUTHORIZATION])) .layer(LoggingLayer)
2008 .service(hyper_client);
2009
2010 let layer_transport = alloy_transport_http::HyperClient::with_service(service);
2011
2012 let http_hyper =
2013 alloy_transport_http::Http::with_client(layer_transport, anvil.endpoint_url());
2014
2015 let rpc_client = alloy_rpc_client::RpcClient::new(http_hyper, true);
2016
2017 let provider = RootProvider::<Ethereum>::new(rpc_client);
2018 let num = provider.get_block_number().await.unwrap();
2019 assert_eq!(0, num);
2020
2021 let cloned_t = provider.client().transport().clone();
2023
2024 let rpc_client = alloy_rpc_client::RpcClient::new(cloned_t, true);
2025
2026 let provider = RootProvider::<Ethereum>::new(rpc_client);
2027 let num = provider.get_block_number().await.unwrap();
2028 assert_eq!(0, num);
2029 }
2030
2031 #[cfg(feature = "hyper")]
2032 #[tokio::test]
2033 #[cfg_attr(windows, ignore = "no reth on windows")]
2034 async fn test_auth_layer_transport() {
2035 crate::ext::test::async_ci_only(|| async move {
2036 use alloy_node_bindings::Reth;
2037 use alloy_rpc_types_engine::JwtSecret;
2038 use alloy_transport_http::{AuthLayer, Http, HyperClient};
2039
2040 let secret = JwtSecret::random();
2041
2042 let reth =
2043 Reth::new().arg("--rpc.jwtsecret").arg(hex::encode(secret.as_bytes())).spawn();
2044
2045 let layer_transport = HyperClient::new().layer(AuthLayer::new(secret));
2046
2047 let http_hyper = Http::with_client(layer_transport, reth.endpoint_url());
2048
2049 let rpc_client = alloy_rpc_client::RpcClient::new(http_hyper, true);
2050
2051 let provider = RootProvider::<Ethereum>::new(rpc_client);
2052
2053 let num = provider.get_block_number().await.unwrap();
2054 assert_eq!(0, num);
2055 })
2056 .await;
2057 }
2058
2059 #[tokio::test]
2060 async fn test_builder_helper_fn_any_network() {
2061 let anvil = Anvil::new().spawn();
2062 let provider =
2063 builder::<AnyNetwork>().with_recommended_fillers().connect_http(anvil.endpoint_url());
2064 let num = provider.get_block_number().await.unwrap();
2065 assert_eq!(0, num);
2066 }
2067
2068 #[cfg(feature = "reqwest")]
2069 #[tokio::test]
2070 async fn object_safety() {
2071 let provider = ProviderBuilder::new().connect_anvil();
2072
2073 let refdyn = &provider as &dyn Provider<_>;
2074 let num = refdyn.get_block_number().await.unwrap();
2075 assert_eq!(0, num);
2076 }
2077
2078 #[cfg(feature = "ws-base")]
2079 #[tokio::test]
2080 async fn subscribe_blocks_http() {
2081 let provider = ProviderBuilder::new().connect_anvil_with_config(|a| a.block_time(1));
2082
2083 let err = provider.subscribe_blocks().await.unwrap_err();
2084 let alloy_json_rpc::RpcError::Transport(
2085 alloy_transport::TransportErrorKind::PubsubUnavailable,
2086 ) = err
2087 else {
2088 panic!("{err:?}");
2089 };
2090 }
2091
2092 #[cfg(feature = "ws-base")]
2094 #[tokio::test]
2095 async fn websocket_tls_setup() {
2096 for url in ["wss://mainnet.infura.io/ws/v3/b0f825787ba840af81e46c6a64d20754"] {
2097 let _ = ProviderBuilder::<_, _, Ethereum>::default().connect(url).await.unwrap();
2098 }
2099 }
2100
2101 #[cfg(feature = "ws-base")]
2102 #[tokio::test]
2103 async fn subscribe_blocks_ws() {
2104 use futures::stream::StreamExt;
2105
2106 let anvil = Anvil::new().block_time_f64(0.2).spawn();
2107 let ws = alloy_rpc_client::WsConnect::new(anvil.ws_endpoint());
2108 let client = alloy_rpc_client::RpcClient::connect_pubsub(ws).await.unwrap();
2109 let provider = RootProvider::<Ethereum>::new(client);
2110
2111 let sub = provider.subscribe_blocks().await.unwrap();
2112 let mut stream = sub.into_stream().take(5);
2113 let mut next = None;
2114 while let Some(header) = stream.next().await {
2115 if let Some(next) = &mut next {
2116 assert_eq!(header.number, *next);
2117 *next += 1;
2118 } else {
2119 next = Some(header.number + 1);
2120 }
2121 }
2122 }
2123
2124 #[cfg(feature = "ws-base")]
2125 #[tokio::test]
2126 async fn subscribe_full_blocks() {
2127 use futures::StreamExt;
2128
2129 let anvil = Anvil::new().block_time_f64(0.2).spawn();
2130 let ws = alloy_rpc_client::WsConnect::new(anvil.ws_endpoint());
2131 let client = alloy_rpc_client::RpcClient::connect_pubsub(ws).await.unwrap();
2132
2133 let provider = RootProvider::<Ethereum>::new(client);
2134
2135 let sub = provider.subscribe_full_blocks().hashes().channel_size(10);
2136
2137 let mut stream = sub.into_stream().await.unwrap().take(5);
2138
2139 let mut next = None;
2140 while let Some(Ok(block)) = stream.next().await {
2141 if let Some(next) = &mut next {
2142 assert_eq!(block.header().number, *next);
2143 *next += 1;
2144 } else {
2145 next = Some(block.header().number + 1);
2146 }
2147 }
2148 }
2149
2150 #[tokio::test]
2151 #[cfg(feature = "ws-base")]
2152 async fn subscribe_blocks_ws_remote() {
2153 use futures::stream::StreamExt;
2154
2155 let url = "wss://eth-mainnet.g.alchemy.com/v2/viFmeVzhg6bWKVMIWWS8MhmzREB-D4f7";
2156 let ws = alloy_rpc_client::WsConnect::new(url);
2157 let Ok(client) = alloy_rpc_client::RpcClient::connect_pubsub(ws).await else { return };
2158 let provider = RootProvider::<Ethereum>::new(client);
2159 let sub = provider.subscribe_blocks().await.unwrap();
2160 let mut stream = sub.into_stream().take(1);
2161 while let Some(header) = stream.next().await {
2162 println!("New block {header:?}");
2163 assert!(header.number > 0);
2164 }
2165 }
2166
2167 #[tokio::test]
2168 async fn test_custom_retry_policy() {
2169 #[derive(Debug, Clone)]
2170 struct CustomPolicy;
2171 impl RetryPolicy for CustomPolicy {
2172 fn should_retry(&self, _err: &alloy_transport::TransportError) -> bool {
2173 true
2174 }
2175
2176 fn backoff_hint(
2177 &self,
2178 _error: &alloy_transport::TransportError,
2179 ) -> Option<std::time::Duration> {
2180 None
2181 }
2182 }
2183
2184 let retry_layer = RetryBackoffLayer::new_with_policy(10, 100, 10000, CustomPolicy);
2185 let anvil = Anvil::new().spawn();
2186 let client = RpcClient::builder().layer(retry_layer).http(anvil.endpoint_url());
2187
2188 let provider = RootProvider::<Ethereum>::new(client);
2189 let num = provider.get_block_number().await.unwrap();
2190 assert_eq!(0, num);
2191 }
2192
2193 #[tokio::test]
2194 async fn test_send_tx() {
2195 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2196 let tx = TransactionRequest {
2197 value: Some(U256::from(100)),
2198 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2199 gas_price: Some(20e9 as u128),
2200 gas: Some(21000),
2201 ..Default::default()
2202 };
2203
2204 let builder = provider.send_transaction(tx.clone()).await.expect("failed to send tx");
2205 let hash1 = *builder.tx_hash();
2206 let hash2 = builder.watch().await.expect("failed to await pending tx");
2207 assert_eq!(hash1, hash2);
2208
2209 let builder = provider.send_transaction(tx).await.expect("failed to send tx");
2210 let hash1 = *builder.tx_hash();
2211 let hash2 =
2212 builder.get_receipt().await.expect("failed to await pending tx").transaction_hash;
2213 assert_eq!(hash1, hash2);
2214 }
2215
2216 #[tokio::test]
2217 async fn test_send_tx_sync() {
2218 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2219 let tx = TransactionRequest {
2220 value: Some(U256::from(100)),
2221 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2222 gas_price: Some(20e9 as u128),
2223 gas: Some(21000),
2224 ..Default::default()
2225 };
2226
2227 let _receipt =
2228 provider.send_transaction_sync(tx.clone()).await.expect("failed to send tx sync");
2229 }
2230
2231 #[tokio::test]
2232 async fn test_send_raw_transaction_sync() {
2233 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2234
2235 let tx = TransactionRequest {
2237 nonce: Some(0),
2238 value: Some(U256::from(100)),
2239 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2240 gas_price: Some(20e9 as u128),
2241 gas: Some(21000),
2242 ..Default::default()
2243 };
2244
2245 let tx_envelope = tx.build(&provider.wallet()).await.expect("failed to build tx");
2247
2248 let encoded = tx_envelope.encoded_2718();
2250
2251 let receipt =
2253 provider.send_raw_transaction_sync(&encoded).await.expect("failed to send raw tx sync");
2254
2255 assert_eq!(receipt.to(), Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045")));
2257 assert!(receipt.block_number().is_some(), "transaction should be mined");
2259 assert!(receipt.transaction_hash() != B256::ZERO, "should have valid tx hash");
2260 }
2261
2262 #[tokio::test]
2263 async fn test_watch_confirmed_tx() {
2264 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2265 let tx = TransactionRequest {
2266 value: Some(U256::from(100)),
2267 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2268 gas_price: Some(20e9 as u128),
2269 gas: Some(21000),
2270 ..Default::default()
2271 };
2272
2273 let builder = provider.send_transaction(tx).await.expect("failed to send tx");
2274 let hash1 = *builder.tx_hash();
2275
2276 loop {
2278 if provider
2279 .get_transaction_receipt(hash1)
2280 .await
2281 .expect("failed to await pending tx")
2282 .is_some()
2283 {
2284 break;
2285 }
2286 }
2287
2288 let tx2 = TransactionRequest {
2290 value: Some(U256::from(100)),
2291 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2292 gas_price: Some(20e9 as u128),
2293 gas: Some(21000),
2294 ..Default::default()
2295 };
2296 provider.send_transaction(tx2).await.expect("failed to send tx").watch().await.unwrap();
2297
2298 let watch = builder.watch();
2300 let watch_with_timeout = tokio::time::timeout(Duration::from_secs(1), watch);
2302 let hash2 = watch_with_timeout
2303 .await
2304 .expect("Watching tx timed out")
2305 .expect("failed to await pending tx");
2306 assert_eq!(hash1, hash2);
2307 }
2308
2309 #[tokio::test]
2310 async fn gets_block_number() {
2311 let provider = ProviderBuilder::new().connect_anvil();
2312 let num = provider.get_block_number().await.unwrap();
2313 assert_eq!(0, num)
2314 }
2315
2316 #[tokio::test]
2317 async fn gets_block_number_for_id() {
2318 let provider = ProviderBuilder::new().connect_anvil();
2319
2320 let block_num = provider
2321 .get_block_number_by_id(BlockId::Number(BlockNumberOrTag::Number(0)))
2322 .await
2323 .unwrap();
2324 assert_eq!(block_num, Some(0));
2325
2326 let block_num = provider
2327 .get_block_number_by_id(BlockId::Number(BlockNumberOrTag::Latest))
2328 .await
2329 .unwrap();
2330 assert_eq!(block_num, Some(0));
2331
2332 let block =
2333 provider.get_block_by_number(BlockNumberOrTag::Number(0)).await.unwrap().unwrap();
2334 let hash = block.header.hash;
2335 let block_num = provider.get_block_number_by_id(BlockId::Hash(hash.into())).await.unwrap();
2336 assert_eq!(block_num, Some(0));
2337 }
2338
2339 #[tokio::test]
2340 async fn gets_block_number_with_raw_req() {
2341 let provider = ProviderBuilder::new().connect_anvil();
2342 let num: U64 =
2343 provider.raw_request("eth_blockNumber".into(), NoParams::default()).await.unwrap();
2344 assert_eq!(0, num.to::<u64>())
2345 }
2346
2347 #[cfg(feature = "anvil-api")]
2348 #[tokio::test]
2349 async fn gets_transaction_count() {
2350 let provider = ProviderBuilder::new().connect_anvil();
2351 let accounts = provider.get_accounts().await.unwrap();
2352 let sender = accounts[0];
2353
2354 let count = provider.get_transaction_count(sender).await.unwrap();
2356 assert_eq!(count, 0);
2357
2358 let tx = TransactionRequest {
2360 value: Some(U256::from(100)),
2361 from: Some(sender),
2362 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2363 gas_price: Some(20e9 as u128),
2364 gas: Some(21000),
2365 ..Default::default()
2366 };
2367 let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await;
2368
2369 let count = provider.get_transaction_count(sender).await.unwrap();
2371 assert_eq!(count, 1);
2372
2373 let count = provider.get_transaction_count(sender).block_id(0.into()).await.unwrap();
2375 assert_eq!(count, 0);
2376 }
2377
2378 #[tokio::test]
2379 async fn gets_block_by_hash() {
2380 let provider = ProviderBuilder::new().connect_anvil();
2381 let num = 0;
2382 let tag: BlockNumberOrTag = num.into();
2383 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
2384 let hash = block.header.hash;
2385 let block = provider.get_block_by_hash(hash).full().await.unwrap().unwrap();
2386 assert_eq!(block.header.hash, hash);
2387 }
2388
2389 #[tokio::test]
2390 async fn gets_block_by_hash_with_raw_req() {
2391 let provider = ProviderBuilder::new().connect_anvil();
2392 let num = 0;
2393 let tag: BlockNumberOrTag = num.into();
2394 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
2395 let hash = block.header.hash;
2396 let block: Block = provider
2397 .raw_request::<(B256, bool), Block>("eth_getBlockByHash".into(), (hash, true))
2398 .await
2399 .unwrap();
2400 assert_eq!(block.header.hash, hash);
2401 }
2402
2403 #[tokio::test]
2404 async fn gets_block_by_number_full() {
2405 let provider = ProviderBuilder::new().connect_anvil();
2406 let num = 0;
2407 let tag: BlockNumberOrTag = num.into();
2408 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
2409 assert_eq!(block.header.number, num);
2410 }
2411
2412 #[tokio::test]
2413 async fn gets_block_by_number() {
2414 let provider = ProviderBuilder::new().connect_anvil();
2415 let num = 0;
2416 let tag: BlockNumberOrTag = num.into();
2417 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
2418 assert_eq!(block.header.number, num);
2419 }
2420
2421 #[tokio::test]
2422 async fn gets_client_version() {
2423 let provider = ProviderBuilder::new().connect_anvil();
2424 let version = provider.get_client_version().await.unwrap();
2425 assert!(version.contains("anvil"), "{version}");
2426 }
2427
2428 #[tokio::test]
2429 async fn gets_sha3() {
2430 let provider = ProviderBuilder::new().connect_anvil();
2431 let data = b"alloy";
2432 let hash = provider.get_sha3(data).await.unwrap();
2433 assert_eq!(hash, keccak256(data));
2434 }
2435
2436 #[tokio::test]
2437 async fn gets_chain_id() {
2438 let dev_chain_id: u64 = 13371337;
2439
2440 let provider =
2441 ProviderBuilder::new().connect_anvil_with_config(|a| a.chain_id(dev_chain_id));
2442
2443 let chain_id = provider.get_chain_id().await.unwrap();
2444 assert_eq!(chain_id, dev_chain_id);
2445 }
2446
2447 #[tokio::test]
2448 async fn gets_network_id() {
2449 let dev_chain_id: u64 = 13371337;
2450 let provider =
2451 ProviderBuilder::new().connect_anvil_with_config(|a| a.chain_id(dev_chain_id));
2452
2453 let chain_id = provider.get_net_version().await.unwrap();
2454 assert_eq!(chain_id, dev_chain_id);
2455 }
2456
2457 #[tokio::test]
2458 async fn gets_storage_at() {
2459 let provider = ProviderBuilder::new().connect_anvil();
2460 let addr = Address::with_last_byte(16);
2461 let storage = provider.get_storage_at(addr, U256::ZERO).await.unwrap();
2462 assert_eq!(storage, U256::ZERO);
2463 }
2464
2465 #[tokio::test]
2466 async fn gets_transaction_by_hash_not_found() {
2467 let provider = ProviderBuilder::new().connect_anvil();
2468 let tx_hash = b256!("5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95");
2469 let tx = provider.get_transaction_by_hash(tx_hash).await.expect("failed to fetch tx");
2470
2471 assert!(tx.is_none());
2472 }
2473
2474 #[tokio::test]
2475 async fn gets_transaction_by_hash() {
2476 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2477
2478 let req = TransactionRequest::default()
2479 .from(provider.default_signer_address())
2480 .to(Address::repeat_byte(5))
2481 .value(U256::ZERO)
2482 .input(bytes!("deadbeef").into());
2483
2484 let tx_hash = *provider.send_transaction(req).await.expect("failed to send tx").tx_hash();
2485
2486 let tx = provider
2487 .get_transaction_by_hash(tx_hash)
2488 .await
2489 .expect("failed to fetch tx")
2490 .expect("tx not included");
2491 assert_eq!(tx.input(), &bytes!("deadbeef"));
2492 }
2493
2494 #[tokio::test]
2495 #[ignore]
2496 async fn gets_logs() {
2497 let provider = ProviderBuilder::new().connect_anvil();
2498 let filter = Filter::new()
2499 .at_block_hash(b256!(
2500 "b20e6f35d4b46b3c4cd72152faec7143da851a0dc281d390bdd50f58bfbdb5d3"
2501 ))
2502 .event_signature(b256!(
2503 "e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"
2504 ));
2505 let logs = provider.get_logs(&filter).await.unwrap();
2506 assert_eq!(logs.len(), 1);
2507 }
2508
2509 #[tokio::test]
2510 #[ignore]
2511 async fn gets_tx_receipt() {
2512 let provider = ProviderBuilder::new().connect_anvil();
2513 let receipt = provider
2514 .get_transaction_receipt(b256!(
2515 "5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95"
2516 ))
2517 .await
2518 .unwrap();
2519 assert!(receipt.is_some());
2520 let receipt = receipt.unwrap();
2521 assert_eq!(
2522 receipt.transaction_hash,
2523 b256!("5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95")
2524 );
2525 }
2526
2527 #[tokio::test]
2528 async fn gets_max_priority_fee_per_gas() {
2529 let provider = ProviderBuilder::new().connect_anvil();
2530 let _fee = provider.get_max_priority_fee_per_gas().await.unwrap();
2531 }
2532
2533 #[tokio::test]
2534 async fn gets_fee_history() {
2535 let provider = ProviderBuilder::new().connect_anvil();
2536 let block_number = provider.get_block_number().await.unwrap();
2537 let fee_history = provider
2538 .get_fee_history(
2539 utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
2540 BlockNumberOrTag::Number(block_number),
2541 &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
2542 )
2543 .await
2544 .unwrap();
2545 assert_eq!(fee_history.oldest_block, 0_u64);
2546 }
2547
2548 #[tokio::test]
2549 async fn gets_block_transaction_count_by_hash() {
2550 let provider = ProviderBuilder::new().connect_anvil();
2551 let block = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
2552 let hash = block.header.hash;
2553 let tx_count = provider.get_block_transaction_count_by_hash(hash).await.unwrap();
2554 assert!(tx_count.is_some());
2555 }
2556
2557 #[tokio::test]
2558 async fn gets_block_transaction_count_by_number() {
2559 let provider = ProviderBuilder::new().connect_anvil();
2560 let tx_count =
2561 provider.get_block_transaction_count_by_number(BlockNumberOrTag::Latest).await.unwrap();
2562 assert!(tx_count.is_some());
2563 }
2564
2565 #[tokio::test]
2566 async fn gets_block_receipts() {
2567 let provider = ProviderBuilder::new().connect_anvil();
2568 let receipts =
2569 provider.get_block_receipts(BlockId::Number(BlockNumberOrTag::Latest)).await.unwrap();
2570 assert!(receipts.is_some());
2571 }
2572
2573 #[tokio::test]
2574 async fn sends_raw_transaction() {
2575 let provider = ProviderBuilder::new().connect_anvil();
2576 let pending = provider
2577 .send_raw_transaction(
2578 bytes!("f865808477359400825208940000000000000000000000000000000000000000018082f4f5a00505e227c1c636c76fac55795db1a40a4d24840d81b40d2fe0cc85767f6bd202a01e91b437099a8a90234ac5af3cb7ca4fb1432e133f75f9a91678eaf5f487c74b").as_ref()
2580 )
2581 .await.unwrap();
2582 assert_eq!(
2583 pending.tx_hash().to_string(),
2584 "0x9dae5cf33694a02e8a7d5de3fe31e9d05ca0ba6e9180efac4ab20a06c9e598a3"
2585 );
2586 }
2587
2588 #[tokio::test]
2589 async fn connect_boxed() {
2590 let anvil = Anvil::new().spawn();
2591
2592 let provider = RootProvider::<Ethereum>::connect(anvil.endpoint().as_str()).await;
2593
2594 match provider {
2595 Ok(provider) => {
2596 let num = provider.get_block_number().await.unwrap();
2597 assert_eq!(0, num);
2598 }
2599 Err(e) => {
2600 assert_eq!(
2601 format!("{e}"),
2602 "hyper not supported by BuiltinConnectionString. Please instantiate a hyper client manually"
2603 );
2604 }
2605 }
2606 }
2607
2608 #[tokio::test]
2609 async fn any_network_wallet_filler() {
2610 use alloy_serde::WithOtherFields;
2611 let anvil = Anvil::new().spawn();
2612 let signer: PrivateKeySigner =
2613 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
2614 let wallet = EthereumWallet::from(signer);
2615
2616 let provider = ProviderBuilder::new()
2617 .network::<AnyNetwork>()
2618 .wallet(wallet)
2619 .connect_http(anvil.endpoint_url());
2620
2621 let tx = TransactionRequest::default()
2622 .with_to(address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"))
2623 .value(U256::from(325235));
2624
2625 let tx = WithOtherFields::new(tx);
2626
2627 let builder = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
2628
2629 assert!(builder.status());
2630 }
2631
2632 #[tokio::test]
2633 async fn builtin_connect_boxed() {
2634 let anvil = Anvil::new().spawn();
2635
2636 let conn: BuiltInConnectionString = anvil.endpoint().parse().unwrap();
2637
2638 let transport = conn.connect_boxed().await.unwrap();
2639
2640 let client = alloy_rpc_client::RpcClient::new(transport, true);
2641
2642 let provider = RootProvider::<Ethereum>::new(client);
2643
2644 let num = provider.get_block_number().await.unwrap();
2645 assert_eq!(0, num);
2646 }
2647
2648 #[tokio::test]
2649 async fn test_uncle_count() {
2650 let provider = ProviderBuilder::new().connect_anvil();
2651
2652 let count = provider.get_uncle_count(0.into()).await.unwrap();
2653 assert_eq!(count, 0);
2654 }
2655
2656 #[tokio::test]
2657 #[cfg(any(
2658 feature = "reqwest-default-tls",
2659 feature = "reqwest-rustls-tls",
2660 feature = "reqwest-native-tls",
2661 ))]
2662 #[ignore = "ignore until <https://github.com/paradigmxyz/reth/pull/14727> is in"]
2663 async fn call_mainnet() {
2664 use alloy_network::TransactionBuilder;
2665 use alloy_sol_types::SolValue;
2666
2667 let url = "https://docs-demo.quiknode.pro/";
2668 let provider = ProviderBuilder::new().connect_http(url.parse().unwrap());
2669 let req = TransactionRequest::default()
2670 .with_to(address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")) .with_input(bytes!("06fdde03")); let result = provider.call(req.clone()).await.unwrap();
2673 assert_eq!(String::abi_decode(&result).unwrap(), "Wrapped Ether");
2674
2675 let result = provider.call(req).block(0.into()).await.unwrap();
2676 assert_eq!(result.to_string(), "0x");
2677 }
2678
2679 #[tokio::test]
2680 async fn call_many_mainnet() {
2681 use alloy_rpc_types_eth::{BlockOverrides, StateContext};
2682
2683 let url = "https://docs-demo.quiknode.pro/";
2684 let provider = ProviderBuilder::new().connect_http(url.parse().unwrap());
2685 let tx1 = TransactionRequest::default()
2686 .with_to(address!("6b175474e89094c44da98b954eedeac495271d0f"))
2687 .with_gas_limit(1000000)
2688 .with_gas_price(2023155498)
2689 .with_input(hex!("a9059cbb000000000000000000000000bc0E63965946815d105E7591407704e6e1964E590000000000000000000000000000000000000000000000000000000005f5e100"));
2690 let tx2 = TransactionRequest::default()
2691 .with_to(address!("833589fcd6edb6e08f4c7c32d4f71b54bda02913"))
2692 .with_gas_price(2023155498)
2693 .with_input(hex!(
2694 "70a08231000000000000000000000000bc0E63965946815d105E7591407704e6e1964E59"
2695 ));
2696
2697 let transactions = vec![tx1.clone(), tx2.clone()];
2698
2699 let block_override =
2700 BlockOverrides { number: Some(U256::from(12279785)), ..Default::default() };
2701
2702 let bundles = vec![Bundle { transactions, block_override: Some(block_override.clone()) }];
2703
2704 let context = StateContext {
2705 block_number: Some(BlockId::number(12279785)),
2706 transaction_index: Some(1.into()),
2707 };
2708
2709 let results = provider.call_many(&bundles).context(&context).await.unwrap();
2710
2711 let tx1_res = EthCallResponse {
2712 value: Some(
2713 hex!("0000000000000000000000000000000000000000000000000000000000000001").into(),
2714 ),
2715 error: None,
2716 };
2717 let tx2_res = EthCallResponse { value: Some(Bytes::new()), error: None };
2718 let expected = vec![vec![tx1_res.clone(), tx2_res.clone()]];
2719
2720 assert_eq!(results, expected);
2721
2722 let bundles = vec![
2724 Bundle {
2725 transactions: vec![tx1.clone()],
2726 block_override: Some(block_override.clone()),
2727 },
2728 Bundle {
2729 transactions: vec![tx2.clone()],
2730 block_override: Some(block_override.clone()),
2731 },
2732 ];
2733
2734 let results = provider.call_many(&bundles).context(&context).await.unwrap();
2735 let expected = vec![vec![tx1_res.clone()], vec![tx2_res.clone()]];
2736 assert_eq!(results, expected);
2737
2738 let b1 =
2740 vec![Bundle { transactions: vec![tx1], block_override: Some(block_override.clone()) }];
2741 let b2 = vec![Bundle { transactions: vec![tx2], block_override: Some(block_override) }];
2742
2743 let results = provider.call_many(&b1).context(&context).extend_bundles(&b2).await.unwrap();
2744 assert_eq!(results, expected);
2745 }
2746
2747 #[tokio::test]
2748 #[cfg(feature = "hyper-tls")]
2749 async fn hyper_https() {
2750 let url = "https://ethereum.reth.rs/rpc";
2751
2752 let provider = ProviderBuilder::new().connect(url).await.unwrap();
2755
2756 let _num = provider.get_block_number().await.unwrap();
2757 }
2758
2759 #[tokio::test]
2760 async fn test_empty_transactions() {
2761 let provider = ProviderBuilder::new().connect_anvil();
2762
2763 let block = provider.get_block_by_number(0.into()).await.unwrap().unwrap();
2764 assert!(block.transactions.is_hashes());
2765 }
2766
2767 #[tokio::test]
2768 async fn disable_test() {
2769 let provider = ProviderBuilder::new()
2770 .disable_recommended_fillers()
2771 .with_cached_nonce_management()
2772 .connect_anvil();
2773
2774 let tx = TransactionRequest::default()
2775 .with_kind(alloy_primitives::TxKind::Create)
2776 .value(U256::from(1235))
2777 .with_input(Bytes::from_str("ffffffffffffff").unwrap());
2778
2779 let err = provider.send_transaction(tx).await.unwrap_err().to_string();
2780 assert!(err.contains("missing properties: [(\"NonceManager\", [\"from\"])]"));
2781 }
2782
2783 #[tokio::test]
2784 async fn capture_anvil_logs() {
2785 let mut anvil = Anvil::new().keep_stdout().spawn();
2786
2787 let provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
2788
2789 let tx = TransactionRequest::default()
2790 .with_from(address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"))
2791 .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2792 .value(U256::from(100));
2793
2794 let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
2795
2796 anvil.child_mut().kill().unwrap();
2797
2798 let mut output = String::new();
2799 anvil.child_mut().stdout.take().unwrap().read_to_string(&mut output).unwrap();
2800
2801 assert_eq!(anvil.chain_id(), 31337);
2802 assert_eq!(anvil.addresses().len(), 10);
2803 assert_eq!(anvil.keys().len(), 10);
2804
2805 assert!(output.contains("eth_sendTransaction"));
2806 assert!(output.contains("Block Number: 1"))
2807 }
2808
2809 #[tokio::test]
2810 async fn custom_estimator() {
2811 let provider = ProviderBuilder::new()
2812 .disable_recommended_fillers()
2813 .with_cached_nonce_management()
2814 .connect_anvil();
2815
2816 let _ = provider
2817 .estimate_eip1559_fees_with(Eip1559Estimator::new(|_fee, _rewards| Eip1559Estimation {
2818 max_fee_per_gas: 0,
2819 max_priority_fee_per_gas: 0,
2820 }))
2821 .await;
2822 }
2823
2824 #[tokio::test]
2825 #[cfg(not(windows))]
2826 async fn eth_sign_transaction() {
2827 async_ci_only(|| async {
2828 run_with_tempdir("reth-sign-tx", |dir| async {
2829 let reth = Reth::new().dev().disable_discovery().data_dir(dir).spawn();
2830 let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
2831
2832 let accounts = provider.get_accounts().await.unwrap();
2833 let from = accounts[0];
2834
2835 let tx = TransactionRequest::default()
2836 .from(from)
2837 .to(Address::random())
2838 .value(U256::from(100))
2839 .gas_limit(21000);
2840
2841 let signed_tx = provider.sign_transaction(tx).await.unwrap().to_vec();
2842
2843 let tx = TxEnvelope::decode(&mut signed_tx.as_slice()).unwrap();
2844
2845 let signer = tx.recover_signer().unwrap();
2846
2847 assert_eq!(signer, from);
2848 })
2849 .await
2850 })
2851 .await;
2852 }
2853
2854 #[cfg(feature = "throttle")]
2855 use alloy_transport::layers::ThrottleLayer;
2856
2857 #[cfg(feature = "throttle")]
2858 #[tokio::test]
2859 async fn test_throttled_provider() {
2860 let request_per_second = 10;
2861 let throttle_layer = ThrottleLayer::new(request_per_second);
2862
2863 let anvil = Anvil::new().spawn();
2864 let client = RpcClient::builder().layer(throttle_layer).http(anvil.endpoint_url());
2865 let provider = RootProvider::<Ethereum>::new(client);
2866
2867 let num_requests = 10;
2868 let start = std::time::Instant::now();
2869 for _ in 0..num_requests {
2870 provider.get_block_number().await.unwrap();
2871 }
2872
2873 let elapsed = start.elapsed();
2874 assert_eq!(elapsed.as_secs_f64().round() as u32, 1);
2875 }
2876
2877 #[tokio::test]
2878 #[cfg(feature = "hyper")]
2879 async fn test_connect_hyper_tls() {
2880 let p = ProviderBuilder::new().connect("https://ethereum.reth.rs/rpc").await.unwrap();
2881
2882 let _num = p.get_block_number().await.unwrap();
2883
2884 let anvil = Anvil::new().spawn();
2885 let p = ProviderBuilder::new().connect(&anvil.endpoint()).await.unwrap();
2886
2887 let _num = p.get_block_number().await.unwrap();
2888 }
2889
2890 #[tokio::test]
2891 async fn test_send_transaction_sync() {
2892 use alloy_network::TransactionBuilder;
2893 use alloy_primitives::{address, U256};
2894
2895 let anvil = Anvil::new().spawn();
2896 let provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
2897
2898 let tx = TransactionRequest::default()
2899 .with_from(address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"))
2900 .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2901 .with_value(U256::from(100));
2902
2903 let receipt = provider.send_transaction_sync(tx).await.unwrap();
2905
2906 let tx_hash = receipt.transaction_hash;
2908 assert!(!tx_hash.is_zero());
2909 assert_eq!(receipt.transaction_hash, tx_hash);
2910 assert!(receipt.status());
2911 }
2912
2913 #[tokio::test]
2914 async fn test_send_transaction_sync_with_fillers() {
2915 use alloy_network::TransactionBuilder;
2916 use alloy_primitives::{address, U256};
2917
2918 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2919
2920 let tx = TransactionRequest::default()
2922 .with_from(provider.default_signer_address())
2923 .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2924 .with_value(U256::from(100));
2925 let receipt = provider.send_transaction_sync(tx).await.unwrap();
2929
2930 let tx_hash = receipt.transaction_hash;
2932 assert!(!tx_hash.is_zero());
2933
2934 assert_eq!(receipt.transaction_hash, tx_hash);
2936 assert!(receipt.status());
2937 assert!(receipt.gas_used() > 0, "fillers should have estimated gas");
2938 }
2939
2940 #[tokio::test]
2941 async fn test_fill_transaction() {
2942 use alloy_network::TransactionBuilder;
2943 use alloy_primitives::{address, U256};
2944
2945 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2946
2947 let tx = TransactionRequest::default()
2948 .with_from(provider.default_signer_address())
2949 .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2950 .with_value(U256::from(100));
2951
2952 let filled = provider.fill_transaction(tx).await.unwrap();
2953
2954 assert!(!filled.raw.is_empty(), "raw transaction bytes should not be empty");
2956
2957 let filled_tx = &filled.tx;
2959 assert!(filled_tx.to().is_some(), "filled transaction should have to address");
2960 assert!(filled_tx.gas_limit() > 0, "filled transaction should have gas limit");
2961 assert!(filled_tx.max_fee_per_gas() > 0, "filled transaction should have max fee per gas");
2962 }
2963}