ethers-rs/ethers-providers/src/provider.rs

748 lines
24 KiB
Rust
Raw Normal View History

use crate::{
ens,
stream::{FilterWatcher, DEFAULT_POLL_INTERVAL},
FromErr, Http as HttpProvider, JsonRpcClient, PendingTransaction,
};
2020-05-27 11:32:44 +00:00
2020-05-31 16:01:34 +00:00
use ethers_core::{
abi::{self, Detokenize, ParamType},
2020-05-31 16:01:34 +00:00
types::{
Address, Block, BlockId, BlockNumber, Bytes, Filter, Log, NameOrAddress, Selector,
Signature, Transaction, TransactionReceipt, TransactionRequest, TxHash, H256, U256, U64,
2020-05-31 16:01:34 +00:00
},
utils,
2020-05-24 15:40:16 +00:00
};
use crate::Middleware;
use async_trait::async_trait;
2020-05-26 10:24:19 +00:00
use serde::Deserialize;
2020-06-01 21:31:32 +00:00
use thiserror::Error;
2020-05-26 10:24:19 +00:00
use url::{ParseError, Url};
use std::{convert::TryFrom, fmt::Debug, time::Duration};
2020-05-24 15:40:16 +00:00
/// An abstract provider for interacting with the [Ethereum JSON RPC
2020-05-28 16:34:06 +00:00
/// 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.)
2020-06-10 08:58:27 +00:00
///
/// # Example
///
/// ```no_run
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
/// use ethers::providers::{Middleware, Provider, Http};
2020-06-10 08:58:27 +00:00
/// use std::convert::TryFrom;
///
/// let provider = Provider::<Http>::try_from(
/// "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"
/// ).expect("could not instantiate HTTP Provider");
///
/// let block = provider.get_block(100u64).await?;
2020-06-11 07:16:38 +00:00
/// println!("Got block: {}", serde_json::to_string(&block)?);
2020-06-10 08:58:27 +00:00
/// # Ok(())
2020-06-11 07:16:38 +00:00
/// # }
2020-06-10 08:58:27 +00:00
/// ```
2020-05-24 15:40:16 +00:00
#[derive(Clone, Debug)]
// TODO: Convert to proper struct
pub struct Provider<P>(P, Option<Address>, Option<Duration>, Option<Address>);
impl FromErr<ProviderError> for ProviderError {
fn from(src: ProviderError) -> Self {
src
}
}
2020-06-01 21:31:32 +00:00
#[derive(Debug, Error)]
/// An error thrown when making a call to the provider
pub enum ProviderError {
2020-06-10 08:58:27 +00:00
/// An internal error in the JSON RPC Client
2020-06-02 10:58:48 +00:00
#[error(transparent)]
JsonRpcClientError(#[from] Box<dyn std::error::Error + Send + Sync>),
2020-06-10 08:58:27 +00:00
/// An error during ENS name resolution
2020-06-01 21:31:32 +00:00
#[error("ens name not found: {0}")]
EnsError(String),
}
2020-05-24 15:40:16 +00:00
/// 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,
}
2020-05-24 15:40:16 +00:00
// JSON RPC bindings
2020-06-01 23:00:58 +00:00
impl<P: JsonRpcClient> Provider<P> {
2020-06-04 18:44:02 +00:00
/// Instantiate a new provider with a backend.
pub fn new(provider: P) -> Self {
Self(provider, None, None, None)
2020-05-24 15:40:16 +00:00
}
pub fn with_sender(mut self, address: impl Into<Address>) -> Self {
self.3 = Some(address.into());
self
2020-05-24 20:11:47 +00:00
}
async fn get_block_gen<Tx: for<'a> Deserialize<'a>>(
&self,
id: BlockId,
include_txs: bool,
) -> Result<Option<Block<Tx>>, ProviderError> {
2020-05-24 20:11:47 +00:00
let include_txs = utils::serialize(&include_txs);
2020-06-01 21:31:32 +00:00
Ok(match id {
2020-05-24 20:11:47 +00:00
BlockId::Hash(hash) => {
let hash = utils::serialize(&hash);
2020-06-01 23:00:58 +00:00
self.0
.request("eth_getBlockByHash", [hash, include_txs])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?
2020-05-24 20:11:47 +00:00
}
BlockId::Number(num) => {
let num = utils::serialize(&num);
2020-06-01 23:00:58 +00:00
self.0
.request("eth_getBlockByNumber", [num, include_txs])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?
2020-05-24 20:11:47 +00:00
}
2020-06-01 21:31:32 +00:00
})
2020-05-24 20:11:47 +00:00
}
}
#[async_trait]
impl<P: JsonRpcClient> Middleware for Provider<P> {
type Error = ProviderError;
type Provider = P;
type Inner = Self;
fn inner(&self) -> &Self::Inner {
unreachable!("There is no inner provider here")
}
////// Blockchain Status
//
// Functions for querying the state of the blockchain
/// Gets the latest block number via the `eth_BlockNumber` API
async fn get_block_number(&self) -> Result<U64, ProviderError> {
Ok(self
.0
.request("eth_blockNumber", ())
.await
.map_err(Into::into)?)
}
/// Gets the block at `block_hash_or_number` (transaction hashes only)
async fn get_block<T: Into<BlockId> + Send + Sync>(
&self,
block_hash_or_number: T,
) -> Result<Option<Block<TxHash>>, Self::Error> {
Ok(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<T: Into<BlockId> + Send + Sync>(
&self,
block_hash_or_number: T,
) -> Result<Option<Block<Transaction>>, ProviderError> {
Ok(self
.get_block_gen(block_hash_or_number.into(), true)
.await?)
}
2020-05-24 20:11:47 +00:00
2020-05-27 11:32:44 +00:00
/// Gets the transaction with `transaction_hash`
async fn get_transaction<T: Send + Sync + Into<TxHash>>(
2020-05-27 11:32:44 +00:00
&self,
transaction_hash: T,
) -> Result<Option<Transaction>, ProviderError> {
2020-05-27 11:32:44 +00:00
let hash = transaction_hash.into();
2020-06-01 21:31:32 +00:00
Ok(self
.0
.request("eth_getTransactionByHash", [hash])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-27 11:32:44 +00:00
}
/// Gets the transaction receipt with `transaction_hash`
async fn get_transaction_receipt<T: Send + Sync + Into<TxHash>>(
2020-05-24 20:27:51 +00:00
&self,
2020-05-27 11:32:44 +00:00
transaction_hash: T,
) -> Result<Option<TransactionReceipt>, ProviderError> {
2020-05-27 11:32:44 +00:00
let hash = transaction_hash.into();
2020-06-01 21:31:32 +00:00
Ok(self
.0
.request("eth_getTransactionReceipt", [hash])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-24 20:27:51 +00:00
}
2020-05-27 11:32:44 +00:00
/// Gets the current gas price as estimated by the node
async fn get_gas_price(&self) -> Result<U256, ProviderError> {
2020-06-01 23:00:58 +00:00
Ok(self
.0
.request("eth_gasPrice", ())
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-27 11:32:44 +00:00
}
/// Gets the accounts on the node
async fn get_accounts(&self) -> Result<Vec<Address>, ProviderError> {
2020-06-01 23:00:58 +00:00
Ok(self
.0
.request("eth_accounts", ())
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-27 11:32:44 +00:00
}
/// Returns the nonce of the address
async fn get_transaction_count<T: Into<NameOrAddress> + Send + Sync>(
2020-05-24 15:40:16 +00:00
&self,
from: T,
2020-05-27 11:32:44 +00:00
block: Option<BlockNumber>,
2020-06-01 21:31:32 +00:00
) -> Result<U256, ProviderError> {
let from = match from.into() {
NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?,
NameOrAddress::Address(addr) => addr,
};
2020-05-27 11:32:44 +00:00
let from = utils::serialize(&from);
let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest));
2020-06-01 21:31:32 +00:00
Ok(self
.0
.request("eth_getTransactionCount", [from, block])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-24 15:40:16 +00:00
}
2020-05-27 11:32:44 +00:00
/// Returns the account's balance
async fn get_balance<T: Into<NameOrAddress> + Send + Sync>(
2020-05-27 11:32:44 +00:00
&self,
from: T,
2020-05-27 11:32:44 +00:00
block: Option<BlockNumber>,
2020-06-01 21:31:32 +00:00
) -> Result<U256, ProviderError> {
let from = match from.into() {
NameOrAddress::Name(ens_name) => self.resolve_name(&ens_name).await?,
NameOrAddress::Address(addr) => addr,
};
2020-05-27 11:32:44 +00:00
let from = utils::serialize(&from);
let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest));
2020-06-01 21:31:32 +00:00
Ok(self
.0
.request("eth_getBalance", [from, block])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-27 11:32:44 +00:00
}
2020-05-24 18:34:56 +00:00
/// 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<U256, ProviderError> {
2020-06-01 23:00:58 +00:00
Ok(self
.0
.request("eth_chainId", ())
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
}
2020-05-27 11:32:44 +00:00
////// Contract Execution
//
// These are relatively low-level calls. The Contracts API should usually be used instead.
2020-06-10 08:58:27 +00:00
/// Sends the read-only (constant) transaction to a single Ethereum node and return the result (as bytes) of executing it.
2020-05-27 11:32:44 +00:00
/// This is free, since it does not change any state on the blockchain.
async fn call(
2020-05-25 15:35:38 +00:00
&self,
tx: &TransactionRequest,
block: Option<BlockNumber>,
2020-06-01 21:31:32 +00:00
) -> Result<Bytes, ProviderError> {
let tx = utils::serialize(tx);
let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest));
2020-06-01 23:00:58 +00:00
Ok(self
.0
.request("eth_call", [tx, block])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-25 15:35:38 +00:00
}
2020-06-10 08:58:27 +00:00
/// Sends a transaction to a single Ethereum node and return the estimated amount of gas required (as a U256) to send it
2020-05-27 11:32:44 +00:00
/// 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: &TransactionRequest) -> Result<U256, ProviderError> {
2020-05-27 11:32:44 +00:00
let tx = utils::serialize(tx);
2020-06-01 23:00:58 +00:00
Ok(self
.0
.request("eth_estimateGas", [tx])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-27 11:32:44 +00:00
}
2020-06-10 08:58:27 +00:00
/// Sends the transaction to the entire Ethereum network and returns the transaction's hash
2020-05-27 11:32:44 +00:00
/// This will consume gas from the account that signed the transaction.
async fn send_transaction(
2020-06-01 21:31:32 +00:00
&self,
mut tx: TransactionRequest,
_: Option<BlockNumber>,
) -> Result<TxHash, ProviderError> {
if tx.from.is_none() {
tx.from = self.3;
}
if tx.gas.is_none() {
tx.gas = Some(self.estimate_gas(&tx).await?);
}
2020-05-27 20:43:02 +00:00
if let Some(ref to) = tx.to {
if let NameOrAddress::Name(ens_name) = to {
2020-06-01 21:31:32 +00:00
// resolve to an address
2020-06-11 07:16:38 +00:00
let addr = self.resolve_name(&ens_name).await?;
2020-06-01 21:31:32 +00:00
// set the value
2020-05-27 20:43:02 +00:00
tx.to = Some(addr.into())
}
}
Ok(self
2020-06-01 23:00:58 +00:00
.0
.request("eth_sendTransaction", [tx])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-24 15:40:16 +00:00
}
2020-05-27 11:32:44 +00:00
/// 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(&self, tx: &Transaction) -> Result<TxHash, ProviderError> {
let rlp = utils::serialize(&tx.rlp());
Ok(self
2020-06-01 23:00:58 +00:00
.0
.request("eth_sendRawTransaction", [rlp])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-24 15:40:16 +00:00
}
2020-06-02 21:10:46 +00:00
/// Signs data using a specific account. This account needs to be unlocked.
async fn sign<T: Into<Bytes> + Send + Sync>(
2020-06-10 12:21:16 +00:00
&self,
data: T,
from: &Address,
) -> Result<Signature, ProviderError> {
let data = utils::serialize(&data.into());
2020-06-02 21:10:46 +00:00
let from = utils::serialize(from);
Ok(self
.0
.request("eth_sign", [from, data])
2020-06-02 21:10:46 +00:00
.await
.map_err(Into::into)?)
}
2020-05-27 11:32:44 +00:00
////// Contract state
2020-05-24 17:26:41 +00:00
2020-05-27 11:32:44 +00:00
/// Returns an array (possibly empty) of logs that match the filter
async fn get_logs(&self, filter: &Filter) -> Result<Vec<Log>, ProviderError> {
2020-06-01 23:00:58 +00:00
Ok(self
.0
.request("eth_getLogs", [filter])
2020-06-01 23:00:58 +00:00
.await
.map_err(Into::into)?)
2020-05-27 11:32:44 +00:00
}
/// Streams matching filter logs
async fn watch<'a>(
&'a self,
filter: &Filter,
) -> Result<FilterWatcher<'a, P, Log>, 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<FilterWatcher<'_, P, H256>, 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<FilterWatcher<'_, P, H256>, 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<U256, ProviderError> {
let (method, args) = match filter {
FilterKind::NewBlocks => ("eth_newBlockFilter", vec![]),
FilterKind::PendingTransactions => ("eth_newPendingTransactionFilter", vec![]),
FilterKind::Logs(filter) => ("eth_newFilter", vec![utils::serialize(&filter)]),
};
Ok(self.0.request(method, args).await.map_err(Into::into)?)
}
/// Uninstalls a filter
async fn uninstall_filter<T: Into<U256> + Send + Sync>(
&self,
id: T,
) -> Result<bool, ProviderError> {
let id = utils::serialize(&id.into());
Ok(self
.0
.request("eth_uninstallFilter", [id])
.await
.map_err(Into::into)?)
}
/// 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<T, R>(&self, id: T) -> Result<Vec<R>, ProviderError>
where
T: Into<U256> + Send + Sync,
R: for<'a> Deserialize<'a> + Send + Sync,
{
let id = utils::serialize(&id.into());
Ok(self
.0
.request("eth_getFilterChanges", [id])
.await
.map_err(Into::into)?)
}
2020-07-31 02:56:28 +00:00
/// Get the storage of an address for a particular slot location
async fn get_storage_at<T: Into<NameOrAddress> + Send + Sync>(
2020-07-31 02:56:28 +00:00
&self,
from: T,
2020-07-31 02:56:28 +00:00
location: H256,
block: Option<BlockNumber>,
) -> Result<H256, ProviderError> {
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(BlockNumber::Latest));
Ok(self
.0
.request("eth_getStorageAt", [from, location, block])
.await
.map_err(Into::into)?)
}
/// Returns the deployed code at a given address
async fn get_code<T: Into<NameOrAddress> + Send + Sync>(
&self,
at: T,
block: Option<BlockNumber>,
) -> Result<Bytes, ProviderError> {
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(BlockNumber::Latest));
Ok(self
.0
.request("eth_getCode", [at, block])
.await
.map_err(Into::into)?)
}
2020-05-27 11:32:44 +00:00
////// 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<Address, ProviderError> {
2020-05-27 11:32:44 +00:00
self.query_resolver(ParamType::Address, ens_name, ens::ADDR_SELECTOR)
2020-05-24 15:40:16 +00:00
.await
}
2020-05-24 17:26:41 +00:00
2020-05-27 11:32:44 +00:00
/// Returns the ENS name the `address` resolves to (or None if not configured).
2020-05-28 16:34:06 +00:00
/// # 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<String, ProviderError> {
2020-05-27 11:32:44 +00:00
let ens_name = ens::reverse_address(address);
self.query_resolver(ParamType::String, &ens_name, ens::NAME_SELECTOR)
.await
}
/// Helper which creates a pending transaction object from a transaction hash
/// using the provider's polling interval
fn pending_transaction(&self, tx_hash: TxHash) -> PendingTransaction<'_, P> {
PendingTransaction::new(tx_hash, self).interval(self.get_interval())
}
}
impl<P: JsonRpcClient> Provider<P> {
2020-05-27 11:32:44 +00:00
async fn query_resolver<T: Detokenize>(
2020-05-24 17:26:41 +00:00
&self,
2020-05-27 11:32:44 +00:00
param: ParamType,
ens_name: &str,
selector: Selector,
2020-06-11 07:16:38 +00:00
) -> Result<T, ProviderError> {
2020-05-27 15:43:43 +00:00
// Get the ENS address, prioritize the local override variable
let ens_addr = self.1.unwrap_or(ens::ENS_ADDRESS);
2020-05-27 11:32:44 +00:00
// 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), None)
2020-05-27 11:32:44 +00:00
.await?;
let resolver_address: Address = decode_bytes(ParamType::Address, data);
if resolver_address == Address::zero() {
2020-06-11 08:40:07 +00:00
return Err(ProviderError::EnsError(ens_name.to_owned()));
2020-05-27 11:32:44 +00:00
}
// resolve
let data = self
.call(&ens::resolve(resolver_address, selector, ens_name), None)
2020-05-27 11:32:44 +00:00
.await?;
2020-06-11 07:16:38 +00:00
Ok(decode_bytes(param, data))
2020-05-27 11:32:44 +00:00
}
#[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.0
.request::<_, U256>("evm_mine", None::<()>)
.await
.map_err(Into::into)?;
}
Ok(())
}
2020-06-10 08:58:27 +00:00
/// Sets the ENS Address (default: mainnet)
2020-05-27 15:43:43 +00:00
pub fn ens<T: Into<Address>>(mut self, ens: T) -> Self {
2020-06-01 21:31:32 +00:00
self.1 = Some(ens.into());
2020-05-27 11:32:44 +00:00
self
2020-05-24 17:26:41 +00:00
}
/// Sets the default polling interval for event filters and pending transactions
/// (default: 7 seconds)
pub fn interval<T: Into<Duration>>(mut self, interval: T) -> Self {
self.2 = 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.2.unwrap_or(DEFAULT_POLL_INTERVAL)
}
2020-05-24 15:40:16 +00:00
}
2020-05-26 10:24:19 +00:00
2020-05-27 11:32:44 +00:00
/// infallbile conversion of Bytes to Address/String
///
/// # Panics
///
/// If the provided bytes were not an interpretation of an address
fn decode_bytes<T: Detokenize>(param: ParamType, bytes: Bytes) -> T {
let tokens =
abi::decode(&[param], &bytes.0).expect("could not abi-decode bytes to address tokens");
2020-05-27 11:32:44 +00:00
T::from_tokens(tokens).expect("could not parse tokens as address")
}
2020-06-01 21:31:32 +00:00
impl TryFrom<&str> for Provider<HttpProvider> {
2020-05-26 10:24:19 +00:00
type Error = ParseError;
fn try_from(src: &str) -> Result<Self, Self::Error> {
Ok(Provider(
HttpProvider::new(Url::parse(src)?),
None,
None,
None,
))
2020-05-27 11:32:44 +00:00
}
}
impl TryFrom<String> for Provider<HttpProvider> {
type Error = ParseError;
fn try_from(src: String) -> Result<Self, Self::Error> {
Provider::try_from(src.as_str())
}
}
2020-05-27 11:32:44 +00:00
#[cfg(test)]
mod ens_tests {
use super::*;
2020-06-02 10:58:48 +00:00
const INFURA: &str = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
2020-05-27 11:32:44 +00:00
#[tokio::test]
// Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2
async fn mainnet_resolve_name() {
2020-06-02 10:58:48 +00:00
let provider = Provider::<HttpProvider>::try_from(INFURA).unwrap();
2020-05-27 11:32:44 +00:00
let addr = provider
.resolve_name("registrar.firefly.eth")
.await
.unwrap();
assert_eq!(
2020-06-11 07:16:38 +00:00
addr,
2020-05-27 11:32:44 +00:00
"6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap()
);
// registrar not found
2020-06-11 07:16:38 +00:00
provider.resolve_name("asdfasdffads").await.unwrap_err();
2020-05-27 11:32:44 +00:00
// name not found
2020-06-11 07:16:38 +00:00
provider
2020-05-27 11:32:44 +00:00
.resolve_name("asdfasdf.registrar.firefly.eth")
.await
2020-06-11 07:16:38 +00:00
.unwrap_err();
2020-05-27 11:32:44 +00:00
}
#[tokio::test]
// Test vector from: https://docs.ethers.io/ethers.js/v5-beta/api-providers.html#id2
async fn mainnet_lookup_address() {
2020-06-02 10:58:48 +00:00
let provider = Provider::<HttpProvider>::try_from(INFURA).unwrap();
2020-05-27 11:32:44 +00:00
let name = provider
.lookup_address("6fC21092DA55B392b045eD78F4732bff3C580e2c".parse().unwrap())
.await
.unwrap();
2020-06-11 07:16:38 +00:00
assert_eq!(name, "registrar.firefly.eth");
2020-05-27 11:32:44 +00:00
2020-06-11 07:16:38 +00:00
provider
2020-05-27 11:32:44 +00:00
.lookup_address("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".parse().unwrap())
.await
2020-06-11 07:16:38 +00:00
.unwrap_err();
2020-05-26 10:24:19 +00:00
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Http;
use ethers_core::types::H256;
use futures_util::StreamExt;
#[tokio::test]
#[ignore]
// Ganache new block filters are super buggy! This test must be run with
// geth or parity running e.g. `geth --dev --rpc --dev.period 1`
async fn test_new_block_filter() {
let num_blocks = 3;
let provider = Provider::<HttpProvider>::try_from("http://localhost:8545")
.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<H256> = stream.take(num_blocks).collect::<Vec<H256>>().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());
}
}
// this must be run with geth or parity since ganache-core still does not support
// eth_pendingTransactions, https://github.com/trufflesuite/ganache-core/issues/405
// example command: `geth --dev --rpc --dev.period 1`
#[tokio::test]
#[ignore]
async fn test_new_pending_txs_filter() {
let num_txs = 5;
let provider = Provider::<HttpProvider>::try_from("http://localhost:8545")
.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<H256> = stream.take(num_txs).collect::<Vec<H256>>().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::<Http>::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 tx_hash = provider.send_transaction(tx, None).await.unwrap();
assert!(provider
.get_transaction_receipt(tx_hash)
.await
.unwrap()
.is_none());
// couple of seconds pass
std::thread::sleep(std::time::Duration::new(3, 0));
assert!(provider
.get_transaction_receipt(tx_hash)
.await
.unwrap()
.is_some());
}
}