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")]
185 #[doc(alias = "call_with_overrides")]
186 fn call(&self, tx: N::TransactionRequest) -> EthCall<N, Bytes> {
187 EthCall::call(self.weak_client(), tx).block(BlockNumberOrTag::Pending.into())
188 }
189
190 #[doc(alias = "eth_callMany")]
199 fn call_many<'req>(
200 &self,
201 bundles: &'req [Bundle],
202 ) -> EthCallMany<'req, N, Vec<Vec<EthCallResponse>>> {
203 EthCallMany::new(self.weak_client(), bundles)
204 }
205
206 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
212 fn multicall(&self) -> MulticallBuilder<Empty, &Self, N>
213 where
214 Self: Sized,
215 {
216 MulticallBuilder::new(self)
217 }
218
219 #[doc(alias = "eth_simulateV1")]
223 fn simulate<'req>(
224 &self,
225 payload: &'req SimulatePayload,
226 ) -> RpcWithBlock<&'req SimulatePayload, Vec<SimulatedBlock<N::BlockResponse>>> {
227 self.client().request("eth_simulateV1", payload).into()
228 }
229
230 fn get_chain_id(&self) -> ProviderCall<NoParams, U64, u64> {
232 self.client()
233 .request_noparams("eth_chainId")
234 .map_resp(utils::convert_u64 as fn(U64) -> u64)
235 .into()
236 }
237
238 fn create_access_list<'a>(
242 &self,
243 request: &'a N::TransactionRequest,
244 ) -> RpcWithBlock<&'a N::TransactionRequest, AccessListResult> {
245 self.client().request("eth_createAccessList", request).into()
246 }
247
248 fn estimate_gas(&self, tx: N::TransactionRequest) -> EthCall<N, U64, u64> {
262 EthCall::gas_estimate(self.weak_client(), tx)
263 .block(BlockNumberOrTag::Pending.into())
264 .map_resp(utils::convert_u64)
265 }
266
267 async fn estimate_eip1559_fees_with(
274 &self,
275 estimator: Eip1559Estimator,
276 ) -> TransportResult<Eip1559Estimation> {
277 let fee_history = self
278 .get_fee_history(
279 utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
280 BlockNumberOrTag::Latest,
281 &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
282 )
283 .await?;
284
285 let base_fee_per_gas = match fee_history.latest_block_base_fee() {
288 Some(base_fee) if base_fee != 0 => base_fee,
289 _ => {
290 self.get_block_by_number(BlockNumberOrTag::Latest)
292 .await?
293 .ok_or(RpcError::NullResp)?
294 .header()
295 .as_ref()
296 .base_fee_per_gas()
297 .ok_or(RpcError::UnsupportedFeature("eip1559"))?
298 .into()
299 }
300 };
301
302 Ok(estimator.estimate(base_fee_per_gas, &fee_history.reward.unwrap_or_default()))
303 }
304
305 async fn estimate_eip1559_fees(&self) -> TransportResult<Eip1559Estimation> {
311 self.estimate_eip1559_fees_with(Eip1559Estimator::default()).await
312 }
313
314 async fn get_fee_history(
320 &self,
321 block_count: u64,
322 last_block: BlockNumberOrTag,
323 reward_percentiles: &[f64],
324 ) -> TransportResult<FeeHistory> {
325 self.client()
326 .request("eth_feeHistory", (U64::from(block_count), last_block, reward_percentiles))
327 .await
328 }
329
330 fn get_gas_price(&self) -> ProviderCall<NoParams, U128, u128> {
332 self.client()
333 .request_noparams("eth_gasPrice")
334 .map_resp(utils::convert_u128 as fn(U128) -> u128)
335 .into()
336 }
337
338 fn get_account_info(
344 &self,
345 address: Address,
346 ) -> RpcWithBlock<Address, alloy_rpc_types_eth::AccountInfo> {
347 self.client().request("eth_getAccountInfo", address).into()
348 }
349
350 fn get_account(&self, address: Address) -> RpcWithBlock<Address, alloy_consensus::TrieAccount> {
353 self.client().request("eth_getAccount", address).into()
354 }
355
356 fn get_balance(&self, address: Address) -> RpcWithBlock<Address, U256, U256> {
360 self.client().request("eth_getBalance", address).into()
361 }
362
363 fn get_block(&self, block: BlockId) -> EthGetBlock<N::BlockResponse> {
374 match block {
375 BlockId::Hash(hash) => EthGetBlock::by_hash(hash.block_hash, self.client()),
376 BlockId::Number(number) => EthGetBlock::by_number(number, self.client()),
377 }
378 }
379
380 fn get_block_by_hash(&self, hash: BlockHash) -> EthGetBlock<N::BlockResponse> {
405 EthGetBlock::by_hash(hash, self.client())
406 }
407
408 fn get_block_by_number(&self, number: BlockNumberOrTag) -> EthGetBlock<N::BlockResponse> {
433 EthGetBlock::by_number(number, self.client())
434 }
435
436 async fn get_block_transaction_count_by_hash(
438 &self,
439 hash: BlockHash,
440 ) -> TransportResult<Option<u64>> {
441 self.client()
442 .request("eth_getBlockTransactionCountByHash", (hash,))
443 .await
444 .map(|opt_count: Option<U64>| opt_count.map(|count| count.to::<u64>()))
445 }
446
447 async fn get_block_transaction_count_by_number(
449 &self,
450 block_number: BlockNumberOrTag,
451 ) -> TransportResult<Option<u64>> {
452 self.client()
453 .request("eth_getBlockTransactionCountByNumber", (block_number,))
454 .await
455 .map(|opt_count: Option<U64>| opt_count.map(|count| count.to::<u64>()))
456 }
457
458 fn get_block_receipts(
460 &self,
461 block: BlockId,
462 ) -> ProviderCall<(BlockId,), Option<Vec<N::ReceiptResponse>>> {
463 self.client().request("eth_getBlockReceipts", (block,)).into()
464 }
465
466 async fn get_block_access_list(
470 &self,
471 block: BlockId,
472 ) -> TransportResult<Option<BlockAccessList>> {
473 match block {
474 BlockId::Hash(hash) => self.get_block_access_list_by_hash(hash.block_hash).await,
475 BlockId::Number(number) => self.get_block_access_list_by_number(number).await,
476 }
477 }
478
479 async fn get_block_access_list_by_hash(
483 &self,
484 hash: BlockHash,
485 ) -> TransportResult<Option<BlockAccessList>> {
486 self.client().request("eth_getBlockAccessListByBlockHash", (hash,)).await
487 }
488
489 async fn get_block_access_list_by_number(
493 &self,
494 number: BlockNumberOrTag,
495 ) -> TransportResult<Option<BlockAccessList>> {
496 self.client().request("eth_getBlockAccessListByBlockNumber", (number,)).await
497 }
498
499 async fn get_block_access_list_raw(&self, block: BlockId) -> TransportResult<Option<Bytes>> {
503 self.client().request("eth_getBlockAccessListRaw", (block,)).await
504 }
505
506 async fn get_header(&self, block: BlockId) -> TransportResult<Option<N::HeaderResponse>> {
527 match block {
528 BlockId::Hash(hash) => self.get_header_by_hash(hash.block_hash).await,
529 BlockId::Number(number) => self.get_header_by_number(number).await,
530 }
531 }
532
533 async fn get_header_by_hash(
552 &self,
553 hash: BlockHash,
554 ) -> TransportResult<Option<N::HeaderResponse>> {
555 match self.client().request("eth_getHeaderByHash", (hash,)).await {
556 Ok(header) => Ok(header),
557 Err(err) if err.as_error_resp().is_some_and(|e| e.code == -32601) => {
559 Ok(self.get_block_by_hash(hash).await?.map(|b| b.header().clone()))
560 }
561 Err(err) => Err(err),
562 }
563 }
564
565 async fn get_header_by_number(
586 &self,
587 number: BlockNumberOrTag,
588 ) -> TransportResult<Option<N::HeaderResponse>> {
589 match self.client().request("eth_getHeaderByNumber", (number,)).await {
590 Ok(header) => Ok(header),
591 Err(err) if err.as_error_resp().is_some_and(|e| e.code == -32601) => {
593 Ok(self.get_block_by_number(number).await?.map(|b| b.header().clone()))
594 }
595 Err(err) => Err(err),
596 }
597 }
598
599 fn get_code_at(&self, address: Address) -> RpcWithBlock<Address, Bytes> {
601 self.client().request("eth_getCode", address).into()
602 }
603
604 async fn watch_blocks(&self) -> TransportResult<FilterPollerBuilder<B256>> {
627 let id = self.new_block_filter().await?;
628 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
629 }
630
631 async fn watch_full_blocks(&self) -> TransportResult<WatchBlocks<N::BlockResponse>> {
655 let id = self.new_block_filter().await?;
656 let poller = PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,));
657
658 Ok(WatchBlocks::new(poller))
659 }
660
661 async fn watch_headers(&self) -> TransportResult<WatchHeaders<N::HeaderResponse>> {
688 let id = self.new_block_filter().await?;
689 let poller = PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,));
690
691 Ok(WatchHeaders::new(poller))
692 }
693
694 async fn watch_pending_transactions(&self) -> TransportResult<FilterPollerBuilder<B256>> {
717 let id = self.new_pending_transactions_filter(false).await?;
718 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
719 }
720
721 async fn watch_logs(&self, filter: &Filter) -> TransportResult<FilterPollerBuilder<Log>> {
750 let id = self.new_filter(filter).await?;
751 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
752 }
753
754 fn watch_blocks_from(&self, start_block: u64) -> WatchBlocksFrom<N> {
821 WatchBlocksFrom::new(self.weak_client(), start_block)
822 }
823
824 fn watch_canonical_blocks_from(&self, start_block: u64) -> WatchCanonicalBlocksFrom<N> {
877 self.watch_blocks_from(start_block).canonical()
878 }
879
880 fn watch_logs_from(&self, start_block: u64, filter: &Filter) -> WatchLogsFrom<N> {
923 WatchLogsFrom::new(self.weak_client(), start_block, filter.clone())
924 }
925
926 fn watch_canonical_logs_from(
973 &self,
974 start_block: u64,
975 filter: &Filter,
976 ) -> WatchCanonicalLogsFrom<N> {
977 self.watch_logs_from(start_block, filter).canonical()
978 }
979
980 async fn watch_full_pending_transactions(
1007 &self,
1008 ) -> TransportResult<FilterPollerBuilder<N::TransactionResponse>> {
1009 let id = self.new_pending_transactions_filter(true).await?;
1010 Ok(PollerBuilder::new(self.weak_client(), "eth_getFilterChanges", (id,)))
1011 }
1012
1013 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
1018 async fn get_filter_changes<R: RpcRecv>(&self, id: U256) -> TransportResult<Vec<R>>
1019 where
1020 Self: Sized,
1021 {
1022 self.client().request("eth_getFilterChanges", (id,)).await
1023 }
1024
1025 async fn get_filter_changes_dyn(&self, id: U256) -> TransportResult<FilterChanges> {
1030 self.client().request("eth_getFilterChanges", (id,)).await
1031 }
1032
1033 async fn get_filter_logs(&self, id: U256) -> TransportResult<Vec<Log>> {
1035 self.client().request("eth_getFilterLogs", (id,)).await
1036 }
1037
1038 async fn uninstall_filter(&self, id: U256) -> TransportResult<bool> {
1040 self.client().request("eth_uninstallFilter", (id,)).await
1041 }
1042
1043 #[inline]
1048 async fn watch_pending_transaction(
1049 &self,
1050 config: PendingTransactionConfig,
1051 ) -> Result<PendingTransaction, PendingTransactionError> {
1052 self.root().watch_pending_transaction(config).await
1053 }
1054
1055 async fn get_logs(&self, filter: &Filter) -> TransportResult<Vec<Log>> {
1057 self.client().request("eth_getLogs", (filter,)).await
1058 }
1059
1060 fn get_proof(
1064 &self,
1065 address: Address,
1066 keys: Vec<StorageKey>,
1067 ) -> RpcWithBlock<(Address, Vec<StorageKey>), EIP1186AccountProofResponse> {
1068 self.client().request("eth_getProof", (address, keys)).into()
1069 }
1070
1071 fn get_storage_at(
1073 &self,
1074 address: Address,
1075 key: U256,
1076 ) -> RpcWithBlock<(Address, U256), StorageValue> {
1077 self.client().request("eth_getStorageAt", (address, key)).into()
1078 }
1079
1080 fn get_storage_values(
1084 &self,
1085 requests: StorageValuesRequest,
1086 ) -> RpcWithBlock<(StorageValuesRequest,), StorageValuesResponse> {
1087 self.client().request("eth_getStorageValues", (requests,)).into()
1088 }
1089
1090 fn get_transaction_by_sender_nonce(
1094 &self,
1095 sender: Address,
1096 nonce: u64,
1097 ) -> ProviderCall<(Address, U64), Option<N::TransactionResponse>> {
1098 self.client()
1099 .request("eth_getTransactionBySenderAndNonce", (sender, U64::from(nonce)))
1100 .into()
1101 }
1102
1103 fn get_transaction_by_hash(
1105 &self,
1106 hash: TxHash,
1107 ) -> ProviderCall<(TxHash,), Option<N::TransactionResponse>> {
1108 self.client().request("eth_getTransactionByHash", (hash,)).into()
1109 }
1110
1111 fn get_transaction_by_block_hash_and_index(
1113 &self,
1114 block_hash: B256,
1115 index: usize,
1116 ) -> ProviderCall<(B256, Index), Option<N::TransactionResponse>> {
1117 self.client()
1118 .request("eth_getTransactionByBlockHashAndIndex", (block_hash, Index(index)))
1119 .into()
1120 }
1121
1122 fn get_raw_transaction_by_block_hash_and_index(
1124 &self,
1125 block_hash: B256,
1126 index: usize,
1127 ) -> ProviderCall<(B256, Index), Option<Bytes>> {
1128 self.client()
1129 .request("eth_getRawTransactionByBlockHashAndIndex", (block_hash, Index(index)))
1130 .into()
1131 }
1132
1133 fn get_transaction_by_block_number_and_index(
1135 &self,
1136 block_number: BlockNumberOrTag,
1137 index: usize,
1138 ) -> ProviderCall<(BlockNumberOrTag, Index), Option<N::TransactionResponse>> {
1139 self.client()
1140 .request("eth_getTransactionByBlockNumberAndIndex", (block_number, Index(index)))
1141 .into()
1142 }
1143
1144 fn get_raw_transaction_by_block_number_and_index(
1146 &self,
1147 block_number: BlockNumberOrTag,
1148 index: usize,
1149 ) -> ProviderCall<(BlockNumberOrTag, Index), Option<Bytes>> {
1150 self.client()
1151 .request("eth_getRawTransactionByBlockNumberAndIndex", (block_number, Index(index)))
1152 .into()
1153 }
1154
1155 fn get_raw_transaction_by_hash(&self, hash: TxHash) -> ProviderCall<(TxHash,), Option<Bytes>> {
1167 self.client().request("eth_getRawTransactionByHash", (hash,)).into()
1168 }
1169
1170 #[doc(alias = "get_nonce")]
1172 #[doc(alias = "get_account_nonce")]
1173 fn get_transaction_count(
1174 &self,
1175 address: Address,
1176 ) -> RpcWithBlock<Address, U64, u64, fn(U64) -> u64> {
1177 self.client()
1178 .request("eth_getTransactionCount", address)
1179 .map_resp(utils::convert_u64 as fn(U64) -> u64)
1180 .into()
1181 }
1182
1183 fn get_transaction_receipt(
1185 &self,
1186 hash: TxHash,
1187 ) -> ProviderCall<(TxHash,), Option<N::ReceiptResponse>> {
1188 self.client().request("eth_getTransactionReceipt", (hash,)).into()
1189 }
1190
1191 async fn get_uncle(&self, tag: BlockId, idx: u64) -> TransportResult<Option<N::BlockResponse>> {
1193 let idx = U64::from(idx);
1194 match tag {
1195 BlockId::Hash(hash) => {
1196 self.client()
1197 .request("eth_getUncleByBlockHashAndIndex", (hash.block_hash, idx))
1198 .await
1199 }
1200 BlockId::Number(number) => {
1201 self.client().request("eth_getUncleByBlockNumberAndIndex", (number, idx)).await
1202 }
1203 }
1204 }
1205
1206 async fn get_uncle_count(&self, tag: BlockId) -> TransportResult<u64> {
1208 match tag {
1209 BlockId::Hash(hash) => self
1210 .client()
1211 .request("eth_getUncleCountByBlockHash", (hash.block_hash,))
1212 .await
1213 .map(|count: U64| count.to::<u64>()),
1214 BlockId::Number(number) => self
1215 .client()
1216 .request("eth_getUncleCountByBlockNumber", (number,))
1217 .await
1218 .map(|count: U64| count.to::<u64>()),
1219 }
1220 }
1221
1222 fn get_max_priority_fee_per_gas(&self) -> ProviderCall<NoParams, U128, u128> {
1224 self.client()
1225 .request_noparams("eth_maxPriorityFeePerGas")
1226 .map_resp(utils::convert_u128 as fn(U128) -> u128)
1227 .into()
1228 }
1229
1230 async fn new_block_filter(&self) -> TransportResult<U256> {
1236 self.client().request_noparams("eth_newBlockFilter").await
1237 }
1238
1239 async fn new_filter(&self, filter: &Filter) -> TransportResult<U256> {
1245 self.client().request("eth_newFilter", (filter,)).await
1246 }
1247
1248 async fn new_pending_transactions_filter(&self, full: bool) -> TransportResult<U256> {
1258 let param = if full { &[true][..] } else { &[] };
1260 self.client().request("eth_newPendingTransactionFilter", param).await
1261 }
1262
1263 async fn send_raw_transaction(
1267 &self,
1268 encoded_tx: &[u8],
1269 ) -> TransportResult<PendingTransactionBuilder<N>> {
1270 let rlp_hex = hex::encode_prefixed(encoded_tx);
1271 let tx_hash = self.client().request("eth_sendRawTransaction", (rlp_hex,)).await?;
1272 Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
1273 }
1274
1275 async fn send_raw_transaction_sync(
1313 &self,
1314 encoded_tx: &[u8],
1315 ) -> TransportResult<N::ReceiptResponse> {
1316 let rlp_hex = hex::encode_prefixed(encoded_tx);
1317 self.client().request("eth_sendRawTransactionSync", (rlp_hex,)).await
1318 }
1319
1320 async fn send_raw_transaction_conditional(
1331 &self,
1332 encoded_tx: &[u8],
1333 conditional: TransactionConditional,
1334 ) -> TransportResult<PendingTransactionBuilder<N>> {
1335 let rlp_hex = hex::encode_prefixed(encoded_tx);
1336 let tx_hash = self
1337 .client()
1338 .request("eth_sendRawTransactionConditional", (rlp_hex, conditional))
1339 .await?;
1340 Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
1341 }
1342
1343 async fn send_transaction(
1371 &self,
1372 tx: N::TransactionRequest,
1373 ) -> TransportResult<PendingTransactionBuilder<N>> {
1374 self.send_transaction_internal(SendableTx::Builder(tx)).await
1375 }
1376
1377 async fn send_tx_envelope(
1382 &self,
1383 tx: N::TxEnvelope,
1384 ) -> TransportResult<PendingTransactionBuilder<N>> {
1385 self.send_transaction_internal(SendableTx::Envelope(tx)).await
1386 }
1387
1388 #[doc(hidden)]
1395 async fn send_transaction_internal(
1396 &self,
1397 tx: SendableTx<N>,
1398 ) -> TransportResult<PendingTransactionBuilder<N>> {
1399 let _handle = self.root().get_heart();
1402
1403 match tx {
1404 SendableTx::Builder(mut tx) => {
1405 alloy_network::NetworkTransactionBuilder::prep_for_submission(&mut tx);
1406 let tx_hash = self.client().request("eth_sendTransaction", (tx,)).await?;
1407 Ok(PendingTransactionBuilder::new(self.root().clone(), tx_hash))
1408 }
1409 SendableTx::Envelope(tx) => {
1410 let encoded_tx = tx.encoded_2718();
1411 self.send_raw_transaction(&encoded_tx).await
1412 }
1413 }
1414 }
1415
1416 async fn send_transaction_sync(
1456 &self,
1457 tx: N::TransactionRequest,
1458 ) -> TransportResult<N::ReceiptResponse> {
1459 self.send_transaction_sync_internal(SendableTx::Builder(tx)).await
1460 }
1461
1462 #[doc(hidden)]
1472 async fn send_transaction_sync_internal(
1473 &self,
1474 tx: SendableTx<N>,
1475 ) -> TransportResult<N::ReceiptResponse> {
1476 let _handle = self.root().get_heart();
1479
1480 match tx {
1481 SendableTx::Builder(mut tx) => {
1482 alloy_network::NetworkTransactionBuilder::prep_for_submission(&mut tx);
1483 let receipt = self.client().request("eth_sendTransactionSync", (tx,)).await?;
1484 Ok(receipt)
1485 }
1486 SendableTx::Envelope(tx) => {
1487 let encoded_tx = tx.encoded_2718();
1488 self.send_raw_transaction_sync(&encoded_tx).await
1489 }
1490 }
1491 }
1492
1493 async fn sign_transaction(&self, tx: N::TransactionRequest) -> TransportResult<Bytes> {
1498 self.client().request("eth_signTransaction", (tx,)).await
1499 }
1500
1501 async fn fill_transaction(
1507 &self,
1508 tx: N::TransactionRequest,
1509 ) -> TransportResult<FillTransaction<N::TxEnvelope>>
1510 where
1511 N::TxEnvelope: RpcRecv,
1512 {
1513 self.client().request("eth_fillTransaction", (tx,)).await
1514 }
1515
1516 #[cfg(feature = "pubsub")]
1542 fn subscribe_blocks(&self) -> GetSubscription<(SubscriptionKind,), N::HeaderResponse> {
1543 let rpc_call = self.client().request("eth_subscribe", (SubscriptionKind::NewHeads,));
1544 GetSubscription::new(self.weak_client(), rpc_call)
1545 }
1546
1547 #[cfg(feature = "pubsub")]
1571 fn subscribe_full_blocks(&self) -> SubFullBlocks<N> {
1572 SubFullBlocks::new(self.subscribe_blocks(), self.weak_client())
1573 }
1574
1575 #[cfg(feature = "pubsub")]
1601 fn subscribe_pending_transactions(&self) -> GetSubscription<(SubscriptionKind,), B256> {
1602 let rpc_call =
1603 self.client().request("eth_subscribe", (SubscriptionKind::NewPendingTransactions,));
1604 GetSubscription::new(self.weak_client(), rpc_call)
1605 }
1606
1607 #[cfg(feature = "pubsub")]
1638 fn subscribe_full_pending_transactions(
1639 &self,
1640 ) -> GetSubscription<(SubscriptionKind, Params), N::TransactionResponse> {
1641 let rpc_call = self.client().request(
1642 "eth_subscribe",
1643 (SubscriptionKind::NewPendingTransactions, Params::Bool(true)),
1644 );
1645 GetSubscription::new(self.weak_client(), rpc_call)
1646 }
1647
1648 #[cfg(feature = "pubsub")]
1679 fn subscribe_logs(&self, filter: &Filter) -> GetSubscription<(SubscriptionKind, Params), Log> {
1680 let rpc_call = self.client().request(
1681 "eth_subscribe",
1682 (SubscriptionKind::Logs, Params::Logs(Box::new(filter.clone()))),
1683 );
1684 GetSubscription::new(self.weak_client(), rpc_call)
1685 }
1686
1687 #[cfg(feature = "pubsub")]
1689 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
1690 fn subscribe<P, R>(&self, params: P) -> GetSubscription<P, R>
1691 where
1692 P: RpcSend,
1693 R: RpcRecv,
1694 Self: Sized,
1695 {
1696 let rpc_call = self.client().request("eth_subscribe", params);
1697 GetSubscription::new(self.weak_client(), rpc_call)
1698 }
1699
1700 #[cfg(feature = "pubsub")]
1721 #[auto_impl(keep_default_for(&, &mut, Rc, Arc, Box))]
1722 fn subscribe_to<R>(&self, method: &'static str) -> GetSubscription<NoParams, R>
1723 where
1724 R: RpcRecv,
1725 Self: Sized,
1726 {
1727 let mut rpc_call = self.client().request_noparams(method);
1728 rpc_call.set_is_subscription();
1729 GetSubscription::new(self.weak_client(), rpc_call)
1730 }
1731
1732 #[cfg(feature = "pubsub")]
1734 async fn unsubscribe(&self, id: B256) -> TransportResult<()> {
1735 self.root().unsubscribe(id)
1736 }
1737
1738 fn syncing(&self) -> ProviderCall<NoParams, SyncStatus> {
1740 self.client().request_noparams("eth_syncing").into()
1741 }
1742
1743 #[doc(alias = "web3_client_version")]
1745 fn get_client_version(&self) -> ProviderCall<NoParams, String> {
1746 self.client().request_noparams("web3_clientVersion").into()
1747 }
1748
1749 #[doc(alias = "web3_sha3")]
1751 fn get_sha3(&self, data: &[u8]) -> ProviderCall<(String,), B256> {
1752 self.client().request("web3_sha3", (hex::encode_prefixed(data),)).into()
1753 }
1754
1755 fn get_net_version(&self) -> ProviderCall<NoParams, U64, u64> {
1757 self.client()
1758 .request_noparams("net_version")
1759 .map_resp(utils::convert_u64 as fn(U64) -> u64)
1760 .into()
1761 }
1762
1763 async fn raw_request<P, R>(&self, method: Cow<'static, str>, params: P) -> TransportResult<R>
1788 where
1789 P: RpcSend,
1790 R: RpcRecv,
1791 Self: Sized,
1792 {
1793 self.client().request(method, ¶ms).await
1794 }
1795
1796 async fn raw_request_dyn(
1819 &self,
1820 method: Cow<'static, str>,
1821 params: &RawValue,
1822 ) -> TransportResult<Box<RawValue>> {
1823 self.client().request(method, params).await
1824 }
1825
1826 #[inline]
1828 fn transaction_request(&self) -> N::TransactionRequest {
1829 Default::default()
1830 }
1831}
1832
1833#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
1834#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
1835impl<N: Network> Provider<N> for RootProvider<N> {
1836 #[inline]
1837 fn root(&self) -> &Self {
1838 self
1839 }
1840
1841 #[inline]
1842 fn client(&self) -> ClientRef<'_> {
1843 self.inner.client_ref()
1844 }
1845
1846 #[inline]
1847 fn weak_client(&self) -> WeakClient {
1848 self.inner.weak_client()
1849 }
1850
1851 #[inline]
1852 async fn watch_pending_transaction(
1853 &self,
1854 config: PendingTransactionConfig,
1855 ) -> Result<PendingTransaction, PendingTransactionError> {
1856 let block_number =
1857 if let Some(receipt) = self.get_transaction_receipt(*config.tx_hash()).await? {
1858 if config.required_confirmations() <= 1 {
1860 return Ok(PendingTransaction::ready(*config.tx_hash()));
1861 }
1862 receipt.block_number()
1865 } else {
1866 None
1867 };
1868
1869 self.get_heart()
1870 .watch_tx(config, block_number)
1871 .await
1872 .map_err(|_| PendingTransactionError::FailedToRegister)
1873 }
1874}
1875
1876#[cfg(test)]
1877mod tests {
1878 use super::*;
1879 use crate::{builder, ext::test::async_ci_only, ProviderBuilder, WalletProvider};
1880 use alloy_consensus::{Transaction, TxEnvelope};
1881 use alloy_network::{
1882 AnyNetwork, EthereumWallet, NetworkTransactionBuilder, TransactionBuilder,
1883 };
1884 use alloy_node_bindings::{utils::run_with_tempdir, Anvil, Reth};
1885 use alloy_primitives::{address, b256, bytes, keccak256};
1886 use alloy_rlp::Decodable;
1887 use alloy_rpc_client::{BuiltInConnectionString, RpcClient};
1888 use alloy_rpc_types_eth::{request::TransactionRequest, Block};
1889 use alloy_signer_local::PrivateKeySigner;
1890 use alloy_transport::layers::{RetryBackoffLayer, RetryPolicy};
1891 use std::{io::Read, str::FromStr, time::Duration};
1892
1893 use alloy_consensus::transaction::SignerRecoverable;
1895 #[cfg(feature = "hyper")]
1896 use alloy_transport_http::{
1897 hyper,
1898 hyper::body::Bytes as HyperBytes,
1899 hyper_util::{
1900 client::legacy::{Client, Error},
1901 rt::TokioExecutor,
1902 },
1903 HyperResponse, HyperResponseFut,
1904 };
1905 #[cfg(feature = "hyper")]
1906 use http_body_util::Full;
1907 #[cfg(feature = "hyper")]
1908 use tower::{Layer, Service};
1909
1910 #[tokio::test]
1911 async fn test_provider_builder() {
1912 let provider =
1913 RootProvider::<Ethereum>::builder().with_recommended_fillers().connect_anvil();
1914 let num = provider.get_block_number().await.unwrap();
1915 assert_eq!(0, num);
1916 }
1917
1918 #[tokio::test]
1919 async fn test_builder_helper_fn() {
1920 let provider = builder::<Ethereum>().with_recommended_fillers().connect_anvil();
1921 let num = provider.get_block_number().await.unwrap();
1922 assert_eq!(0, num);
1923 }
1924
1925 #[cfg(feature = "hyper")]
1926 #[tokio::test]
1927 async fn test_default_hyper_transport() {
1928 let anvil = Anvil::new().spawn();
1929 let hyper_t = alloy_transport_http::HyperTransport::new_hyper(anvil.endpoint_url());
1930
1931 let rpc_client = alloy_rpc_client::RpcClient::new(hyper_t, true);
1932
1933 let provider = RootProvider::<Ethereum>::new(rpc_client);
1934 let num = provider.get_block_number().await.unwrap();
1935 assert_eq!(0, num);
1936 }
1937
1938 #[cfg(feature = "hyper")]
1939 #[tokio::test]
1940 async fn test_hyper_layer_transport() {
1941 struct LoggingLayer;
1942
1943 impl<S> Layer<S> for LoggingLayer {
1944 type Service = LoggingService<S>;
1945
1946 fn layer(&self, inner: S) -> Self::Service {
1947 LoggingService { inner }
1948 }
1949 }
1950
1951 #[derive(Clone)] struct LoggingService<S> {
1953 inner: S,
1954 }
1955
1956 impl<S, B> Service<hyper::Request<B>> for LoggingService<S>
1957 where
1958 S: Service<hyper::Request<B>, Response = HyperResponse, Error = Error>
1959 + Clone
1960 + Send
1961 + Sync
1962 + 'static,
1963 S::Future: Send,
1964 S::Error: std::error::Error + Send + Sync + 'static,
1965 B: From<Vec<u8>> + Send + 'static + Clone + Sync + std::fmt::Debug,
1966 {
1967 type Response = HyperResponse;
1968 type Error = Error;
1969 type Future = HyperResponseFut;
1970
1971 fn poll_ready(
1972 &mut self,
1973 cx: &mut std::task::Context<'_>,
1974 ) -> std::task::Poll<Result<(), Self::Error>> {
1975 self.inner.poll_ready(cx)
1976 }
1977
1978 fn call(&mut self, req: hyper::Request<B>) -> Self::Future {
1979 println!("Logging Layer - HyperRequest {req:?}");
1980
1981 let fut = self.inner.call(req);
1982
1983 Box::pin(fut)
1984 }
1985 }
1986 use http::header::{self, HeaderValue};
1987 use tower_http::{
1988 sensitive_headers::SetSensitiveRequestHeadersLayer, set_header::SetRequestHeaderLayer,
1989 };
1990 let anvil = Anvil::new().spawn();
1991 let hyper_client = Client::builder(TokioExecutor::new()).build_http::<Full<HyperBytes>>();
1992
1993 let service = tower::ServiceBuilder::new()
1995 .layer(SetRequestHeaderLayer::if_not_present(
1996 header::USER_AGENT,
1997 HeaderValue::from_static("alloy app"),
1998 ))
1999 .layer(SetRequestHeaderLayer::overriding(
2000 header::AUTHORIZATION,
2001 HeaderValue::from_static("some-jwt-token"),
2002 ))
2003 .layer(SetRequestHeaderLayer::appending(
2004 header::SET_COOKIE,
2005 HeaderValue::from_static("cookie-value"),
2006 ))
2007 .layer(SetSensitiveRequestHeadersLayer::new([header::AUTHORIZATION])) .layer(LoggingLayer)
2009 .service(hyper_client);
2010
2011 let layer_transport = alloy_transport_http::HyperClient::with_service(service);
2012
2013 let http_hyper =
2014 alloy_transport_http::Http::with_client(layer_transport, anvil.endpoint_url());
2015
2016 let rpc_client = alloy_rpc_client::RpcClient::new(http_hyper, true);
2017
2018 let provider = RootProvider::<Ethereum>::new(rpc_client);
2019 let num = provider.get_block_number().await.unwrap();
2020 assert_eq!(0, num);
2021
2022 let cloned_t = provider.client().transport().clone();
2024
2025 let rpc_client = alloy_rpc_client::RpcClient::new(cloned_t, true);
2026
2027 let provider = RootProvider::<Ethereum>::new(rpc_client);
2028 let num = provider.get_block_number().await.unwrap();
2029 assert_eq!(0, num);
2030 }
2031
2032 #[cfg(feature = "hyper")]
2033 #[tokio::test]
2034 #[cfg_attr(windows, ignore = "no reth on windows")]
2035 async fn test_auth_layer_transport() {
2036 crate::ext::test::async_ci_only(|| async move {
2037 use alloy_node_bindings::Reth;
2038 use alloy_rpc_types_engine::JwtSecret;
2039 use alloy_transport_http::{AuthLayer, Http, HyperClient};
2040
2041 let secret = JwtSecret::random();
2042
2043 let reth =
2044 Reth::new().arg("--rpc.jwtsecret").arg(hex::encode(secret.as_bytes())).spawn();
2045
2046 let layer_transport = HyperClient::new().layer(AuthLayer::new(secret));
2047
2048 let http_hyper = Http::with_client(layer_transport, reth.endpoint_url());
2049
2050 let rpc_client = alloy_rpc_client::RpcClient::new(http_hyper, true);
2051
2052 let provider = RootProvider::<Ethereum>::new(rpc_client);
2053
2054 let num = provider.get_block_number().await.unwrap();
2055 assert_eq!(0, num);
2056 })
2057 .await;
2058 }
2059
2060 #[tokio::test]
2061 async fn test_builder_helper_fn_any_network() {
2062 let anvil = Anvil::new().spawn();
2063 let provider =
2064 builder::<AnyNetwork>().with_recommended_fillers().connect_http(anvil.endpoint_url());
2065 let num = provider.get_block_number().await.unwrap();
2066 assert_eq!(0, num);
2067 }
2068
2069 #[cfg(feature = "reqwest")]
2070 #[tokio::test]
2071 async fn object_safety() {
2072 let provider = ProviderBuilder::new().connect_anvil();
2073
2074 let refdyn = &provider as &dyn Provider<_>;
2075 let num = refdyn.get_block_number().await.unwrap();
2076 assert_eq!(0, num);
2077 }
2078
2079 #[cfg(feature = "ws-base")]
2080 #[tokio::test]
2081 async fn subscribe_blocks_http() {
2082 let provider = ProviderBuilder::new().connect_anvil_with_config(|a| a.block_time(1));
2083
2084 let err = provider.subscribe_blocks().await.unwrap_err();
2085 let alloy_json_rpc::RpcError::Transport(
2086 alloy_transport::TransportErrorKind::PubsubUnavailable,
2087 ) = err
2088 else {
2089 panic!("{err:?}");
2090 };
2091 }
2092
2093 #[cfg(feature = "ws-base")]
2095 #[tokio::test]
2096 async fn websocket_tls_setup() {
2097 for url in ["wss://ethereum.reth.rs/ws"] {
2098 let _ = ProviderBuilder::<_, _, Ethereum>::default().connect(url).await.unwrap();
2099 }
2100 }
2101
2102 #[cfg(feature = "ws-base")]
2103 #[tokio::test]
2104 async fn subscribe_blocks_ws() {
2105 use futures::stream::StreamExt;
2106
2107 let anvil = Anvil::new().block_time_f64(0.2).spawn();
2108 let ws = alloy_rpc_client::WsConnect::new(anvil.ws_endpoint());
2109 let client = alloy_rpc_client::RpcClient::connect_pubsub(ws).await.unwrap();
2110 let provider = RootProvider::<Ethereum>::new(client);
2111
2112 let sub = provider.subscribe_blocks().await.unwrap();
2113 let mut stream = sub.into_stream().take(5);
2114 let mut next = None;
2115 while let Some(header) = stream.next().await {
2116 if let Some(next) = &mut next {
2117 assert_eq!(header.number, *next);
2118 *next += 1;
2119 } else {
2120 next = Some(header.number + 1);
2121 }
2122 }
2123 }
2124
2125 #[cfg(feature = "ws-base")]
2126 #[tokio::test]
2127 async fn subscribe_full_blocks() {
2128 use futures::StreamExt;
2129
2130 let anvil = Anvil::new().block_time_f64(0.2).spawn();
2131 let ws = alloy_rpc_client::WsConnect::new(anvil.ws_endpoint());
2132 let client = alloy_rpc_client::RpcClient::connect_pubsub(ws).await.unwrap();
2133
2134 let provider = RootProvider::<Ethereum>::new(client);
2135
2136 let sub = provider.subscribe_full_blocks().hashes().channel_size(10);
2137
2138 let mut stream = sub.into_stream().await.unwrap().take(5);
2139
2140 let mut next = None;
2141 while let Some(Ok(block)) = stream.next().await {
2142 if let Some(next) = &mut next {
2143 assert_eq!(block.header().number, *next);
2144 *next += 1;
2145 } else {
2146 next = Some(block.header().number + 1);
2147 }
2148 }
2149 }
2150
2151 #[tokio::test]
2152 #[cfg(feature = "ws-base")]
2153 async fn subscribe_blocks_ws_remote() {
2154 use futures::stream::StreamExt;
2155
2156 let url = "wss://eth-mainnet.g.alchemy.com/v2/viFmeVzhg6bWKVMIWWS8MhmzREB-D4f7";
2157 let ws = alloy_rpc_client::WsConnect::new(url);
2158 let Ok(client) = alloy_rpc_client::RpcClient::connect_pubsub(ws).await else { return };
2159 let provider = RootProvider::<Ethereum>::new(client);
2160 let sub = provider.subscribe_blocks().await.unwrap();
2161 let mut stream = sub.into_stream().take(1);
2162 while let Some(header) = stream.next().await {
2163 println!("New block {header:?}");
2164 assert!(header.number > 0);
2165 }
2166 }
2167
2168 #[tokio::test]
2169 async fn test_custom_retry_policy() {
2170 #[derive(Debug, Clone)]
2171 struct CustomPolicy;
2172 impl RetryPolicy for CustomPolicy {
2173 fn should_retry(&self, _err: &alloy_transport::TransportError) -> bool {
2174 true
2175 }
2176
2177 fn backoff_hint(
2178 &self,
2179 _error: &alloy_transport::TransportError,
2180 ) -> Option<std::time::Duration> {
2181 None
2182 }
2183 }
2184
2185 let retry_layer = RetryBackoffLayer::new_with_policy(10, 100, 10000, CustomPolicy);
2186 let anvil = Anvil::new().spawn();
2187 let client = RpcClient::builder().layer(retry_layer).http(anvil.endpoint_url());
2188
2189 let provider = RootProvider::<Ethereum>::new(client);
2190 let num = provider.get_block_number().await.unwrap();
2191 assert_eq!(0, num);
2192 }
2193
2194 #[tokio::test]
2195 async fn test_send_tx() {
2196 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2197 let tx = TransactionRequest {
2198 value: Some(U256::from(100)),
2199 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2200 gas_price: Some(20e9 as u128),
2201 gas: Some(21000),
2202 ..Default::default()
2203 };
2204
2205 let builder = provider.send_transaction(tx.clone()).await.expect("failed to send tx");
2206 let hash1 = *builder.tx_hash();
2207 let hash2 = builder.watch().await.expect("failed to await pending tx");
2208 assert_eq!(hash1, hash2);
2209
2210 let builder = provider.send_transaction(tx).await.expect("failed to send tx");
2211 let hash1 = *builder.tx_hash();
2212 let hash2 =
2213 builder.get_receipt().await.expect("failed to await pending tx").transaction_hash;
2214 assert_eq!(hash1, hash2);
2215 }
2216
2217 #[tokio::test]
2218 async fn test_send_tx_sync() {
2219 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2220 let tx = TransactionRequest {
2221 value: Some(U256::from(100)),
2222 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2223 gas_price: Some(20e9 as u128),
2224 gas: Some(21000),
2225 ..Default::default()
2226 };
2227
2228 let _receipt =
2229 provider.send_transaction_sync(tx.clone()).await.expect("failed to send tx sync");
2230 }
2231
2232 #[tokio::test]
2233 async fn test_send_raw_transaction_sync() {
2234 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2235
2236 let tx = TransactionRequest {
2238 nonce: Some(0),
2239 value: Some(U256::from(100)),
2240 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2241 gas_price: Some(20e9 as u128),
2242 gas: Some(21000),
2243 ..Default::default()
2244 };
2245
2246 let tx_envelope = tx.build(&provider.wallet()).await.expect("failed to build tx");
2248
2249 let encoded = tx_envelope.encoded_2718();
2251
2252 let receipt =
2254 provider.send_raw_transaction_sync(&encoded).await.expect("failed to send raw tx sync");
2255
2256 assert_eq!(receipt.to(), Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045")));
2258 assert!(receipt.block_number().is_some(), "transaction should be mined");
2260 assert!(receipt.transaction_hash() != B256::ZERO, "should have valid tx hash");
2261 }
2262
2263 #[tokio::test]
2264 async fn test_watch_confirmed_tx() {
2265 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2266 let tx = TransactionRequest {
2267 value: Some(U256::from(100)),
2268 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2269 gas_price: Some(20e9 as u128),
2270 gas: Some(21000),
2271 ..Default::default()
2272 };
2273
2274 let builder = provider.send_transaction(tx).await.expect("failed to send tx");
2275 let hash1 = *builder.tx_hash();
2276
2277 loop {
2279 if provider
2280 .get_transaction_receipt(hash1)
2281 .await
2282 .expect("failed to await pending tx")
2283 .is_some()
2284 {
2285 break;
2286 }
2287 }
2288
2289 let tx2 = TransactionRequest {
2291 value: Some(U256::from(100)),
2292 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2293 gas_price: Some(20e9 as u128),
2294 gas: Some(21000),
2295 ..Default::default()
2296 };
2297 provider.send_transaction(tx2).await.expect("failed to send tx").watch().await.unwrap();
2298
2299 let watch = builder.watch();
2301 let watch_with_timeout = tokio::time::timeout(Duration::from_secs(1), watch);
2303 let hash2 = watch_with_timeout
2304 .await
2305 .expect("Watching tx timed out")
2306 .expect("failed to await pending tx");
2307 assert_eq!(hash1, hash2);
2308 }
2309
2310 #[tokio::test]
2311 async fn gets_block_number() {
2312 let provider = ProviderBuilder::new().connect_anvil();
2313 let num = provider.get_block_number().await.unwrap();
2314 assert_eq!(0, num)
2315 }
2316
2317 #[tokio::test]
2318 async fn gets_block_number_for_id() {
2319 let provider = ProviderBuilder::new().connect_anvil();
2320
2321 let block_num = provider
2322 .get_block_number_by_id(BlockId::Number(BlockNumberOrTag::Number(0)))
2323 .await
2324 .unwrap();
2325 assert_eq!(block_num, Some(0));
2326
2327 let block_num = provider
2328 .get_block_number_by_id(BlockId::Number(BlockNumberOrTag::Latest))
2329 .await
2330 .unwrap();
2331 assert_eq!(block_num, Some(0));
2332
2333 let block =
2334 provider.get_block_by_number(BlockNumberOrTag::Number(0)).await.unwrap().unwrap();
2335 let hash = block.header.hash;
2336 let block_num = provider.get_block_number_by_id(BlockId::Hash(hash.into())).await.unwrap();
2337 assert_eq!(block_num, Some(0));
2338 }
2339
2340 #[tokio::test]
2341 async fn gets_block_number_with_raw_req() {
2342 let provider = ProviderBuilder::new().connect_anvil();
2343 let num: U64 =
2344 provider.raw_request("eth_blockNumber".into(), NoParams::default()).await.unwrap();
2345 assert_eq!(0, num.to::<u64>())
2346 }
2347
2348 #[cfg(feature = "anvil-api")]
2349 #[tokio::test]
2350 async fn gets_transaction_count() {
2351 let provider = ProviderBuilder::new().connect_anvil();
2352 let accounts = provider.get_accounts().await.unwrap();
2353 let sender = accounts[0];
2354
2355 let count = provider.get_transaction_count(sender).await.unwrap();
2357 assert_eq!(count, 0);
2358
2359 let tx = TransactionRequest {
2361 value: Some(U256::from(100)),
2362 from: Some(sender),
2363 to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()),
2364 gas_price: Some(20e9 as u128),
2365 gas: Some(21000),
2366 ..Default::default()
2367 };
2368 let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await;
2369
2370 let count = provider.get_transaction_count(sender).await.unwrap();
2372 assert_eq!(count, 1);
2373
2374 let count = provider.get_transaction_count(sender).block_id(0.into()).await.unwrap();
2376 assert_eq!(count, 0);
2377 }
2378
2379 #[tokio::test]
2380 async fn gets_block_by_hash() {
2381 let provider = ProviderBuilder::new().connect_anvil();
2382 let num = 0;
2383 let tag: BlockNumberOrTag = num.into();
2384 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
2385 let hash = block.header.hash;
2386 let block = provider.get_block_by_hash(hash).full().await.unwrap().unwrap();
2387 assert_eq!(block.header.hash, hash);
2388 }
2389
2390 #[tokio::test]
2391 async fn gets_block_by_hash_with_raw_req() {
2392 let provider = ProviderBuilder::new().connect_anvil();
2393 let num = 0;
2394 let tag: BlockNumberOrTag = num.into();
2395 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
2396 let hash = block.header.hash;
2397 let block: Block = provider
2398 .raw_request::<(B256, bool), Block>("eth_getBlockByHash".into(), (hash, true))
2399 .await
2400 .unwrap();
2401 assert_eq!(block.header.hash, hash);
2402 }
2403
2404 #[tokio::test]
2405 async fn gets_block_by_number_full() {
2406 let provider = ProviderBuilder::new().connect_anvil();
2407 let num = 0;
2408 let tag: BlockNumberOrTag = num.into();
2409 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
2410 assert_eq!(block.header.number, num);
2411 }
2412
2413 #[tokio::test]
2414 async fn gets_block_by_number() {
2415 let provider = ProviderBuilder::new().connect_anvil();
2416 let num = 0;
2417 let tag: BlockNumberOrTag = num.into();
2418 let block = provider.get_block_by_number(tag).full().await.unwrap().unwrap();
2419 assert_eq!(block.header.number, num);
2420 }
2421
2422 #[tokio::test]
2423 async fn gets_client_version() {
2424 let provider = ProviderBuilder::new().connect_anvil();
2425 let version = provider.get_client_version().await.unwrap();
2426 assert!(version.contains("anvil"), "{version}");
2427 }
2428
2429 #[tokio::test]
2430 async fn gets_sha3() {
2431 let provider = ProviderBuilder::new().connect_anvil();
2432 let data = b"alloy";
2433 let hash = provider.get_sha3(data).await.unwrap();
2434 assert_eq!(hash, keccak256(data));
2435 }
2436
2437 #[tokio::test]
2438 async fn gets_chain_id() {
2439 let dev_chain_id: u64 = 13371337;
2440
2441 let provider =
2442 ProviderBuilder::new().connect_anvil_with_config(|a| a.chain_id(dev_chain_id));
2443
2444 let chain_id = provider.get_chain_id().await.unwrap();
2445 assert_eq!(chain_id, dev_chain_id);
2446 }
2447
2448 #[tokio::test]
2449 async fn gets_network_id() {
2450 let dev_chain_id: u64 = 13371337;
2451 let provider =
2452 ProviderBuilder::new().connect_anvil_with_config(|a| a.chain_id(dev_chain_id));
2453
2454 let chain_id = provider.get_net_version().await.unwrap();
2455 assert_eq!(chain_id, dev_chain_id);
2456 }
2457
2458 #[tokio::test]
2459 async fn gets_storage_at() {
2460 let provider = ProviderBuilder::new().connect_anvil();
2461 let addr = Address::with_last_byte(16);
2462 let storage = provider.get_storage_at(addr, U256::ZERO).await.unwrap();
2463 assert_eq!(storage, U256::ZERO);
2464 }
2465
2466 #[tokio::test]
2467 async fn gets_transaction_by_hash_not_found() {
2468 let provider = ProviderBuilder::new().connect_anvil();
2469 let tx_hash = b256!("5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95");
2470 let tx = provider.get_transaction_by_hash(tx_hash).await.expect("failed to fetch tx");
2471
2472 assert!(tx.is_none());
2473 }
2474
2475 #[tokio::test]
2476 async fn gets_transaction_by_hash() {
2477 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2478
2479 let req = TransactionRequest::default()
2480 .from(provider.default_signer_address())
2481 .to(Address::repeat_byte(5))
2482 .value(U256::ZERO)
2483 .input(bytes!("deadbeef").into());
2484
2485 let tx_hash = *provider.send_transaction(req).await.expect("failed to send tx").tx_hash();
2486
2487 let tx = provider
2488 .get_transaction_by_hash(tx_hash)
2489 .await
2490 .expect("failed to fetch tx")
2491 .expect("tx not included");
2492 assert_eq!(tx.input(), &bytes!("deadbeef"));
2493 }
2494
2495 #[tokio::test]
2496 #[ignore]
2497 async fn gets_logs() {
2498 let provider = ProviderBuilder::new().connect_anvil();
2499 let filter = Filter::new()
2500 .at_block_hash(b256!(
2501 "b20e6f35d4b46b3c4cd72152faec7143da851a0dc281d390bdd50f58bfbdb5d3"
2502 ))
2503 .event_signature(b256!(
2504 "e1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c"
2505 ));
2506 let logs = provider.get_logs(&filter).await.unwrap();
2507 assert_eq!(logs.len(), 1);
2508 }
2509
2510 #[tokio::test]
2511 #[ignore]
2512 async fn gets_tx_receipt() {
2513 let provider = ProviderBuilder::new().connect_anvil();
2514 let receipt = provider
2515 .get_transaction_receipt(b256!(
2516 "5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95"
2517 ))
2518 .await
2519 .unwrap();
2520 assert!(receipt.is_some());
2521 let receipt = receipt.unwrap();
2522 assert_eq!(
2523 receipt.transaction_hash,
2524 b256!("5c03fab9114ceb98994b43892ade87ddfd9ae7e8f293935c3bd29d435dc9fd95")
2525 );
2526 }
2527
2528 #[tokio::test]
2529 async fn gets_max_priority_fee_per_gas() {
2530 let provider = ProviderBuilder::new().connect_anvil();
2531 let _fee = provider.get_max_priority_fee_per_gas().await.unwrap();
2532 }
2533
2534 #[tokio::test]
2535 async fn gets_fee_history() {
2536 let provider = ProviderBuilder::new().connect_anvil();
2537 let block_number = provider.get_block_number().await.unwrap();
2538 let fee_history = provider
2539 .get_fee_history(
2540 utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS,
2541 BlockNumberOrTag::Number(block_number),
2542 &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE],
2543 )
2544 .await
2545 .unwrap();
2546 assert_eq!(fee_history.oldest_block, 0_u64);
2547 }
2548
2549 #[tokio::test]
2550 async fn gets_block_transaction_count_by_hash() {
2551 let provider = ProviderBuilder::new().connect_anvil();
2552 let block = provider.get_block(BlockId::latest()).await.unwrap().unwrap();
2553 let hash = block.header.hash;
2554 let tx_count = provider.get_block_transaction_count_by_hash(hash).await.unwrap();
2555 assert!(tx_count.is_some());
2556 }
2557
2558 #[tokio::test]
2559 async fn gets_block_transaction_count_by_number() {
2560 let provider = ProviderBuilder::new().connect_anvil();
2561 let tx_count =
2562 provider.get_block_transaction_count_by_number(BlockNumberOrTag::Latest).await.unwrap();
2563 assert!(tx_count.is_some());
2564 }
2565
2566 #[tokio::test]
2567 async fn gets_block_receipts() {
2568 let provider = ProviderBuilder::new().connect_anvil();
2569 let receipts =
2570 provider.get_block_receipts(BlockId::Number(BlockNumberOrTag::Latest)).await.unwrap();
2571 assert!(receipts.is_some());
2572 }
2573
2574 #[tokio::test]
2575 async fn sends_raw_transaction() {
2576 let provider = ProviderBuilder::new().connect_anvil();
2577 let pending = provider
2578 .send_raw_transaction(
2579 bytes!("f865808477359400825208940000000000000000000000000000000000000000018082f4f5a00505e227c1c636c76fac55795db1a40a4d24840d81b40d2fe0cc85767f6bd202a01e91b437099a8a90234ac5af3cb7ca4fb1432e133f75f9a91678eaf5f487c74b").as_ref()
2581 )
2582 .await.unwrap();
2583 assert_eq!(
2584 pending.tx_hash().to_string(),
2585 "0x9dae5cf33694a02e8a7d5de3fe31e9d05ca0ba6e9180efac4ab20a06c9e598a3"
2586 );
2587 }
2588
2589 #[tokio::test]
2590 async fn connect_boxed() {
2591 let anvil = Anvil::new().spawn();
2592
2593 let provider = RootProvider::<Ethereum>::connect(anvil.endpoint().as_str()).await;
2594
2595 match provider {
2596 Ok(provider) => {
2597 let num = provider.get_block_number().await.unwrap();
2598 assert_eq!(0, num);
2599 }
2600 Err(e) => {
2601 assert_eq!(
2602 format!("{e}"),
2603 "hyper not supported by BuiltinConnectionString. Please instantiate a hyper client manually"
2604 );
2605 }
2606 }
2607 }
2608
2609 #[tokio::test]
2610 async fn any_network_wallet_filler() {
2611 use alloy_serde::WithOtherFields;
2612 let anvil = Anvil::new().spawn();
2613 let signer: PrivateKeySigner =
2614 "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap();
2615 let wallet = EthereumWallet::from(signer);
2616
2617 let provider = ProviderBuilder::new()
2618 .network::<AnyNetwork>()
2619 .wallet(wallet)
2620 .connect_http(anvil.endpoint_url());
2621
2622 let tx = TransactionRequest::default()
2623 .with_to(address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"))
2624 .value(U256::from(325235));
2625
2626 let tx = WithOtherFields::new(tx);
2627
2628 let builder = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
2629
2630 assert!(builder.status());
2631 }
2632
2633 #[tokio::test]
2634 async fn builtin_connect_boxed() {
2635 let anvil = Anvil::new().spawn();
2636
2637 let conn: BuiltInConnectionString = anvil.endpoint().parse().unwrap();
2638
2639 let transport = conn.connect_boxed().await.unwrap();
2640
2641 let client = alloy_rpc_client::RpcClient::new(transport, true);
2642
2643 let provider = RootProvider::<Ethereum>::new(client);
2644
2645 let num = provider.get_block_number().await.unwrap();
2646 assert_eq!(0, num);
2647 }
2648
2649 #[tokio::test]
2650 async fn test_uncle_count() {
2651 let provider = ProviderBuilder::new().connect_anvil();
2652
2653 let count = provider.get_uncle_count(0.into()).await.unwrap();
2654 assert_eq!(count, 0);
2655 }
2656
2657 #[tokio::test]
2658 #[cfg(any(
2659 feature = "reqwest-default-tls",
2660 feature = "reqwest-rustls-tls",
2661 feature = "reqwest-native-tls",
2662 ))]
2663 #[ignore = "ignore until <https://github.com/paradigmxyz/reth/pull/14727> is in"]
2664 async fn call_mainnet() {
2665 use alloy_network::TransactionBuilder;
2666 use alloy_sol_types::SolValue;
2667
2668 let url = "https://docs-demo.quiknode.pro/";
2669 let provider = ProviderBuilder::new().connect_http(url.parse().unwrap());
2670 let req = TransactionRequest::default()
2671 .with_to(address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2")) .with_input(bytes!("06fdde03")); let result = provider.call(req.clone()).await.unwrap();
2674 assert_eq!(String::abi_decode(&result).unwrap(), "Wrapped Ether");
2675
2676 let result = provider.call(req).block(0.into()).await.unwrap();
2677 assert_eq!(result.to_string(), "0x");
2678 }
2679
2680 #[tokio::test]
2681 async fn call_many_mainnet() {
2682 use alloy_rpc_types_eth::{BlockOverrides, StateContext};
2683
2684 let url = "https://docs-demo.quiknode.pro/";
2685 let provider = ProviderBuilder::new().connect_http(url.parse().unwrap());
2686 let tx1 = TransactionRequest::default()
2687 .with_to(address!("6b175474e89094c44da98b954eedeac495271d0f"))
2688 .with_gas_limit(1000000)
2689 .with_gas_price(2023155498)
2690 .with_input(hex!("a9059cbb000000000000000000000000bc0E63965946815d105E7591407704e6e1964E590000000000000000000000000000000000000000000000000000000005f5e100"));
2691 let tx2 = TransactionRequest::default()
2692 .with_to(address!("833589fcd6edb6e08f4c7c32d4f71b54bda02913"))
2693 .with_gas_price(2023155498)
2694 .with_input(hex!(
2695 "70a08231000000000000000000000000bc0E63965946815d105E7591407704e6e1964E59"
2696 ));
2697
2698 let transactions = vec![tx1.clone(), tx2.clone()];
2699
2700 let block_override =
2701 BlockOverrides { number: Some(U256::from(12279785)), ..Default::default() };
2702
2703 let bundles = vec![Bundle { transactions, block_override: Some(block_override.clone()) }];
2704
2705 let context = StateContext {
2706 block_number: Some(BlockId::number(12279785)),
2707 transaction_index: Some(1.into()),
2708 };
2709
2710 let results = provider.call_many(&bundles).context(&context).await.unwrap();
2711
2712 let tx1_res = EthCallResponse {
2713 value: Some(
2714 hex!("0000000000000000000000000000000000000000000000000000000000000001").into(),
2715 ),
2716 error: None,
2717 };
2718 let tx2_res = EthCallResponse { value: Some(Bytes::new()), error: None };
2719 let expected = vec![vec![tx1_res.clone(), tx2_res.clone()]];
2720
2721 assert_eq!(results, expected);
2722
2723 let bundles = vec![
2725 Bundle {
2726 transactions: vec![tx1.clone()],
2727 block_override: Some(block_override.clone()),
2728 },
2729 Bundle {
2730 transactions: vec![tx2.clone()],
2731 block_override: Some(block_override.clone()),
2732 },
2733 ];
2734
2735 let results = provider.call_many(&bundles).context(&context).await.unwrap();
2736 let expected = vec![vec![tx1_res.clone()], vec![tx2_res.clone()]];
2737 assert_eq!(results, expected);
2738
2739 let b1 =
2741 vec![Bundle { transactions: vec![tx1], block_override: Some(block_override.clone()) }];
2742 let b2 = vec![Bundle { transactions: vec![tx2], block_override: Some(block_override) }];
2743
2744 let results = provider.call_many(&b1).context(&context).extend_bundles(&b2).await.unwrap();
2745 assert_eq!(results, expected);
2746 }
2747
2748 #[tokio::test]
2749 #[cfg(feature = "hyper-tls")]
2750 async fn hyper_https() {
2751 let url = "https://ethereum.reth.rs/rpc";
2752
2753 let provider = ProviderBuilder::new().connect(url).await.unwrap();
2756
2757 let _num = provider.get_block_number().await.unwrap();
2758 }
2759
2760 #[tokio::test]
2761 async fn test_empty_transactions() {
2762 let provider = ProviderBuilder::new().connect_anvil();
2763
2764 let block = provider.get_block_by_number(0.into()).await.unwrap().unwrap();
2765 assert!(block.transactions.is_hashes());
2766 }
2767
2768 #[tokio::test]
2769 async fn disable_test() {
2770 let provider = ProviderBuilder::new()
2771 .disable_recommended_fillers()
2772 .with_cached_nonce_management()
2773 .connect_anvil();
2774
2775 let tx = TransactionRequest::default()
2776 .with_kind(alloy_primitives::TxKind::Create)
2777 .value(U256::from(1235))
2778 .with_input(Bytes::from_str("ffffffffffffff").unwrap());
2779
2780 let err = provider.send_transaction(tx).await.unwrap_err().to_string();
2781 assert!(err.contains("missing properties: [(\"NonceManager\", [\"from\"])]"));
2782 }
2783
2784 #[tokio::test]
2785 async fn capture_anvil_logs() {
2786 let mut anvil = Anvil::new().keep_stdout().spawn();
2787
2788 let provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
2789
2790 let tx = TransactionRequest::default()
2791 .with_from(address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"))
2792 .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2793 .value(U256::from(100));
2794
2795 let _ = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap();
2796
2797 anvil.child_mut().kill().unwrap();
2798
2799 let mut output = String::new();
2800 anvil.child_mut().stdout.take().unwrap().read_to_string(&mut output).unwrap();
2801
2802 assert_eq!(anvil.chain_id(), 31337);
2803 assert_eq!(anvil.addresses().len(), 10);
2804 assert_eq!(anvil.keys().len(), 10);
2805
2806 assert!(output.contains("eth_sendTransaction"));
2807 assert!(output.contains("Block Number: 1"))
2808 }
2809
2810 #[tokio::test]
2811 async fn custom_estimator() {
2812 let provider = ProviderBuilder::new()
2813 .disable_recommended_fillers()
2814 .with_cached_nonce_management()
2815 .connect_anvil();
2816
2817 let _ = provider
2818 .estimate_eip1559_fees_with(Eip1559Estimator::new(|_fee, _rewards| Eip1559Estimation {
2819 max_fee_per_gas: 0,
2820 max_priority_fee_per_gas: 0,
2821 }))
2822 .await;
2823 }
2824
2825 #[tokio::test]
2826 #[cfg(not(windows))]
2827 async fn eth_sign_transaction() {
2828 async_ci_only(|| async {
2829 run_with_tempdir("reth-sign-tx", |dir| async {
2830 let reth = Reth::new().dev().disable_discovery().data_dir(dir).spawn();
2831 let provider = ProviderBuilder::new().connect_http(reth.endpoint_url());
2832
2833 let accounts = provider.get_accounts().await.unwrap();
2834 let from = accounts[0];
2835
2836 let tx = TransactionRequest::default()
2837 .from(from)
2838 .to(Address::random())
2839 .value(U256::from(100))
2840 .gas_limit(21000);
2841
2842 let signed_tx = provider.sign_transaction(tx).await.unwrap().to_vec();
2843
2844 let tx = TxEnvelope::decode(&mut signed_tx.as_slice()).unwrap();
2845
2846 let signer = tx.recover_signer().unwrap();
2847
2848 assert_eq!(signer, from);
2849 })
2850 .await
2851 })
2852 .await;
2853 }
2854
2855 #[cfg(feature = "throttle")]
2856 use alloy_transport::layers::ThrottleLayer;
2857
2858 #[cfg(feature = "throttle")]
2859 #[tokio::test]
2860 async fn test_throttled_provider() {
2861 let request_per_second = 10;
2862 let throttle_layer = ThrottleLayer::new(request_per_second);
2863
2864 let anvil = Anvil::new().spawn();
2865 let client = RpcClient::builder().layer(throttle_layer).http(anvil.endpoint_url());
2866 let provider = RootProvider::<Ethereum>::new(client);
2867
2868 let num_requests = 10;
2869 let start = std::time::Instant::now();
2870 for _ in 0..num_requests {
2871 provider.get_block_number().await.unwrap();
2872 }
2873
2874 let elapsed = start.elapsed();
2875 assert_eq!(elapsed.as_secs_f64().round() as u32, 1);
2876 }
2877
2878 #[tokio::test]
2879 #[cfg(feature = "hyper")]
2880 async fn test_connect_hyper_tls() {
2881 let p = ProviderBuilder::new().connect("https://ethereum.reth.rs/rpc").await.unwrap();
2882
2883 let _num = p.get_block_number().await.unwrap();
2884
2885 let anvil = Anvil::new().spawn();
2886 let p = ProviderBuilder::new().connect(&anvil.endpoint()).await.unwrap();
2887
2888 let _num = p.get_block_number().await.unwrap();
2889 }
2890
2891 #[tokio::test]
2892 async fn test_send_transaction_sync() {
2893 use alloy_network::TransactionBuilder;
2894 use alloy_primitives::{address, U256};
2895
2896 let anvil = Anvil::new().spawn();
2897 let provider = ProviderBuilder::new().connect_http(anvil.endpoint_url());
2898
2899 let tx = TransactionRequest::default()
2900 .with_from(address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"))
2901 .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2902 .with_value(U256::from(100));
2903
2904 let receipt = provider.send_transaction_sync(tx).await.unwrap();
2906
2907 let tx_hash = receipt.transaction_hash;
2909 assert!(!tx_hash.is_zero());
2910 assert_eq!(receipt.transaction_hash, tx_hash);
2911 assert!(receipt.status());
2912 }
2913
2914 #[tokio::test]
2915 async fn test_send_transaction_sync_with_fillers() {
2916 use alloy_network::TransactionBuilder;
2917 use alloy_primitives::{address, U256};
2918
2919 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2920
2921 let tx = TransactionRequest::default()
2923 .with_from(provider.default_signer_address())
2924 .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2925 .with_value(U256::from(100));
2926 let receipt = provider.send_transaction_sync(tx).await.unwrap();
2930
2931 let tx_hash = receipt.transaction_hash;
2933 assert!(!tx_hash.is_zero());
2934
2935 assert_eq!(receipt.transaction_hash, tx_hash);
2937 assert!(receipt.status());
2938 assert!(receipt.gas_used() > 0, "fillers should have estimated gas");
2939 }
2940
2941 #[tokio::test]
2942 async fn test_fill_transaction() {
2943 use alloy_network::TransactionBuilder;
2944 use alloy_primitives::{address, U256};
2945
2946 let provider = ProviderBuilder::new().connect_anvil_with_wallet();
2947
2948 let tx = TransactionRequest::default()
2949 .with_from(provider.default_signer_address())
2950 .with_to(address!("70997970C51812dc3A010C7d01b50e0d17dc79C8"))
2951 .with_value(U256::from(100));
2952
2953 let filled = provider.fill_transaction(tx).await.unwrap();
2954
2955 assert!(!filled.raw.is_empty(), "raw transaction bytes should not be empty");
2957
2958 let filled_tx = &filled.tx;
2960 assert!(filled_tx.to().is_some(), "filled transaction should have to address");
2961 assert!(filled_tx.gas_limit() > 0, "filled transaction should have gas limit");
2962 assert!(filled_tx.max_fee_per_gas() > 0, "filled transaction should have max fee per gas");
2963 }
2964}