#![cfg_attr(docsrs, feature(doc_cfg))] #![deny(broken_intra_doc_links)] #![allow(clippy::type_complexity)] //! # Clients for interacting with Ethereum nodes //! //! This crate provides asynchronous [Ethereum JSON-RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) //! compliant clients. //! //! For more documentation on the available calls, refer to the [`Provider`](crate::Provider) //! struct. //! //! # Examples //! //! ```no_run //! use ethers_providers::{Provider, Http, Middleware}; //! use std::convert::TryFrom; //! //! # async fn foo() -> Result<(), Box> { //! let provider = Provider::::try_from( //! "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27" //! )?; //! //! let block = provider.get_block(100u64).await?; //! println!("Got block: {}", serde_json::to_string(&block)?); //! //! let code = provider.get_code("0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359", None).await?; //! println!("Got code: {}", serde_json::to_string(&code)?); //! # Ok(()) //! # } //! ``` //! //! # Websockets //! //! The crate has support for WebSockets via Tokio. //! //! ``` //! # async fn foo() -> Result<(), Box> { //! # use ethers_providers::Ws; //! let ws = Ws::connect("ws://localhost:8545").await?; //! # Ok(()) //! # } //! ``` //! //! # Ethereum Name Service //! //! The provider may also be used to resolve [Ethereum Name Service](https://ens.domains) (ENS) names //! to addresses (and vice versa). The default ENS address is [mainnet](https://etherscan.io/address/0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e) and can be overriden by calling the [`ens`](method@crate::Provider::ens) method on the provider. //! //! ```no_run //! # use ethers_providers::{Provider, Http, Middleware}; //! # use std::convert::TryFrom; //! # async fn foo() -> Result<(), Box> { //! # let provider = Provider::::try_from( //! # "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27" //! # )?; //! // Resolve ENS name to Address //! let name = "vitalik.eth"; //! let address = provider.resolve_name(name).await?; //! //! // Lookup ENS name given Address //! let resolved_name = provider.lookup_address(address).await?; //! assert_eq!(name, resolved_name); //! # Ok(()) //! # } //! ``` mod transports; pub use transports::*; mod provider; // ENS support pub mod ens; mod pending_transaction; pub use pending_transaction::PendingTransaction; mod stream; pub use futures_util::StreamExt; pub use stream::{interval, FilterWatcher, TransactionStream, DEFAULT_POLL_INTERVAL}; mod pubsub; pub use pubsub::{PubsubClient, SubscriptionStream}; use async_trait::async_trait; use auto_impl::auto_impl; use ethers_core::types::transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed}; use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize}; use std::{error::Error, fmt::Debug, future::Future, pin::Pin, str::FromStr}; pub use provider::{FilterKind, Provider, ProviderError}; // Helper type alias #[cfg(target_arch = "wasm32")] pub(crate) type PinBoxFut<'a, T> = Pin> + 'a>>; #[cfg(not(target_arch = "wasm32"))] pub(crate) type PinBoxFut<'a, T> = Pin> + Send + 'a>>; #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[auto_impl(&, Box, Arc)] /// Trait which must be implemented by data transports to be used with the Ethereum /// JSON-RPC provider. pub trait JsonRpcClient: Debug + Send + Sync { /// A JSON-RPC Error type Error: Error + Into; /// Sends a request with the provided JSON-RPC and parameters serialized as JSON async fn request(&self, method: &str, params: T) -> Result where T: Debug + Serialize + Send + Sync, R: Serialize + DeserializeOwned; } use ethers_core::types::*; pub trait FromErr { fn from(src: T) -> Self; } /// Calls the future if `item` is None, otherwise returns a `futures::ok` pub async fn maybe(item: Option, f: F) -> Result where F: Future>, { if let Some(item) = item { futures_util::future::ok(item).await } else { f.await } } /// A middleware allows customizing requests send and received from an ethereum node. /// /// Writing a middleware is as simple as: /// 1. implementing the [`inner`](crate::Middleware::inner) method to point to the next layer in the "middleware onion", /// 2. implementing the [`FromErr`](crate::FromErr) trait on your middleware's error type /// 3. implementing any of the methods you want to override /// /// ```rust /// use ethers_providers::{Middleware, FromErr}; /// use ethers_core::types::{U64, TransactionRequest, U256, transaction::eip2718::TypedTransaction}; /// use thiserror::Error; /// use async_trait::async_trait; /// /// #[derive(Debug)] /// struct MyMiddleware(M); /// /// #[derive(Error, Debug)] /// pub enum MyError { /// #[error("{0}")] /// MiddlewareError(M::Error), /// /// // Add your middleware's specific errors here /// } /// /// impl FromErr for MyError { /// fn from(src: M::Error) -> MyError { /// MyError::MiddlewareError(src) /// } /// } /// /// #[async_trait] /// impl Middleware for MyMiddleware /// where /// M: Middleware, /// { /// type Error = MyError; /// type Provider = M::Provider; /// type Inner = M; /// /// fn inner(&self) -> &M { /// &self.0 /// } /// /// /// Overrides the default `get_block_number` method to always return 0 /// async fn get_block_number(&self) -> Result { /// Ok(U64::zero()) /// } /// /// /// Overrides the default `estimate_gas` method to log that it was called, /// /// before forwarding the call to the next layer. /// async fn estimate_gas(&self, tx: &TypedTransaction) -> Result { /// println!("Estimating gas..."); /// self.inner().estimate_gas(tx).await.map_err(FromErr::from) /// } /// } /// ``` #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[auto_impl(&, Box, Arc)] pub trait Middleware: Sync + Send + Debug { type Error: Sync + Send + Error + FromErr<::Error>; type Provider: JsonRpcClient; type Inner: Middleware; /// The next middleware in the stack fn inner(&self) -> &Self::Inner; /// The HTTP or Websocket provider. fn provider(&self) -> &Provider { self.inner().provider() } fn default_sender(&self) -> Option
{ self.inner().default_sender() } async fn client_version(&self) -> Result { self.inner().client_version().await.map_err(FromErr::from) } /// Helper for filling a transaction async fn fill_transaction( &self, tx: &mut TypedTransaction, block: Option, ) -> Result<(), Self::Error> { if let Some(default_sender) = self.default_sender() { if tx.from().is_none() { tx.set_from(default_sender); } } // TODO: Can we poll the futures below at the same time? // Access List + Name resolution and then Gas price + Gas // set the ENS name if let Some(NameOrAddress::Name(ref ens_name)) = tx.to() { let addr = self.resolve_name(ens_name).await?; tx.set_to(addr); } // estimate the gas without the access list let gas = maybe(tx.gas().cloned(), self.estimate_gas(tx)).await?; let mut al_used = false; // set the access lists if let Some(access_list) = tx.access_list() { if access_list.0.is_empty() { if let Ok(al_with_gas) = self.create_access_list(tx, block).await { // only set the access list if the used gas is less than the // normally estimated gas if al_with_gas.gas_used < gas { tx.set_access_list(al_with_gas.access_list); tx.set_gas(al_with_gas.gas_used); al_used = true; } } } } if !al_used { tx.set_gas(gas); } match tx { TypedTransaction::Eip2930(_) | TypedTransaction::Legacy(_) => { let gas_price = maybe(tx.gas_price(), self.get_gas_price()).await?; tx.set_gas_price(gas_price); } TypedTransaction::Eip1559(ref mut inner) => { if inner.max_fee_per_gas.is_none() || inner.max_priority_fee_per_gas.is_none() { let (max_fee_per_gas, max_priority_fee_per_gas) = self.estimate_eip1559_fees(None).await?; inner.max_fee_per_gas = Some(max_fee_per_gas); inner.max_priority_fee_per_gas = Some(max_priority_fee_per_gas); }; } } Ok(()) } async fn get_block_number(&self) -> Result { self.inner().get_block_number().await.map_err(FromErr::from) } async fn send_transaction + Send + Sync>( &self, tx: T, block: Option, ) -> Result, Self::Error> { self.inner() .send_transaction(tx, block) .await .map_err(FromErr::from) } async fn resolve_name(&self, ens_name: &str) -> Result { self.inner() .resolve_name(ens_name) .await .map_err(FromErr::from) } async fn lookup_address(&self, address: Address) -> Result { self.inner() .lookup_address(address) .await .map_err(FromErr::from) } async fn get_block + Send + Sync>( &self, block_hash_or_number: T, ) -> Result>, Self::Error> { self.inner() .get_block(block_hash_or_number) .await .map_err(FromErr::from) } async fn get_block_with_txs + Send + Sync>( &self, block_hash_or_number: T, ) -> Result>, Self::Error> { self.inner() .get_block_with_txs(block_hash_or_number) .await .map_err(FromErr::from) } async fn get_uncle_count + Send + Sync>( &self, block_hash_or_number: T, ) -> Result { self.inner() .get_uncle_count(block_hash_or_number) .await .map_err(FromErr::from) } async fn get_uncle + Send + Sync>( &self, block_hash_or_number: T, idx: U64, ) -> Result>, Self::Error> { self.inner() .get_uncle(block_hash_or_number, idx) .await .map_err(FromErr::from) } async fn get_transaction_count + Send + Sync>( &self, from: T, block: Option, ) -> Result { self.inner() .get_transaction_count(from, block) .await .map_err(FromErr::from) } async fn estimate_gas(&self, tx: &TypedTransaction) -> Result { self.inner().estimate_gas(tx).await.map_err(FromErr::from) } async fn call( &self, tx: &TypedTransaction, block: Option, ) -> Result { self.inner().call(tx, block).await.map_err(FromErr::from) } async fn get_chainid(&self) -> Result { self.inner().get_chainid().await.map_err(FromErr::from) } async fn get_balance + Send + Sync>( &self, from: T, block: Option, ) -> Result { self.inner() .get_balance(from, block) .await .map_err(FromErr::from) } async fn get_transaction>( &self, transaction_hash: T, ) -> Result, Self::Error> { self.inner() .get_transaction(transaction_hash) .await .map_err(FromErr::from) } async fn get_transaction_receipt>( &self, transaction_hash: T, ) -> Result, Self::Error> { self.inner() .get_transaction_receipt(transaction_hash) .await .map_err(FromErr::from) } async fn get_block_receipts + Send + Sync>( &self, block: T, ) -> Result, Self::Error> { self.inner() .get_block_receipts(block) .await .map_err(FromErr::from) } async fn get_gas_price(&self) -> Result { self.inner().get_gas_price().await.map_err(FromErr::from) } async fn estimate_eip1559_fees( &self, estimator: Option>) -> (U256, U256)>, ) -> Result<(U256, U256), Self::Error> { self.inner() .estimate_eip1559_fees(estimator) .await .map_err(FromErr::from) } async fn get_accounts(&self) -> Result, Self::Error> { self.inner().get_accounts().await.map_err(FromErr::from) } async fn send_raw_transaction<'a>( &'a self, tx: Bytes, ) -> Result, Self::Error> { self.inner() .send_raw_transaction(tx) .await .map_err(FromErr::from) } /// This returns true if either the middleware stack contains a `SignerMiddleware`, or the /// JSON-RPC provider has an unlocked key that can sign using the `eth_sign` call. If none of /// the above conditions are met, then the middleware stack is not capable of signing data. async fn is_signer(&self) -> bool { self.inner().is_signer().await } async fn sign + Send + Sync>( &self, data: T, from: &Address, ) -> Result { self.inner().sign(data, from).await.map_err(FromErr::from) } ////// Contract state async fn get_logs(&self, filter: &Filter) -> Result, Self::Error> { self.inner().get_logs(filter).await.map_err(FromErr::from) } async fn new_filter(&self, filter: FilterKind<'_>) -> Result { self.inner().new_filter(filter).await.map_err(FromErr::from) } async fn uninstall_filter + Send + Sync>( &self, id: T, ) -> Result { self.inner() .uninstall_filter(id) .await .map_err(FromErr::from) } async fn watch<'a>( &'a self, filter: &Filter, ) -> Result, Self::Error> { self.inner().watch(filter).await.map_err(FromErr::from) } async fn watch_pending_transactions( &self, ) -> Result, Self::Error> { self.inner() .watch_pending_transactions() .await .map_err(FromErr::from) } async fn get_filter_changes(&self, id: T) -> Result, Self::Error> where T: Into + Send + Sync, R: Serialize + DeserializeOwned + Send + Sync + Debug, { self.inner() .get_filter_changes(id) .await .map_err(FromErr::from) } async fn watch_blocks(&self) -> Result, Self::Error> { self.inner().watch_blocks().await.map_err(FromErr::from) } async fn get_code + Send + Sync>( &self, at: T, block: Option, ) -> Result { self.inner() .get_code(at, block) .await .map_err(FromErr::from) } async fn get_storage_at + Send + Sync>( &self, from: T, location: H256, block: Option, ) -> Result { self.inner() .get_storage_at(from, location, block) .await .map_err(FromErr::from) } async fn get_proof + Send + Sync>( &self, from: T, locations: Vec, block: Option, ) -> Result { self.inner() .get_proof(from, locations, block) .await .map_err(FromErr::from) } // Mempool inspection for Geth's API async fn txpool_content(&self) -> Result { self.inner().txpool_content().await.map_err(FromErr::from) } async fn txpool_inspect(&self) -> Result { self.inner().txpool_inspect().await.map_err(FromErr::from) } async fn txpool_status(&self) -> Result { self.inner().txpool_status().await.map_err(FromErr::from) } // Parity `trace` support /// Executes the given call and returns a number of possible traces for it async fn trace_call + Send + Sync>( &self, req: T, trace_type: Vec, block: Option, ) -> Result { self.inner() .trace_call(req, trace_type, block) .await .map_err(FromErr::from) } /// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces async fn trace_raw_transaction( &self, data: Bytes, trace_type: Vec, ) -> Result { self.inner() .trace_raw_transaction(data, trace_type) .await .map_err(FromErr::from) } /// Replays a transaction, returning the traces async fn trace_replay_transaction( &self, hash: H256, trace_type: Vec, ) -> Result { self.inner() .trace_replay_transaction(hash, trace_type) .await .map_err(FromErr::from) } /// Replays all transactions in a block returning the requested traces for each transaction async fn trace_replay_block_transactions( &self, block: BlockNumber, trace_type: Vec, ) -> Result, Self::Error> { self.inner() .trace_replay_block_transactions(block, trace_type) .await .map_err(FromErr::from) } /// Returns traces created at given block async fn trace_block(&self, block: BlockNumber) -> Result, Self::Error> { self.inner().trace_block(block).await.map_err(FromErr::from) } /// Return traces matching the given filter async fn trace_filter(&self, filter: TraceFilter) -> Result, Self::Error> { self.inner() .trace_filter(filter) .await .map_err(FromErr::from) } /// Returns trace at the given position async fn trace_get + Send + Sync>( &self, hash: H256, index: Vec, ) -> Result { self.inner() .trace_get(hash, index) .await .map_err(FromErr::from) } /// Returns all traces of a given transaction async fn trace_transaction(&self, hash: H256) -> Result, Self::Error> { self.inner() .trace_transaction(hash) .await .map_err(FromErr::from) } // Parity namespace /// Returns all receipts for that block. Must be done on a parity node. async fn parity_block_receipts + Send + Sync>( &self, block: T, ) -> Result, Self::Error> { self.inner() .parity_block_receipts(block) .await .map_err(FromErr::from) } async fn subscribe( &self, params: T, ) -> Result, Self::Error> where T: Debug + Serialize + Send + Sync, R: DeserializeOwned + Send + Sync, ::Provider: PubsubClient, { self.inner().subscribe(params).await.map_err(FromErr::from) } async fn unsubscribe(&self, id: T) -> Result where T: Into + Send + Sync, ::Provider: PubsubClient, { self.inner().unsubscribe(id).await.map_err(FromErr::from) } async fn subscribe_blocks( &self, ) -> Result>, Self::Error> where ::Provider: PubsubClient, { self.inner().subscribe_blocks().await.map_err(FromErr::from) } async fn subscribe_pending_txs( &self, ) -> Result, Self::Error> where ::Provider: PubsubClient, { self.inner() .subscribe_pending_txs() .await .map_err(FromErr::from) } async fn subscribe_logs<'a>( &'a self, filter: &Filter, ) -> Result, Self::Error> where ::Provider: PubsubClient, { self.inner() .subscribe_logs(filter) .await .map_err(FromErr::from) } async fn fee_history + serde::Serialize + Send + Sync>( &self, block_count: T, last_block: BlockNumber, reward_percentiles: &[f64], ) -> Result { self.inner() .fee_history(block_count, last_block, reward_percentiles) .await .map_err(FromErr::from) } async fn create_access_list( &self, tx: &TypedTransaction, block: Option, ) -> Result { self.inner() .create_access_list(tx, block) .await .map_err(FromErr::from) } } #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "camelCase")] pub struct FeeHistory { pub base_fee_per_gas: Vec, pub gas_used_ratio: Vec, #[serde(deserialize_with = "from_int_or_hex")] /// oldestBlock is returned as an unsigned integer up to geth v1.10.6. From /// geth v1.10.7, this has been updated to return in the hex encoded form. /// The custom deserializer allows backward compatibility for those clients /// not running v1.10.7 yet. pub oldest_block: U256, pub reward: Vec>, } fn from_int_or_hex<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { #[derive(Deserialize)] #[serde(untagged)] enum IntOrHex { Int(u64), Hex(String), } match IntOrHex::deserialize(deserializer)? { IntOrHex::Int(n) => Ok(U256::from(n)), IntOrHex::Hex(s) => U256::from_str(s.as_str()).map_err(serde::de::Error::custom), } } #[cfg(feature = "celo")] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait CeloMiddleware: Middleware { async fn get_validators_bls_public_keys + Send + Sync>( &self, block_id: T, ) -> Result, ProviderError> { self.provider() .get_validators_bls_public_keys(block_id) .await .map_err(FromErr::from) } }