use crate::{ ens, pubsub::{PubsubClient, SubscriptionStream}, stream::{FilterWatcher, DEFAULT_POLL_INTERVAL}, FeeHistory, FromErr, Http as HttpProvider, JsonRpcClient, JsonRpcClientWrapper, MockProvider, PendingTransaction, QuorumProvider, }; #[cfg(feature = "celo")] use crate::CeloMiddleware; use crate::Middleware; use async_trait::async_trait; use ethers_core::{ abi::{self, Detokenize, ParamType}, types::{ transaction::{eip2718::TypedTransaction, eip2930::AccessListWithGasUsed}, Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, EIP1186ProofResponse, Filter, Log, NameOrAddress, Selector, Signature, Trace, TraceFilter, TraceType, Transaction, TransactionReceipt, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64, }, utils, }; use hex::FromHex; use serde::{de::DeserializeOwned, Serialize}; use thiserror::Error; use url::{ParseError, Url}; use futures_util::lock::Mutex; use std::{convert::TryFrom, fmt::Debug, str::FromStr, sync::Arc, time::Duration}; use tracing::trace; use tracing_futures::Instrument; #[derive(Copy, Clone)] pub enum NodeClient { Geth, Erigon, OpenEthereum, Nethermind, Besu, } impl FromStr for NodeClient { type Err = ProviderError; fn from_str(s: &str) -> Result { match s.split('/').next().unwrap() { "Geth" => Ok(NodeClient::Geth), "Erigon" => Ok(NodeClient::Erigon), "OpenEthereum" => Ok(NodeClient::OpenEthereum), "Nethermind" => Ok(NodeClient::Nethermind), "besu" => Ok(NodeClient::Besu), _ => Err(ProviderError::UnsupportedNodeClient), } } } /// An abstract provider for interacting with the [Ethereum JSON RPC /// API](https://github.com/ethereum/wiki/wiki/JSON-RPC). Must be instantiated /// with a data transport which implements the [`JsonRpcClient`](trait@crate::JsonRpcClient) trait /// (e.g. [HTTP](crate::Http), Websockets etc.) /// /// # Example /// /// ```no_run /// # async fn foo() -> Result<(), Box> { /// use ethers_providers::{Middleware, Provider, Http}; /// use std::convert::TryFrom; /// /// let provider = Provider::::try_from( /// "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27" /// ).expect("could not instantiate HTTP Provider"); /// /// let block = provider.get_block(100u64).await?; /// println!("Got block: {}", serde_json::to_string(&block)?); /// # Ok(()) /// # } /// ``` #[derive(Clone, Debug)] pub struct Provider

