From e3651c9d0f6b82ebdd0515fad433a30c5ef547c3 Mon Sep 17 00:00:00 2001 From: Yash Atreya <44857776+yash-atreya@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:41:33 +0530 Subject: [PATCH] feat(`provider`): instantiate recommended fillers by default (#1901) * feat(`provider`): make recommended fillers the default * fix: event filter tests * fix * test * nit * fix: doc tests + event_filter tests + return impl Provider * fix * docs * doc nit --- crates/contract/Cargo.toml | 1 + crates/contract/README.md | 2 +- crates/contract/src/call.rs | 12 +++--- crates/contract/src/event.rs | 20 ++++++++- crates/contract/src/instance.rs | 2 +- crates/provider/src/builder.rs | 54 +++++++++++++++---------- crates/provider/src/ext/debug.rs | 5 +-- crates/provider/src/fillers/chain_id.rs | 2 +- crates/provider/src/fillers/gas.rs | 10 ++--- crates/provider/src/fillers/nonce.rs | 12 ++++-- crates/provider/src/layers/cache.rs | 10 +++-- crates/provider/src/provider/trait.rs | 25 +++++++++--- crates/provider/src/provider/wallet.rs | 4 +- crates/provider/src/utils.rs | 12 ++++++ 14 files changed, 116 insertions(+), 55 deletions(-) diff --git a/crates/contract/Cargo.toml b/crates/contract/Cargo.toml index a65be56b6d6..0d57e964d9f 100644 --- a/crates/contract/Cargo.toml +++ b/crates/contract/Cargo.toml @@ -46,6 +46,7 @@ alloy-rpc-client = { workspace = true, features = ["pubsub", "ws"] } alloy-transport-http.workspace = true alloy-node-bindings.workspace = true alloy-provider = { workspace = true, features = ["anvil-node"] } +alloy-signer-local.workspace = true reqwest.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/crates/contract/README.md b/crates/contract/README.md index dd830b68f2f..b1a468584c2 100644 --- a/crates/contract/README.md +++ b/crates/contract/README.md @@ -32,7 +32,7 @@ sol! { } // Build a provider. -let provider = ProviderBuilder::new().with_recommended_fillers().on_builtin("http://localhost:8545").await?; +let provider = ProviderBuilder::new().on_builtin("http://localhost:8545").await?; // If `#[sol(bytecode = "0x...")]` is provided, the contract can be deployed with `MyContract::deploy`, // and a new instance will be created. diff --git a/crates/contract/src/call.rs b/crates/contract/src/call.rs index fc33ee306c8..a043dc6f665 100644 --- a/crates/contract/src/call.rs +++ b/crates/contract/src/call.rs @@ -569,9 +569,7 @@ mod tests { use super::*; use alloy_consensus::Transaction; use alloy_primitives::{address, b256, bytes, hex, utils::parse_units, B256}; - use alloy_provider::{ - layers::AnvilProvider, Provider, ProviderBuilder, RootProvider, WalletProvider, - }; + use alloy_provider::{Provider, ProviderBuilder, WalletProvider}; use alloy_rpc_types_eth::AccessListItem; use alloy_sol_types::sol; @@ -621,8 +619,8 @@ mod tests { /// Creates a new call_builder to test field modifications, taken from [call_encoding] #[allow(clippy::type_complexity)] - fn build_call_builder( - ) -> CallBuilder<(), AnvilProvider, PhantomData> { + fn build_call_builder() -> CallBuilder<(), impl Provider, PhantomData> + { let provider = ProviderBuilder::new().on_anvil(); let contract = MyContract::new(Address::ZERO, provider); let call_builder = contract.doStuff(U256::ZERO, true).with_cloned_provider(); @@ -730,7 +728,7 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn deploy_and_call() { - let provider = ProviderBuilder::new().with_recommended_fillers().on_anvil_with_wallet(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let expected_address = provider.default_signer_address().create(0); let my_contract = MyContract::deploy(provider, true).await.unwrap(); @@ -756,7 +754,7 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn deploy_and_call_with_priority() { - let provider = ProviderBuilder::new().on_anvil(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let counter_contract = Counter::deploy(provider.clone()).await.unwrap(); let max_fee_per_gas: U256 = parse_units("50", "gwei").unwrap().into(); let max_priority_fee_per_gas: U256 = parse_units("0.1", "gwei").unwrap().into(); diff --git a/crates/contract/src/event.rs b/crates/contract/src/event.rs index 0263eae6f27..7ddd21e2502 100644 --- a/crates/contract/src/event.rs +++ b/crates/contract/src/event.rs @@ -280,7 +280,9 @@ pub(crate) mod subscription { #[cfg(test)] mod tests { use super::*; + use alloy_network::EthereumWallet; use alloy_primitives::U256; + use alloy_signer_local::PrivateKeySigner; use alloy_sol_types::sol; sol! { @@ -309,8 +311,15 @@ mod tests { let _ = tracing_subscriber::fmt::try_init(); let anvil = alloy_node_bindings::Anvil::new().spawn(); - let provider = alloy_provider::ProviderBuilder::new().on_http(anvil.endpoint_url()); + let pk: PrivateKeySigner = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap(); + let wallet = EthereumWallet::from(pk); + let provider = alloy_provider::ProviderBuilder::new() + .wallet(wallet.clone()) + .on_http(anvil.endpoint_url()); + + // let from = address!("f39Fd6e51aad88F6F4ce6aB8827279cffFb92266"); let contract = MyContract::deploy(&provider).await.unwrap(); let event: Event<(), _, MyContract::MyEvent, _> = Event::new(&provider, Filter::new()); @@ -365,6 +374,7 @@ mod tests { #[cfg(feature = "pubsub")] { let provider = alloy_provider::ProviderBuilder::new() + .wallet(wallet) .on_builtin(&anvil.ws_endpoint()) .await .unwrap(); @@ -410,7 +420,12 @@ mod tests { let _ = tracing_subscriber::fmt::try_init(); let anvil = alloy_node_bindings::Anvil::new().spawn(); - let provider = alloy_provider::ProviderBuilder::new().on_http(anvil.endpoint_url()); + let pk: PrivateKeySigner = + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80".parse().unwrap(); + let wallet = EthereumWallet::from(pk); + let provider = alloy_provider::ProviderBuilder::new() + .wallet(wallet.clone()) + .on_http(anvil.endpoint_url()); let contract = MyContract::deploy(&provider).await.unwrap(); @@ -465,6 +480,7 @@ mod tests { #[cfg(feature = "pubsub")] { let provider = alloy_provider::ProviderBuilder::new() + .wallet(wallet) .on_builtin(&anvil.ws_endpoint()) .await .unwrap(); diff --git a/crates/contract/src/instance.rs b/crates/contract/src/instance.rs index 97484fedba8..814429cce2b 100644 --- a/crates/contract/src/instance.rs +++ b/crates/contract/src/instance.rs @@ -127,7 +127,7 @@ mod tests { #[tokio::test] async fn contract_interface() { - let provider = ProviderBuilder::new().on_anvil(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let abi_str = r#"[{"inputs":[],"name":"counter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"increment","outputs":[],"stateMutability":"nonpayable","type":"function"}]"#; let abi = serde_json::from_str::(abi_str).unwrap(); diff --git a/crates/provider/src/builder.rs b/crates/provider/src/builder.rs index 901db43be51..e9b224a6449 100644 --- a/crates/provider/src/builder.rs +++ b/crates/provider/src/builder.rs @@ -104,6 +104,13 @@ where /// This type is similar to [`tower::ServiceBuilder`], with extra complication /// around maintaining the network and transport types. /// +/// The [`ProviderBuilder`] can be instantiated in two ways, using `ProviderBuilder::new()` or +/// `ProviderBuilder::default()`. +/// +/// `ProviderBuilder::new()` will create a new [`ProviderBuilder`] with the [`RecommendedFillers`] +/// enabled, whereas `ProviderBuilder::default()` will instantiate it in its vanilla +/// [`ProviderBuilder`] form i.e with no fillers enabled. +/// /// [`tower::ServiceBuilder`]: https://docs.rs/tower/latest/tower/struct.ServiceBuilder.html #[derive(Debug)] pub struct ProviderBuilder { @@ -112,10 +119,31 @@ pub struct ProviderBuilder { network: PhantomData N>, } -impl ProviderBuilder { - /// Create a new [`ProviderBuilder`]. - pub const fn new() -> Self { - Self { layer: Identity, filler: Identity, network: PhantomData } +impl + ProviderBuilder< + Identity, + JoinFill::RecommendedFillers>, + Ethereum, + > +{ + /// Create a new [`ProviderBuilder`] with the recommended filler enabled. + /// + /// Recommended fillers are preconfigured set of fillers that handle gas estimation, nonce + /// management, and chain-id fetching. + /// + /// Building a provider with this setting enabled will return a [`crate::fillers::FillProvider`] + /// with [`crate::utils::JoinedRecommendedFillers`]. + /// + /// You can opt-out of using these fillers by using the `.disable_recommended_fillers()` method. + pub fn new() -> Self { + ProviderBuilder::default().with_recommended_fillers() + } + + /// Opt-out of the recommended fillers by reseting the fillers stack in the [`ProviderBuilder`]. + /// + /// This is equivalent to creating the builder using `ProviderBuilder::default()`. + pub fn disable_recommended_fillers(self) -> ProviderBuilder { + ProviderBuilder { layer: self.layer, filler: Identity, network: self.network } } } @@ -384,6 +412,7 @@ impl ProviderBuilder { >, { self.on_anvil_with_wallet_and_config(std::convert::identity) + .expect("failed to build provider") } /// Build this provider with anvil, using the BoxTransport. The @@ -411,23 +440,6 @@ impl ProviderBuilder { pub fn on_anvil_with_wallet_and_config( self, f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil, - ) -> as ProviderLayer>::Provider - where - F: TxFiller + ProviderLayer, - L: crate::builder::ProviderLayer< - crate::layers::AnvilProvider, - >, - { - self.try_on_anvil_with_wallet_and_config(f).unwrap() - } - - /// Build this provider with anvil, using the BoxTransport. The - /// given function is used to configure the anvil instance. This - /// function configures a wallet backed by anvil keys, and is intended for - /// use in tests. - pub fn try_on_anvil_with_wallet_and_config( - self, - f: impl FnOnce(alloy_node_bindings::Anvil) -> alloy_node_bindings::Anvil, ) -> AnvilProviderResult< as ProviderLayer>::Provider> where F: TxFiller + ProviderLayer, diff --git a/crates/provider/src/ext/debug.rs b/crates/provider/src/ext/debug.rs index dfeb1bc0884..137f53a8b47 100644 --- a/crates/provider/src/ext/debug.rs +++ b/crates/provider/src/ext/debug.rs @@ -413,7 +413,7 @@ mod test { #[tokio::test] async fn test_debug_trace_transaction() { async_ci_only(|| async move { - let provider = ProviderBuilder::new().with_recommended_fillers().on_anvil_with_wallet(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let from = provider.default_signer_address(); let gas_price = provider.get_gas_price().await.unwrap(); @@ -542,8 +542,7 @@ mod test { async_ci_only(|| async move { run_with_tempdir("reth-test-", |temp_dir| async move { let reth = Reth::new().dev().disable_discovery().data_dir(temp_dir).spawn(); - let provider = - ProviderBuilder::new().with_recommended_fillers().on_http(reth.endpoint_url()); + let provider = ProviderBuilder::new().on_http(reth.endpoint_url()); let tx1 = TransactionRequest::default() .with_from(address!("0000000000000000000000000000000000000123")) diff --git a/crates/provider/src/fillers/chain_id.rs b/crates/provider/src/fillers/chain_id.rs index 583228cbeeb..296a059f763 100644 --- a/crates/provider/src/fillers/chain_id.rs +++ b/crates/provider/src/fillers/chain_id.rs @@ -26,7 +26,7 @@ use crate::{ /// # use alloy_rpc_types_eth::TransactionRequest; /// # use alloy_provider::{ProviderBuilder, RootProvider, Provider}; /// # async fn test + Clone>(url: url::Url, wallet: W) -> Result<(), Box> { -/// let provider = ProviderBuilder::new() +/// let provider = ProviderBuilder::default() /// .with_chain_id(1) /// .wallet(wallet) /// .on_http(url); diff --git a/crates/provider/src/fillers/gas.rs b/crates/provider/src/fillers/gas.rs index ab66fb29513..006cb048230 100644 --- a/crates/provider/src/fillers/gas.rs +++ b/crates/provider/src/fillers/gas.rs @@ -52,7 +52,7 @@ pub enum GasFillable { /// # use alloy_rpc_types_eth::TransactionRequest; /// # use alloy_provider::{ProviderBuilder, RootProvider, Provider}; /// # async fn test + Clone>(url: url::Url, wallet: W) -> Result<(), Box> { -/// let provider = ProviderBuilder::new() +/// let provider = ProviderBuilder::default() /// .with_gas_estimation() /// .wallet(wallet) /// .on_http(url); @@ -253,7 +253,7 @@ mod tests { #[tokio::test] async fn no_gas_price_or_limit() { - let provider = ProviderBuilder::new().with_recommended_fillers().on_anvil_with_wallet(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); // GasEstimationLayer requires chain_id to be set to handle EIP-1559 tx let tx = TransactionRequest { @@ -273,7 +273,7 @@ mod tests { #[tokio::test] async fn no_gas_limit() { - let provider = ProviderBuilder::new().with_recommended_fillers().on_anvil_with_wallet(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let gas_price = provider.get_gas_price().await.unwrap(); let tx = TransactionRequest { @@ -292,7 +292,7 @@ mod tests { #[tokio::test] async fn no_max_fee_per_blob_gas() { - let provider = ProviderBuilder::new().with_recommended_fillers().on_anvil_with_wallet(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let sidecar: SidecarBuilder = SidecarBuilder::from_slice(b"Hello World"); let sidecar = sidecar.build().unwrap(); @@ -319,7 +319,7 @@ mod tests { #[tokio::test] async fn zero_max_fee_per_blob_gas() { - let provider = ProviderBuilder::new().with_recommended_fillers().on_anvil_with_wallet(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let sidecar: SidecarBuilder = SidecarBuilder::from_slice(b"Hello World"); let sidecar = sidecar.build().unwrap(); diff --git a/crates/provider/src/fillers/nonce.rs b/crates/provider/src/fillers/nonce.rs index 82bd4488f00..fcd7fffb063 100644 --- a/crates/provider/src/fillers/nonce.rs +++ b/crates/provider/src/fillers/nonce.rs @@ -104,7 +104,7 @@ impl NonceManager for CachedNonceManager { /// # use alloy_rpc_types_eth::TransactionRequest; /// # use alloy_provider::{ProviderBuilder, RootProvider, Provider}; /// # async fn test + Clone>(url: url::Url, wallet: W) -> Result<(), Box> { -/// let provider = ProviderBuilder::new() +/// let provider = ProviderBuilder::default() /// .with_simple_nonce_management() /// .wallet(wallet) /// .on_http(url); @@ -232,7 +232,10 @@ mod tests { #[tokio::test] async fn no_nonce_if_sender_unset() { - let provider = ProviderBuilder::new().with_cached_nonce_management().on_anvil(); + let provider = ProviderBuilder::new() + .disable_recommended_fillers() + .with_cached_nonce_management() + .on_anvil(); let tx = TransactionRequest { value: Some(U256::from(100)), @@ -248,7 +251,10 @@ mod tests { #[tokio::test] async fn increments_nonce() { - let provider = ProviderBuilder::new().with_cached_nonce_management().on_anvil_with_wallet(); + let provider = ProviderBuilder::new() + .disable_recommended_fillers() + .with_cached_nonce_management() + .on_anvil_with_wallet(); let from = provider.default_signer_address(); let tx = TransactionRequest { diff --git a/crates/provider/src/layers/cache.rs b/crates/provider/src/layers/cache.rs index d0813f1c4cf..17d4afdc9a5 100644 --- a/crates/provider/src/layers/cache.rs +++ b/crates/provider/src/layers/cache.rs @@ -656,7 +656,10 @@ mod tests { let cache_layer = CacheLayer::new(100); let shared_cache = cache_layer.cache(); let anvil = Anvil::new().block_time_f64(0.3).spawn(); - let provider = ProviderBuilder::new().layer(cache_layer).on_http(anvil.endpoint_url()); + let provider = ProviderBuilder::new() + .disable_recommended_fillers() + .layer(cache_layer) + .on_http(anvil.endpoint_url()); let path = dir.join("rpc-cache-tx.txt"); shared_cache.load_cache(path.clone()).unwrap(); @@ -724,8 +727,7 @@ mod tests { run_with_tempdir("get-code", |dir| async move { let cache_layer = CacheLayer::new(100); let shared_cache = cache_layer.cache(); - let anvil = Anvil::new().spawn(); - let provider = ProviderBuilder::new().layer(cache_layer).on_http(anvil.endpoint_url()); + let provider = ProviderBuilder::default().with_gas_estimation().layer(cache_layer).on_anvil_with_wallet(); let path = dir.join("rpc-cache-code.txt"); shared_cache.load_cache(path.clone()).unwrap(); @@ -734,7 +736,7 @@ mod tests { // solc v0.8.26; solc Counter.sol --via-ir --optimize --bin "6080806040523460135760df908160198239f35b600080fdfe6080806040526004361015601257600080fd5b60003560e01c9081633fb5c1cb1460925781638381f58a146079575063d09de08a14603c57600080fd5b3460745760003660031901126074576000546000198114605e57600101600055005b634e487b7160e01b600052601160045260246000fd5b600080fd5b3460745760003660031901126074576020906000548152f35b34607457602036600319011260745760043560005500fea2646970667358221220e978270883b7baed10810c4079c941512e93a7ba1cd1108c781d4bc738d9090564736f6c634300081a0033" ).unwrap(); - let tx = TransactionRequest::default().with_deploy_code(bytecode); + let tx = TransactionRequest::default().with_nonce(0).with_deploy_code(bytecode).with_chain_id(31337); let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); diff --git a/crates/provider/src/provider/trait.rs b/crates/provider/src/provider/trait.rs index 46b419b379a..adee3d13544 100644 --- a/crates/provider/src/provider/trait.rs +++ b/crates/provider/src/provider/trait.rs @@ -1106,7 +1106,7 @@ impl Provider for RootProvider { #[cfg(test)] mod tests { - use std::time::Duration; + use std::{str::FromStr, time::Duration}; use super::*; use crate::{builder, ProviderBuilder, WalletProvider}; @@ -1407,7 +1407,7 @@ mod tests { #[tokio::test] async fn test_send_tx() { - let provider = ProviderBuilder::new().on_anvil(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let tx = TransactionRequest { value: Some(U256::from(100)), to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()), @@ -1430,7 +1430,7 @@ mod tests { #[tokio::test] async fn test_watch_confirmed_tx() { - let provider = ProviderBuilder::new().on_anvil(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let tx = TransactionRequest { value: Some(U256::from(100)), to: Some(address!("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045").into()), @@ -1622,7 +1622,7 @@ mod tests { #[tokio::test] async fn gets_transaction_by_hash() { - let provider = ProviderBuilder::new().with_recommended_fillers().on_anvil_with_wallet(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); let req = TransactionRequest::default() .from(provider.default_signer_address()) @@ -1767,7 +1767,6 @@ mod tests { let wallet = EthereumWallet::from(signer); let provider = ProviderBuilder::new() - .with_recommended_fillers() .network::() .wallet(wallet) .on_http(anvil.endpoint_url()); @@ -1840,4 +1839,20 @@ mod tests { .unwrap(); assert!(block.transactions.is_hashes()); } + + #[tokio::test] + async fn disable_test() { + let provider = ProviderBuilder::new() + .disable_recommended_fillers() + .with_cached_nonce_management() + .on_anvil(); + + let tx = TransactionRequest::default() + .with_kind(alloy_primitives::TxKind::Create) + .value(U256::from(1235)) + .with_input(Bytes::from_str("ffffffffffffff").unwrap()); + + let err = provider.send_transaction(tx).await.unwrap_err().to_string(); + assert!(err.contains("missing properties: [(\"NonceManager\", [\"from\"])]")); + } } diff --git a/crates/provider/src/provider/wallet.rs b/crates/provider/src/provider/wallet.rs index cc204b90af3..f01f961b0f5 100644 --- a/crates/provider/src/provider/wallet.rs +++ b/crates/provider/src/provider/wallet.rs @@ -97,14 +97,14 @@ mod test { #[test] fn basic_usage() { - let provider = ProviderBuilder::new().on_anvil_with_wallet(); + let provider = ProviderBuilder::new().disable_recommended_fillers().on_anvil_with_wallet(); assert!(provider.signer_addresses().contains(&provider.default_signer_address())); } #[test] fn bubbles_through_fillers() { - let provider = ProviderBuilder::new().with_recommended_fillers().on_anvil_with_wallet(); + let provider = ProviderBuilder::new().on_anvil_with_wallet(); assert!(provider.signer_addresses().contains(&provider.default_signer_address())); } diff --git a/crates/provider/src/utils.rs b/crates/provider/src/utils.rs index ba2d1960723..021f3ed453c 100644 --- a/crates/provider/src/utils.rs +++ b/crates/provider/src/utils.rs @@ -2,6 +2,11 @@ use alloy_primitives::{U128, U64}; +use crate::{ + fillers::{BlobGasFiller, ChainIdFiller, GasFiller, JoinFill, NonceFiller}, + Identity, +}; + /// The number of blocks from the past for which the fee rewards are fetched for fee estimation. pub const EIP1559_FEE_ESTIMATION_PAST_BLOCKS: u64 = 10; /// Multiplier for the current base fee to estimate max base fee for the next block. @@ -66,6 +71,13 @@ pub(crate) fn convert_u64(r: U64) -> u64 { r.to::() } +/// Helper type representing the joined recommended fillers i.e [`GasFiller`], +/// [`BlobGasFiller`], [`NonceFiller`], and [`ChainIdFiller`]. +pub type JoinedRecommendedFillers = JoinFill< + Identity, + JoinFill>>, +>; + #[cfg(test)] mod tests { use super::*;