{ inner: P, ens: Option

, interval: Option, from: Option
, /// Node client hasn't been checked yet = `None` /// Unsupported node client = `Some(None)` /// Supported node client = `Some(Some(NodeClient))` _node_client: Arc>>, } impl

AsRef

for Provider

{ fn as_ref(&self) -> &P { &self.inner } } impl FromErr for ProviderError { fn from(src: ProviderError) -> Self { src } } #[derive(Debug, Error)] /// An error thrown when making a call to the provider pub enum ProviderError { /// An internal error in the JSON RPC Client #[error(transparent)] JsonRpcClientError(#[from] Box), /// An error during ENS name resolution #[error("ens name not found: {0}")] EnsError(String), #[error(transparent)] SerdeJson(#[from] serde_json::Error), #[error(transparent)] HexError(#[from] hex::FromHexError), #[error("custom error: {0}")] CustomError(String), #[error("unsupported RPC")] UnsupportedRPC, #[error("unsupported node client")] UnsupportedNodeClient, #[error("Attempted to sign a transaction with no available signer. Hint: did you mean to use a SignerMiddleware?")] SignerUnavailable, } /// Types of filters supported by the JSON-RPC. #[derive(Clone, Debug)] pub enum FilterKind<'a> { /// `eth_newBlockFilter` Logs(&'a Filter), /// `eth_newBlockFilter` filter NewBlocks, /// `eth_newPendingTransactionFilter` filter PendingTransactions, } // JSON RPC bindings impl Provider

{ /// Instantiate a new provider with a backend. pub fn new(provider: P) -> Self { Self { inner: provider, ens: None, interval: None, from: None, _node_client: Arc::new(Mutex::new(None)), } } /// Returns the type of node we're connected to, while also caching the value for use /// in other node-specific API calls, such as the get_block_receipts call. pub async fn node_client(&self) -> Result { let mut node_client = self._node_client.lock().await; if let Some(node_client) = *node_client { Ok(node_client) } else { let client_version = self.client_version().await?; let client_version = match client_version.parse::() { Ok(res) => res, Err(_) => return Err(ProviderError::UnsupportedNodeClient), }; *node_client = Some(client_version); Ok(client_version) } } pub fn with_sender(mut self, address: impl Into

) -> Self { self.from = Some(address.into()); self } async fn request(&self, method: &str, params: T) -> Result where T: Debug + Serialize + Send + Sync, R: Serialize + DeserializeOwned + Debug, { let span = tracing::trace_span!("rpc", method = method, params = ?serde_json::to_string(¶ms)?); // https://docs.rs/tracing/0.1.22/tracing/span/struct.Span.html#in-asynchronous-code let res = async move { trace!("tx"); let res: R = self.inner.request(method, params).await.map_err(Into::into)?; trace!(rx = ?serde_json::to_string(&res)?); Ok::<_, ProviderError>(res) } .instrument(span) .await?; Ok(res) } async fn get_block_gen( &self, id: BlockId, include_txs: bool, ) -> Result>, ProviderError> { let include_txs = utils::serialize(&include_txs); Ok(match id { BlockId::Hash(hash) => { let hash = utils::serialize(&hash); self.request("eth_getBlockByHash", [hash, include_txs]).await? } BlockId::Number(num) => { let num = utils::serialize(&num); self.request("eth_getBlockByNumber", [num, include_txs]).await? } }) } } #[cfg(feature = "celo")] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl CeloMiddleware for Provider

{ async fn get_validators_bls_public_keys + Send + Sync>( &self, block_id: T, ) -> Result, ProviderError> { let block_id = utils::serialize(&block_id.into()); self.request("istanbul_getValidatorsBLSPublicKeys", [block_id]).await } } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl Middleware for Provider

{ type Error = ProviderError; type Provider = P; type Inner = Self; fn inner(&self) -> &Self::Inner { unreachable!("There is no inner provider here") } fn provider(&self) -> &Provider { self } fn default_sender(&self) -> Option

{ self.from } ////// Blockchain Status // // Functions for querying the state of the blockchain /// Returns the current client version using the `web3_clientVersion` RPC. async fn client_version(&self) -> Result { self.request("web3_clientVersion", ()).await } /// Gets the latest block number via the `eth_BlockNumber` API async fn get_block_number(&self) -> Result { self.request("eth_blockNumber", ()).await } /// Gets the block at `block_hash_or_number` (transaction hashes only) async fn get_block + Send + Sync>( &self, block_hash_or_number: T, ) -> Result>, Self::Error> { self.get_block_gen(block_hash_or_number.into(), false).await } /// Gets the block at `block_hash_or_number` (full transactions included) async fn get_block_with_txs + Send + Sync>( &self, block_hash_or_number: T, ) -> Result>, ProviderError> { self.get_block_gen(block_hash_or_number.into(), true).await } /// Gets the block uncle count at `block_hash_or_number` async fn get_uncle_count + Send + Sync>( &self, block_hash_or_number: T, ) -> Result { let id = block_hash_or_number.into(); Ok(match id { BlockId::Hash(hash) => { let hash = utils::serialize(&hash); self.request("eth_getUncleCountByBlockHash", [hash]).await? } BlockId::Number(num) => { let num = utils::serialize(&num); self.request("eth_getUncleCountByBlockNumber", [num]).await? } }) } /// Gets the block uncle at `block_hash_or_number` and `idx` async fn get_uncle + Send + Sync>( &self, block_hash_or_number: T, idx: U64, ) -> Result>, ProviderError> { let blk_id = block_hash_or_number.into(); let idx = utils::serialize(&idx); Ok(match blk_id { BlockId::Hash(hash) => { let hash = utils::serialize(&hash); self.request("eth_getUncleByBlockHashAndIndex", [hash, idx]).await? } BlockId::Number(num) => { let num = utils::serialize(&num); self.request("eth_getUncleByBlockNumberAndIndex", [num, idx]).await? } }) } /// Gets the transaction with `transaction_hash` async fn get_transaction>( &self, transaction_hash: T, ) -> Result, ProviderError> { let hash = transaction_hash.into(); self.request("eth_getTransactionByHash", [hash]).await } /// Gets the transaction receipt with `transaction_hash` async fn get_transaction_receipt>( &self, transaction_hash: T, ) -> Result, ProviderError> { let hash = transaction_hash.into(); self.request("eth_getTransactionReceipt", [hash]).await } /// Returns all receipts for a block. /// /// Note that this uses the `eth_getBlockReceipts` or `parity_getBlockReceipts` RPC, which is /// non-standard and currently supported by Erigon, OpenEthereum and Nethermind. async fn get_block_receipts + Send + Sync>( &self, block: T, ) -> Result, Self::Error> { let method = match self.node_client().await? { NodeClient::Erigon => "eth_getBlockReceipts", NodeClient::OpenEthereum | NodeClient::Nethermind => "parity_getBlockReceipts", _ => return Err(ProviderError::UnsupportedRPC), }; self.request(method, [block.into()]).await } /// Gets the current gas price as estimated by the node async fn get_gas_price(&self) -> Result { self.request("eth_gasPrice", ()).await } /// Gets a heuristic recommendation of max fee per gas and max priority fee per gas for /// EIP-1559 compatible transactions. async fn estimate_eip1559_fees( &self, estimator: Option>) -> (U256, U256)>, ) -> Result<(U256, U256), Self::Error> { let base_fee_per_gas = self .get_block(BlockNumber::Latest) .await? .ok_or_else(|| ProviderError::CustomError("Latest block not found".into()))? .base_fee_per_gas .ok_or_else(|| ProviderError::CustomError("EIP-1559 not activated".into()))?; let fee_history = self .fee_history( utils::EIP1559_FEE_ESTIMATION_PAST_BLOCKS, BlockNumber::Latest, &[utils::EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE], ) .await?; // use the provided fee estimator function, or fallback to the default implementation. let (max_fee_per_gas, max_priority_fee_per_gas) = if let Some(es) = estimator { es(base_fee_per_gas, fee_history.reward) } else { utils::eip1559_default_estimator(base_fee_per_gas, fee_history.reward) }; Ok((max_fee_per_gas, max_priority_fee_per_gas)) } /// Gets the accounts on the node async fn get_accounts(&self) -> Result, ProviderError> { self.request("eth_accounts", ()).await } /// Returns the nonce of the address async fn get_transaction_count + Send + Sync>( &self, from: T, block: Option, ) -> Result { let from = match from.into() { NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?, NameOrAddress::Address(addr) => addr, }; let from = utils::serialize(&from); let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); self.request("eth_getTransactionCount", [from, block]).await } /// Returns the account's balance async fn get_balance + Send + Sync>( &self, from: T, block: Option, ) -> Result { let from = match from.into() { NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?, NameOrAddress::Address(addr) => addr, }; let from = utils::serialize(&from); let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); self.request("eth_getBalance", [from, block]).await } /// Returns the currently configured chain id, a value used in replay-protected /// transaction signing as introduced by EIP-155. async fn get_chainid(&self) -> Result { self.request("eth_chainId", ()).await } ////// Contract Execution // // These are relatively low-level calls. The Contracts API should usually be used instead. /// Sends the read-only (constant) transaction to a single Ethereum node and return the result /// (as bytes) of executing it. This is free, since it does not change any state on the /// blockchain. async fn call( &self, tx: &TypedTransaction, block: Option, ) -> Result { let tx = utils::serialize(tx); let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); self.request("eth_call", [tx, block]).await } /// Sends a transaction to a single Ethereum node and return the estimated amount of gas /// required (as a U256) to send it This is free, but only an estimate. Providing too little /// gas will result in a transaction being rejected (while still consuming all provided /// gas). async fn estimate_gas(&self, tx: &TypedTransaction) -> Result { self.request("eth_estimateGas", [tx]).await } async fn create_access_list( &self, tx: &TypedTransaction, block: Option, ) -> Result { let tx = utils::serialize(tx); let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); self.request("eth_createAccessList", [tx, block]).await } /// Sends the transaction to the entire Ethereum network and returns the transaction's hash /// This will consume gas from the account that signed the transaction. async fn send_transaction + Send + Sync>( &self, tx: T, block: Option, ) -> Result, ProviderError> { let mut tx = tx.into(); self.fill_transaction(&mut tx, block).await?; let tx_hash = self.request("eth_sendTransaction", [tx]).await?; Ok(PendingTransaction::new(tx_hash, self).interval(self.get_interval())) } /// Send the raw RLP encoded transaction to the entire Ethereum network and returns the /// transaction's hash This will consume gas from the account that signed the transaction. async fn send_raw_transaction<'a>( &'a self, tx: Bytes, ) -> Result, ProviderError> { let rlp = utils::serialize(&tx); let tx_hash = self.request("eth_sendRawTransaction", [rlp]).await?; Ok(PendingTransaction::new(tx_hash, self).interval(self.get_interval())) } /// The JSON-RPC provider is at the bottom-most position in the middleware stack. Here we check /// if it has the key for the sender address unlocked, as well as supports the `eth_sign` call. async fn is_signer(&self) -> bool { match self.from { Some(sender) => self.sign(vec![], &sender).await.is_ok(), None => false, } } /// Signs data using a specific account. This account needs to be unlocked. async fn sign + Send + Sync>( &self, data: T, from: &Address, ) -> Result { let data = utils::serialize(&data.into()); let from = utils::serialize(from); // get the response from `eth_sign` call and trim the 0x-prefix if present. let sig: String = self.request("eth_sign", [from, data]).await?; let sig = sig.strip_prefix("0x").unwrap_or(&sig); // decode the signature. let sig = hex::decode(sig)?; Ok(Signature::try_from(sig.as_slice()) .map_err(|e| ProviderError::CustomError(e.to_string()))?) } /// Sign a transaction via RPC call async fn sign_transaction( &self, _tx: &TypedTransaction, _from: Address, ) -> Result { Err(ProviderError::SignerUnavailable).map_err(FromErr::from) } ////// Contract state /// Returns an array (possibly empty) of logs that match the filter async fn get_logs(&self, filter: &Filter) -> Result, ProviderError> { self.request("eth_getLogs", [filter]).await } /// Streams matching filter logs async fn watch<'a>( &'a self, filter: &Filter, ) -> Result, ProviderError> { let id = self.new_filter(FilterKind::Logs(filter)).await?; let filter = FilterWatcher::new(id, self).interval(self.get_interval()); Ok(filter) } /// Streams new block hashes async fn watch_blocks(&self) -> Result, ProviderError> { let id = self.new_filter(FilterKind::NewBlocks).await?; let filter = FilterWatcher::new(id, self).interval(self.get_interval()); Ok(filter) } /// Streams pending transactions async fn watch_pending_transactions( &self, ) -> Result, ProviderError> { let id = self.new_filter(FilterKind::PendingTransactions).await?; let filter = FilterWatcher::new(id, self).interval(self.get_interval()); Ok(filter) } /// Creates a filter object, based on filter options, to notify when the state changes (logs). /// To check if the state has changed, call `get_filter_changes` with the filter id. async fn new_filter(&self, filter: FilterKind<'_>) -> Result { let (method, args) = match filter { FilterKind::NewBlocks => ("eth_newBlockFilter", vec![]), FilterKind::PendingTransactions => ("eth_newPendingTransactionFilter", vec![]), FilterKind::Logs(filter) => ("eth_newFilter", vec![utils::serialize(&filter)]), }; self.request(method, args).await } /// Uninstalls a filter async fn uninstall_filter + Send + Sync>( &self, id: T, ) -> Result { let id = utils::serialize(&id.into()); self.request("eth_uninstallFilter", [id]).await } /// Polling method for a filter, which returns an array of logs which occurred since last poll. /// /// This method must be called with one of the following return types, depending on the filter /// type: /// - `eth_newBlockFilter`: [`H256`], returns block hashes /// - `eth_newPendingTransactionFilter`: [`H256`], returns transaction hashes /// - `eth_newFilter`: [`Log`], returns raw logs /// /// If one of these types is not used, decoding will fail and the method will /// return an error. /// /// [`H256`]: ethers_core::types::H256 /// [`Log`]: ethers_core::types::Log async fn get_filter_changes(&self, id: T) -> Result, ProviderError> where T: Into + Send + Sync, R: Serialize + DeserializeOwned + Send + Sync + Debug, { let id = utils::serialize(&id.into()); self.request("eth_getFilterChanges", [id]).await } /// Get the storage of an address for a particular slot location async fn get_storage_at + Send + Sync>( &self, from: T, location: H256, block: Option, ) -> Result { let from = match from.into() { NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?, NameOrAddress::Address(addr) => addr, }; let from = utils::serialize(&from); let location = utils::serialize(&location); let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); // get the hex encoded value. let value: String = self.request("eth_getStorageAt", [from, location, block]).await?; // get rid of the 0x prefix and left pad it with zeroes. let value = format!("{:0>64}", value.replace("0x", "")); Ok(H256::from_slice(&Vec::from_hex(value)?)) } /// Returns the deployed code at a given address async fn get_code + Send + Sync>( &self, at: T, block: Option, ) -> Result { let at = match at.into() { NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?, NameOrAddress::Address(addr) => addr, }; let at = utils::serialize(&at); let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); self.request("eth_getCode", [at, block]).await } /// Returns the EIP-1186 proof response /// https://github.com/ethereum/EIPs/issues/1186 async fn get_proof + Send + Sync>( &self, from: T, locations: Vec, block: Option, ) -> Result { let from = match from.into() { NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?, NameOrAddress::Address(addr) => addr, }; let from = utils::serialize(&from); let locations = locations.iter().map(|location| utils::serialize(&location)).collect(); let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into())); self.request("eth_getProof", [from, locations, block]).await } ////// Ethereum Naming Service // The Ethereum Naming Service (ENS) allows easy to remember and use names to // be assigned to Ethereum addresses. Any provider operation which takes an address // may also take an ENS name. // // ENS also provides the ability for a reverse lookup, which determines the name for an address // if it has been configured. /// Returns the address that the `ens_name` resolves to (or None if not configured). /// /// # Panics /// /// If the bytes returned from the ENS registrar/resolver cannot be interpreted as /// an address. This should theoretically never happen. async fn resolve_name(&self, ens_name: &str) -> Result { self.query_resolver(ParamType::Address, ens_name, ens::ADDR_SELECTOR).await } /// Returns the ENS name the `address` resolves to (or None if not configured). /// # Panics /// /// If the bytes returned from the ENS registrar/resolver cannot be interpreted as /// a string. This should theoretically never happen. async fn lookup_address(&self, address: Address) -> Result { let ens_name = ens::reverse_address(address); self.query_resolver(ParamType::String, &ens_name, ens::NAME_SELECTOR).await } /// Returns the details of all transactions currently pending for inclusion in the next /// block(s), as well as the ones that are being scheduled for future execution only. /// Ref: [Here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content) async fn txpool_content(&self) -> Result { self.request("txpool_content", ()).await } /// Returns a summary of all the transactions currently pending for inclusion in the next /// block(s), as well as the ones that are being scheduled for future execution only. /// Ref: [Here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_inspect) async fn txpool_inspect(&self) -> Result { self.request("txpool_inspect", ()).await } /// Returns the number of transactions currently pending for inclusion in the next block(s), as /// well as the ones that are being scheduled for future execution only. /// Ref: [Here](https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_status) async fn txpool_status(&self) -> Result { self.request("txpool_status", ()).await } /// 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 { let req = req.into(); let req = utils::serialize(&req); let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest)); let trace_type = utils::serialize(&trace_type); self.request("trace_call", [req, trace_type, block]).await } /// 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 { let data = utils::serialize(&data); let trace_type = utils::serialize(&trace_type); self.request("trace_rawTransaction", [data, trace_type]).await } /// Replays a transaction, returning the traces async fn trace_replay_transaction( &self, hash: H256, trace_type: Vec, ) -> Result { let hash = utils::serialize(&hash); let trace_type = utils::serialize(&trace_type); self.request("trace_replayTransaction", [hash, trace_type]).await } /// 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, ProviderError> { let block = utils::serialize(&block); let trace_type = utils::serialize(&trace_type); self.request("trace_replayBlockTransactions", [block, trace_type]).await } /// Returns traces created at given block async fn trace_block(&self, block: BlockNumber) -> Result, ProviderError> { let block = utils::serialize(&block); self.request("trace_block", [block]).await } /// Return traces matching the given filter async fn trace_filter(&self, filter: TraceFilter) -> Result, ProviderError> { let filter = utils::serialize(&filter); self.request("trace_filter", vec![filter]).await } /// Returns trace at the given position async fn trace_get + Send + Sync>( &self, hash: H256, index: Vec, ) -> Result { let hash = utils::serialize(&hash); let index: Vec = index.into_iter().map(|i| i.into()).collect(); let index = utils::serialize(&index); self.request("trace_get", vec![hash, index]).await } /// Returns all traces of a given transaction async fn trace_transaction(&self, hash: H256) -> Result, ProviderError> { let hash = utils::serialize(&hash); self.request("trace_transaction", vec![hash]).await } async fn subscribe( &self, params: T, ) -> Result, ProviderError> where T: Debug + Serialize + Send + Sync, R: DeserializeOwned + Send + Sync, P: PubsubClient, { let id: U256 = self.request("eth_subscribe", params).await?; SubscriptionStream::new(id, self).map_err(Into::into) } async fn unsubscribe(&self, id: T) -> Result where T: Into + Send + Sync, P: PubsubClient, { self.request("eth_unsubscribe", [id.into()]).await } async fn subscribe_blocks( &self, ) -> Result>, ProviderError> where P: PubsubClient, { self.subscribe(["newHeads"]).await } async fn subscribe_pending_txs( &self, ) -> Result, ProviderError> where P: PubsubClient, { self.subscribe(["newPendingTransactions"]).await } async fn subscribe_logs<'a>( &'a self, filter: &Filter, ) -> Result, ProviderError> where P: PubsubClient, { let logs = utils::serialize(&"logs"); // TODO: Make this a static let filter = utils::serialize(filter); self.subscribe([logs, filter]).await } async fn fee_history + serde::Serialize + Send + Sync>( &self, block_count: T, last_block: BlockNumber, reward_percentiles: &[f64], ) -> Result { let last_block = utils::serialize(&last_block); let reward_percentiles = utils::serialize(&reward_percentiles); // The blockCount param is expected to be an unsigned integer up to geth v1.10.6. // Geth v1.10.7 onwards, this has been updated to a hex encoded form. Failure to // decode the param from client side would fallback to the old API spec. self.request( "eth_feeHistory", [utils::serialize(&block_count), last_block.clone(), reward_percentiles.clone()], ) .await .or(self .request( "eth_feeHistory", [utils::serialize(&block_count.into().as_u64()), last_block, reward_percentiles], ) .await) } } impl Provider

{ async fn query_resolver( &self, param: ParamType, ens_name: &str, selector: Selector, ) -> Result { // Get the ENS address, prioritize the local override variable let ens_addr = self.ens.unwrap_or(ens::ENS_ADDRESS); // first get the resolver responsible for this name // the call will return a Bytes array which we convert to an address let data = self.call(&ens::get_resolver(ens_addr, ens_name).into(), None).await?; let resolver_address: Address = decode_bytes(ParamType::Address, data); if resolver_address == Address::zero() { return Err(ProviderError::EnsError(ens_name.to_owned())) } // resolve let data = self.call(&ens::resolve(resolver_address, selector, ens_name).into(), None).await?; Ok(decode_bytes(param, data)) } #[cfg(test)] /// ganache-only function for mining empty blocks pub async fn mine(&self, num_blocks: usize) -> Result<(), ProviderError> { for _ in 0..num_blocks { self.inner.request::<_, U256>("evm_mine", None::<()>).await.map_err(Into::into)?; } Ok(()) } /// Sets the ENS Address (default: mainnet) pub fn ens>(mut self, ens: T) -> Self { self.ens = Some(ens.into()); self } /// Sets the default polling interval for event filters and pending transactions /// (default: 7 seconds) pub fn interval>(mut self, interval: T) -> Self { self.interval = Some(interval.into()); self } /// Gets the polling interval which the provider currently uses for event filters /// and pending transactions (default: 7 seconds) pub fn get_interval(&self) -> Duration { self.interval.unwrap_or(DEFAULT_POLL_INTERVAL) } } #[cfg(feature = "ws")] impl Provider { /// Direct connection to a websocket endpoint #[cfg(not(target_arch = "wasm32"))] pub async fn connect( url: impl tokio_tungstenite::tungstenite::client::IntoClientRequest + Unpin, ) -> Result { let ws = crate::Ws::connect(url).await?; Ok(Self::new(ws)) } /// Direct connection to a websocket endpoint #[cfg(target_arch = "wasm32")] pub async fn connect(url: &str) -> Result { let ws = crate::Ws::connect(url).await?; Ok(Self::new(ws)) } } #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "ipc")] impl Provider { /// Direct connection to an IPC socket. pub async fn connect_ipc(path: impl AsRef) -> Result { let ipc = crate::Ipc::connect(path).await?; Ok(Self::new(ipc)) } } impl Provider> { /// Provider that uses a quorum pub fn quorum(inner: QuorumProvider) -> Self { Self::new(inner) } } impl Provider { /// Returns a `Provider` instantiated with an internal "mock" transport. /// /// # Example /// /// ``` /// # async fn foo() -> Result<(), Box> { /// use ethers_core::types::U64; /// use ethers_providers::{Middleware, Provider}; /// // Instantiate the provider /// let (provider, mock) = Provider::mocked(); /// // Push the mock response /// mock.push(U64::from(12))?; /// // Make the call /// let blk = provider.get_block_number().await.unwrap(); /// // The response matches /// assert_eq!(blk.as_u64(), 12); /// // and the request as well! /// mock.assert_request("eth_blockNumber", ()).unwrap(); /// # Ok(()) /// # } /// ``` pub fn mocked() -> (Self, MockProvider) { let mock = MockProvider::new(); let mock_clone = mock.clone(); (Self::new(mock), mock_clone) } } /// infallible conversion of Bytes to Address/String /// /// # Panics /// /// If the provided bytes were not an interpretation of an address fn decode_bytes(param: ParamType, bytes: Bytes) -> T { let tokens = abi::decode(&[param], bytes.as_ref()) .expect("could not abi-decode bytes to address tokens"); T::from_tokens(tokens).expect("could not parse tokens as address") } impl TryFrom<&str> for Provider { type Error = ParseError; fn try_from(src: &str) -> Result { Ok(Provider::new(HttpProvider::new(Url::parse(src)?))) } } impl TryFrom for Provider { type Error = ParseError; fn try_from(src: String) -> Result { Provider::try_from(src.as_str()) } } #[cfg(test)] #[cfg(not(target_arch = "wasm32"))] mod tests { use super::*; use crate::Http; use ethers_core::{ types::{TransactionRequest, H256}, utils::Geth, }; use futures_util::StreamExt; const INFURA: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; #[tokio::test] // Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2 async fn mainnet_resolve_name() { let provider = Provider::::try_from(INFURA).unwrap(); let addr = provider.resolve_name("registrar.firefly.eth").await.unwrap(); assert_eq!(addr, "6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap()); // registrar not found provider.resolve_name("asdfasdffads").await.unwrap_err(); // name not found provider.resolve_name("asdfasdf.registrar.firefly.eth").await.unwrap_err(); } #[tokio::test] // Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2 async fn mainnet_lookup_address() { let provider = Provider::::try_from(INFURA).unwrap(); let name = provider .lookup_address("6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap()) .await .unwrap(); assert_eq!(name, "registrar.firefly.eth"); provider .lookup_address("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".parse().unwrap()) .await .unwrap_err(); } #[tokio::test] #[cfg_attr(feature = "celo", ignore)] async fn test_new_block_filter() { let num_blocks = 3; let geth = Geth::new().block_time(2u64).spawn(); let provider = Provider::::try_from(geth.endpoint()) .unwrap() .interval(Duration::from_millis(1000)); let start_block = provider.get_block_number().await.unwrap(); let stream = provider.watch_blocks().await.unwrap().stream(); let hashes: Vec = stream.take(num_blocks).collect::>().await; for (i, hash) in hashes.iter().enumerate() { let block = provider.get_block(start_block + i as u64 + 1).await.unwrap().unwrap(); assert_eq!(*hash, block.hash.unwrap()); } } #[tokio::test] #[cfg_attr(feature = "celo", ignore)] async fn test_is_signer() { use ethers_core::utils::Ganache; use std::str::FromStr; let ganache = Ganache::new().spawn(); let provider = Provider::::try_from(ganache.endpoint()) .unwrap() .with_sender(ganache.addresses()[0]); assert!(provider.is_signer().await); let provider = Provider::::try_from(ganache.endpoint()).unwrap(); assert!(!provider.is_signer().await); let sender = Address::from_str("635B4764D1939DfAcD3a8014726159abC277BecC") .expect("should be able to parse hex address"); let provider = Provider::::try_from( "https://ropsten.infura.io/v3/fd8b88b56aa84f6da87b60f5441d6778", ) .unwrap() .with_sender(sender); assert!(!provider.is_signer().await); } #[tokio::test] async fn test_new_pending_txs_filter() { let num_txs = 5; let geth = Geth::new().block_time(2u64).spawn(); let provider = Provider::::try_from(geth.endpoint()) .unwrap() .interval(Duration::from_millis(1000)); let accounts = provider.get_accounts().await.unwrap(); let stream = provider.watch_pending_transactions().await.unwrap().stream(); let mut tx_hashes = Vec::new(); let tx = TransactionRequest::new().from(accounts[0]).to(accounts[0]).value(1e18 as u64); for _ in 0..num_txs { tx_hashes.push(provider.send_transaction(tx.clone(), None).await.unwrap()); } let hashes: Vec = stream.take(num_txs).collect::>().await; assert_eq!(tx_hashes, hashes); } #[tokio::test] async fn receipt_on_unmined_tx() { use ethers_core::{ types::TransactionRequest, utils::{parse_ether, Ganache}, }; let ganache = Ganache::new().block_time(2u64).spawn(); let provider = Provider::::try_from(ganache.endpoint()).unwrap(); let accounts = provider.get_accounts().await.unwrap(); let tx = TransactionRequest::pay(accounts[0], parse_ether(1u64).unwrap()).from(accounts[0]); let pending_tx = provider.send_transaction(tx, None).await.unwrap(); assert!(provider.get_transaction_receipt(*pending_tx).await.unwrap().is_none()); let hash = *pending_tx; let receipt = pending_tx.await.unwrap().unwrap(); assert_eq!(receipt.transaction_hash, hash); } #[tokio::test] async fn parity_block_receipts() { let url = match std::env::var("PARITY") { Ok(inner) => inner, _ => return, }; let provider = Provider::::try_from(url.as_str()).unwrap(); let receipts = provider.get_block_receipts(10657200).await.unwrap(); assert!(!receipts.is_empty()); } #[tokio::test] // Celo blocks can not get parsed when used with Ganache #[cfg(not(feature = "celo"))] async fn block_subscribe() { use ethers_core::utils::Ganache; use futures_util::StreamExt; let ganache = Ganache::new().block_time(2u64).spawn(); let provider = Provider::connect(ganache.ws_endpoint()).await.unwrap(); let stream = provider.subscribe_blocks().await.unwrap(); let blocks = stream.take(3).map(|x| x.number.unwrap().as_u64()).collect::>().await; assert_eq!(blocks, vec![1, 2, 3]); } #[tokio::test] #[cfg_attr(feature = "celo", ignore)] async fn fee_history() { let provider = Provider::::try_from( "https://goerli.infura.io/v3/fd8b88b56aa84f6da87b60f5441d6778", ) .unwrap(); let history = provider.fee_history(10u64, BlockNumber::Latest, &[10.0, 40.0]).await.unwrap(); dbg!(&history); } }