2020-06-15 08:46:07 +00:00
|
|
|
use crate::{
|
|
|
|
ens,
|
2020-11-30 09:33:06 +00:00
|
|
|
pubsub::{PubsubClient, SubscriptionStream},
|
2020-09-23 08:04:54 +00:00
|
|
|
stream::{FilterWatcher, DEFAULT_POLL_INTERVAL},
|
2020-11-27 12:57:44 +00:00
|
|
|
FromErr, Http as HttpProvider, JsonRpcClient, MockProvider, PendingTransaction,
|
2020-06-15 08:46:07 +00:00
|
|
|
};
|
2020-05-27 11:32:44 +00:00
|
|
|
|
2020-05-31 16:01:34 +00:00
|
|
|
use ethers_core::{
|
2020-05-28 09:04:12 +00:00
|
|
|
abi::{self, Detokenize, ParamType},
|
2020-05-31 16:01:34 +00:00
|
|
|
types::{
|
2020-10-31 10:44:08 +00:00
|
|
|
Address, Block, BlockId, BlockNumber, BlockTrace, Bytes, Filter, Log, NameOrAddress,
|
|
|
|
Selector, Signature, Trace, TraceFilter, TraceType, Transaction, TransactionReceipt,
|
|
|
|
TransactionRequest, TxHash, TxpoolContent, TxpoolInspect, TxpoolStatus, H256, U256, U64,
|
2020-05-31 16:01:34 +00:00
|
|
|
},
|
|
|
|
utils,
|
2020-05-24 15:40:16 +00:00
|
|
|
};
|
|
|
|
|
2021-06-15 12:22:53 +00:00
|
|
|
#[cfg(feature = "celo")]
|
|
|
|
use crate::CeloMiddleware;
|
2020-09-24 21:33:09 +00:00
|
|
|
use crate::Middleware;
|
|
|
|
use async_trait::async_trait;
|
2021-01-22 09:25:22 +00:00
|
|
|
use hex::FromHex;
|
2020-11-30 09:33:06 +00:00
|
|
|
use serde::{de::DeserializeOwned, Serialize};
|
2020-06-01 21:31:32 +00:00
|
|
|
use thiserror::Error;
|
2020-05-26 10:24:19 +00:00
|
|
|
use url::{ParseError, Url};
|
|
|
|
|
2020-06-22 08:44:08 +00:00
|
|
|
use std::{convert::TryFrom, fmt::Debug, time::Duration};
|
2020-12-24 20:23:05 +00:00
|
|
|
use tracing::trace;
|
|
|
|
use tracing_futures::Instrument;
|
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
|
2020-06-20 13:55:07 +00:00
|
|
|
/// 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
|
2020-09-24 21:33:09 +00:00
|
|
|
/// # 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)]
|
2020-09-24 21:33:09 +00:00
|
|
|
// TODO: Convert to proper struct
|
|
|
|
pub struct Provider<P>(P, Option<Address>, Option<Duration>, Option<Address>);
|
|
|
|
|
2020-11-27 12:57:44 +00:00
|
|
|
impl<P> AsRef<P> for Provider<P> {
|
|
|
|
fn as_ref(&self) -> &P {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-24 21:33:09 +00:00
|
|
|
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-12-24 20:23:05 +00:00
|
|
|
|
|
|
|
#[error(transparent)]
|
|
|
|
SerdeJson(#[from] serde_json::Error),
|
2021-01-22 09:25:22 +00:00
|
|
|
|
|
|
|
#[error(transparent)]
|
|
|
|
HexError(#[from] hex::FromHexError),
|
2021-01-28 06:51:53 +00:00
|
|
|
|
|
|
|
#[error("custom error: {0}")]
|
|
|
|
CustomError(String),
|
2020-06-01 21:31:32 +00:00
|
|
|
}
|
2020-05-24 15:40:16 +00:00
|
|
|
|
2020-06-15 08:46:07 +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 {
|
2020-09-24 21:33:09 +00:00
|
|
|
Self(provider, None, None, None)
|
2020-05-24 15:40:16 +00:00
|
|
|
}
|
|
|
|
|
2020-09-24 21:33:09 +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
|
|
|
}
|
|
|
|
|
2020-12-24 20:23:05 +00:00
|
|
|
async fn request<T, R>(&self, method: &str, params: T) -> Result<R, ProviderError>
|
|
|
|
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.0.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<Tx: Default + Serialize + DeserializeOwned + Debug>(
|
2020-05-24 20:11:47 +00:00
|
|
|
&self,
|
|
|
|
id: BlockId,
|
|
|
|
include_txs: bool,
|
2020-09-17 11:06:56 +00:00
|
|
|
) -> 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-12-24 20:23:05 +00:00
|
|
|
self.request("eth_getBlockByHash", [hash, include_txs])
|
|
|
|
.await?
|
2020-05-24 20:11:47 +00:00
|
|
|
}
|
|
|
|
BlockId::Number(num) => {
|
|
|
|
let num = utils::serialize(&num);
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_getBlockByNumber", [num, include_txs])
|
|
|
|
.await?
|
2020-05-24 20:11:47 +00:00
|
|
|
}
|
2020-06-01 21:31:32 +00:00
|
|
|
})
|
2020-05-24 20:11:47 +00:00
|
|
|
}
|
2020-09-24 21:33:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 12:22:53 +00:00
|
|
|
#[cfg(feature = "celo")]
|
|
|
|
#[async_trait]
|
|
|
|
impl<P: JsonRpcClient> CeloMiddleware for Provider<P> {
|
2021-06-22 11:56:10 +00:00
|
|
|
async fn get_validators_bls_public_keys<T: Into<BlockId> + Send + Sync>(
|
2021-06-15 12:22:53 +00:00
|
|
|
&self,
|
2021-06-22 11:56:10 +00:00
|
|
|
block_id: T,
|
|
|
|
) -> Result<Vec<String>, ProviderError> {
|
|
|
|
let block_id = utils::serialize(&block_id.into());
|
|
|
|
self.request("istanbul_getValidatorsBLSPublicKeys", [block_id])
|
2021-06-15 12:22:53 +00:00
|
|
|
.await
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-08 15:56:36 +00:00
|
|
|
#[async_trait]
|
2020-09-24 21:33:09 +00:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
|
2020-12-17 11:26:01 +00:00
|
|
|
fn provider(&self) -> &Provider<Self::Provider> {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-09-24 21:33:09 +00:00
|
|
|
////// 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> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_blockNumber", ()).await
|
2020-09-24 21:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.get_block_gen(block_hash_or_number.into(), false).await
|
2020-09-24 21:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.get_block_gen(block_hash_or_number.into(), true).await
|
2020-09-24 21:33:09 +00:00
|
|
|
}
|
2020-05-24 20:11:47 +00:00
|
|
|
|
2020-05-27 11:32:44 +00:00
|
|
|
/// Gets the transaction with `transaction_hash`
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_transaction<T: Send + Sync + Into<TxHash>>(
|
2020-05-27 11:32:44 +00:00
|
|
|
&self,
|
|
|
|
transaction_hash: T,
|
2020-09-17 11:06:56 +00:00
|
|
|
) -> Result<Option<Transaction>, ProviderError> {
|
2020-05-27 11:32:44 +00:00
|
|
|
let hash = transaction_hash.into();
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_getTransactionByHash", [hash]).await
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the transaction receipt with `transaction_hash`
|
2020-09-24 21:33:09 +00:00
|
|
|
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,
|
2020-09-17 11:06:56 +00:00
|
|
|
) -> Result<Option<TransactionReceipt>, ProviderError> {
|
2020-05-27 11:32:44 +00:00
|
|
|
let hash = transaction_hash.into();
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_getTransactionReceipt", [hash]).await
|
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
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_gas_price(&self) -> Result<U256, ProviderError> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_gasPrice", ()).await
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the accounts on the node
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_accounts(&self) -> Result<Vec<Address>, ProviderError> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_accounts", ()).await
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the nonce of the address
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_transaction_count<T: Into<NameOrAddress> + Send + Sync>(
|
2020-05-24 15:40:16 +00:00
|
|
|
&self,
|
2020-09-24 21:33:09 +00:00
|
|
|
from: T,
|
2021-03-16 19:46:07 +00:00
|
|
|
block: Option<BlockId>,
|
2020-06-01 21:31:32 +00:00
|
|
|
) -> Result<U256, ProviderError> {
|
2020-06-15 14:16:14 +00:00
|
|
|
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);
|
2021-03-16 19:46:07 +00:00
|
|
|
let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_getTransactionCount", [from, block]).await
|
2020-05-24 15:40:16 +00:00
|
|
|
}
|
|
|
|
|
2020-05-27 11:32:44 +00:00
|
|
|
/// Returns the account's balance
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_balance<T: Into<NameOrAddress> + Send + Sync>(
|
2020-05-27 11:32:44 +00:00
|
|
|
&self,
|
2020-09-24 21:33:09 +00:00
|
|
|
from: T,
|
2021-03-16 19:46:07 +00:00
|
|
|
block: Option<BlockId>,
|
2020-06-01 21:31:32 +00:00
|
|
|
) -> Result<U256, ProviderError> {
|
2020-06-15 14:16:14 +00:00
|
|
|
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);
|
2021-03-16 19:46:07 +00:00
|
|
|
let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_getBalance", [from, block]).await
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|
2020-05-24 18:34:56 +00:00
|
|
|
|
2020-05-31 21:17:50 +00:00
|
|
|
/// Returns the currently configured chain id, a value used in replay-protected
|
|
|
|
/// transaction signing as introduced by EIP-155.
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_chainid(&self) -> Result<U256, ProviderError> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_chainId", ()).await
|
2020-05-31 21:17:50 +00:00
|
|
|
}
|
|
|
|
|
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.
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn call(
|
2020-05-25 15:35:38 +00:00
|
|
|
&self,
|
2020-06-02 10:36:02 +00:00
|
|
|
tx: &TransactionRequest,
|
2021-03-16 19:46:07 +00:00
|
|
|
block: Option<BlockId>,
|
2020-06-01 21:31:32 +00:00
|
|
|
) -> Result<Bytes, ProviderError> {
|
2020-06-02 10:36:02 +00:00
|
|
|
let tx = utils::serialize(tx);
|
2021-03-16 19:46:07 +00:00
|
|
|
let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_call", [tx, block]).await
|
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).
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn estimate_gas(&self, tx: &TransactionRequest) -> Result<U256, ProviderError> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_estimateGas", [tx]).await
|
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.
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn send_transaction(
|
2020-06-01 21:31:32 +00:00
|
|
|
&self,
|
|
|
|
mut tx: TransactionRequest,
|
2021-03-16 19:46:07 +00:00
|
|
|
_: Option<BlockId>,
|
2020-12-17 11:26:01 +00:00
|
|
|
) -> Result<PendingTransaction<'_, P>, ProviderError> {
|
2020-09-24 21:33:09 +00:00
|
|
|
if tx.from.is_none() {
|
|
|
|
tx.from = self.3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if tx.gas.is_none() {
|
|
|
|
tx.gas = Some(self.estimate_gas(&tx).await?);
|
|
|
|
}
|
|
|
|
|
2021-02-16 17:10:26 +00:00
|
|
|
if let Some(NameOrAddress::Name(ref ens_name)) = tx.to {
|
|
|
|
// resolve to an address
|
|
|
|
let addr = self.resolve_name(&ens_name).await?;
|
2020-06-01 21:31:32 +00:00
|
|
|
|
2021-02-16 17:10:26 +00:00
|
|
|
// set the value
|
|
|
|
tx.to = Some(addr.into())
|
2020-05-27 20:43:02 +00:00
|
|
|
}
|
|
|
|
|
2020-12-24 20:23:05 +00:00
|
|
|
let tx_hash = self.request("eth_sendTransaction", [tx]).await?;
|
2020-12-17 11:26:01 +00:00
|
|
|
|
|
|
|
Ok(PendingTransaction::new(tx_hash, self).interval(self.get_interval()))
|
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.
|
2020-12-17 11:26:01 +00:00
|
|
|
async fn send_raw_transaction<'a>(
|
|
|
|
&'a self,
|
|
|
|
tx: &Transaction,
|
|
|
|
) -> Result<PendingTransaction<'a, P>, ProviderError> {
|
2020-05-24 16:29:04 +00:00
|
|
|
let rlp = utils::serialize(&tx.rlp());
|
2020-12-24 20:23:05 +00:00
|
|
|
let tx_hash = self.request("eth_sendRawTransaction", [rlp]).await?;
|
2020-12-17 11:26:01 +00:00
|
|
|
Ok(PendingTransaction::new(tx_hash, self).interval(self.get_interval()))
|
2020-05-24 15:40:16 +00:00
|
|
|
}
|
|
|
|
|
2021-01-28 06:51:53 +00:00
|
|
|
/// 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.3 {
|
|
|
|
Some(sender) => self.sign(vec![], &sender).await.is_ok(),
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-02 21:10:46 +00:00
|
|
|
/// Signs data using a specific account. This account needs to be unlocked.
|
2020-09-24 21:33:09 +00:00
|
|
|
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);
|
2021-01-28 06:51:53 +00:00
|
|
|
|
|
|
|
// 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()))?)
|
2020-06-02 21:10:46 +00:00
|
|
|
}
|
|
|
|
|
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
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_logs(&self, filter: &Filter) -> Result<Vec<Log>, ProviderError> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_getLogs", [filter]).await
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|
|
|
|
|
2020-06-15 08:46:07 +00:00
|
|
|
/// Streams matching filter logs
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn watch<'a>(
|
|
|
|
&'a self,
|
|
|
|
filter: &Filter,
|
|
|
|
) -> Result<FilterWatcher<'a, P, Log>, ProviderError> {
|
2020-06-15 08:46:07 +00:00
|
|
|
let id = self.new_filter(FilterKind::Logs(filter)).await?;
|
2020-09-23 08:04:54 +00:00
|
|
|
let filter = FilterWatcher::new(id, self).interval(self.get_interval());
|
2020-06-22 08:44:08 +00:00
|
|
|
Ok(filter)
|
2020-06-15 08:46:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Streams new block hashes
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn watch_blocks(&self) -> Result<FilterWatcher<'_, P, H256>, ProviderError> {
|
2020-06-15 08:46:07 +00:00
|
|
|
let id = self.new_filter(FilterKind::NewBlocks).await?;
|
2020-09-23 08:04:54 +00:00
|
|
|
let filter = FilterWatcher::new(id, self).interval(self.get_interval());
|
2020-06-22 08:44:08 +00:00
|
|
|
Ok(filter)
|
2020-06-15 08:46:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Streams pending transactions
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn watch_pending_transactions(
|
2020-06-15 08:46:07 +00:00
|
|
|
&self,
|
2020-09-23 08:04:54 +00:00
|
|
|
) -> Result<FilterWatcher<'_, P, H256>, ProviderError> {
|
2020-06-15 08:46:07 +00:00
|
|
|
let id = self.new_filter(FilterKind::PendingTransactions).await?;
|
2020-09-23 08:04:54 +00:00
|
|
|
let filter = FilterWatcher::new(id, self).interval(self.get_interval());
|
2020-06-22 08:44:08 +00:00
|
|
|
Ok(filter)
|
2020-06-15 08:46:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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.
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn new_filter(&self, filter: FilterKind<'_>) -> Result<U256, ProviderError> {
|
2020-06-15 08:46:07 +00:00
|
|
|
let (method, args) = match filter {
|
2020-06-17 13:09:41 +00:00
|
|
|
FilterKind::NewBlocks => ("eth_newBlockFilter", vec![]),
|
|
|
|
FilterKind::PendingTransactions => ("eth_newPendingTransactionFilter", vec![]),
|
|
|
|
FilterKind::Logs(filter) => ("eth_newFilter", vec![utils::serialize(&filter)]),
|
2020-06-15 08:46:07 +00:00
|
|
|
};
|
|
|
|
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request(method, args).await
|
2020-06-15 08:46:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Uninstalls a filter
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn uninstall_filter<T: Into<U256> + Send + Sync>(
|
|
|
|
&self,
|
|
|
|
id: T,
|
|
|
|
) -> Result<bool, ProviderError> {
|
2020-06-15 08:46:07 +00:00
|
|
|
let id = utils::serialize(&id.into());
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_uninstallFilter", [id]).await
|
2020-06-15 08:46:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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:
|
2020-06-20 13:55:07 +00:00
|
|
|
/// - `eth_newBlockFilter`: [`H256`], returns block hashes
|
|
|
|
/// - `eth_newPendingTransactionFilter`: [`H256`], returns transaction hashes
|
|
|
|
/// - `eth_newFilter`: [`Log`], returns raw logs
|
2020-06-15 08:46:07 +00:00
|
|
|
///
|
|
|
|
/// If one of these types is not used, decoding will fail and the method will
|
|
|
|
/// return an error.
|
2020-06-20 13:55:07 +00:00
|
|
|
///
|
|
|
|
/// [`H256`]: ethers_core::types::H256
|
|
|
|
/// [`Log`]: ethers_core::types::Log
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_filter_changes<T, R>(&self, id: T) -> Result<Vec<R>, ProviderError>
|
2020-06-15 08:46:07 +00:00
|
|
|
where
|
2020-09-24 21:33:09 +00:00
|
|
|
T: Into<U256> + Send + Sync,
|
2020-12-24 20:23:05 +00:00
|
|
|
R: Serialize + DeserializeOwned + Send + Sync + Debug,
|
2020-06-15 08:46:07 +00:00
|
|
|
{
|
|
|
|
let id = utils::serialize(&id.into());
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_getFilterChanges", [id]).await
|
2020-06-15 08:46:07 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 02:56:28 +00:00
|
|
|
/// Get the storage of an address for a particular slot location
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_storage_at<T: Into<NameOrAddress> + Send + Sync>(
|
2020-07-31 02:56:28 +00:00
|
|
|
&self,
|
2020-09-24 21:33:09 +00:00
|
|
|
from: T,
|
2020-07-31 02:56:28 +00:00
|
|
|
location: H256,
|
2021-03-16 19:46:07 +00:00
|
|
|
block: Option<BlockId>,
|
2020-07-31 02:56:28 +00:00
|
|
|
) -> 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);
|
2021-03-16 19:46:07 +00:00
|
|
|
let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));
|
2021-01-22 09:25:22 +00:00
|
|
|
|
|
|
|
// 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)?))
|
2020-07-31 02:56:28 +00:00
|
|
|
}
|
2020-06-15 20:10:27 +00:00
|
|
|
|
|
|
|
/// Returns the deployed code at a given address
|
2020-09-24 21:33:09 +00:00
|
|
|
async fn get_code<T: Into<NameOrAddress> + Send + Sync>(
|
2020-06-15 20:10:27 +00:00
|
|
|
&self,
|
2020-09-24 21:33:09 +00:00
|
|
|
at: T,
|
2021-03-16 19:46:07 +00:00
|
|
|
block: Option<BlockId>,
|
2020-06-15 20:10:27 +00:00
|
|
|
) -> 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);
|
2021-03-16 19:46:07 +00:00
|
|
|
let block = utils::serialize(&block.unwrap_or_else(|| BlockNumber::Latest.into()));
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_getCode", [at, block]).await
|
2020-06-15 20:10:27 +00:00
|
|
|
}
|
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.
|
2020-09-24 21:33:09 +00:00
|
|
|
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.
|
2020-09-24 21:33:09 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-10-24 08:13:13 +00:00
|
|
|
/// 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<TxpoolContent, ProviderError> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("txpool_content", ()).await
|
2020-10-24 08:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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<TxpoolInspect, ProviderError> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("txpool_inspect", ()).await
|
2020-10-24 08:13:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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<TxpoolStatus, ProviderError> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("txpool_status", ()).await
|
2020-10-24 08:13:13 +00:00
|
|
|
}
|
2020-10-31 10:44:08 +00:00
|
|
|
|
|
|
|
/// Executes the given call and returns a number of possible traces for it
|
|
|
|
async fn trace_call(
|
|
|
|
&self,
|
|
|
|
req: TransactionRequest,
|
|
|
|
trace_type: Vec<TraceType>,
|
|
|
|
block: Option<BlockNumber>,
|
|
|
|
) -> Result<BlockTrace, ProviderError> {
|
|
|
|
let req = utils::serialize(&req);
|
|
|
|
let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest));
|
|
|
|
let trace_type = utils::serialize(&trace_type);
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("trace_call", [req, trace_type, block]).await
|
2020-10-31 10:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Traces a call to `eth_sendRawTransaction` without making the call, returning the traces
|
|
|
|
async fn trace_raw_transaction(
|
|
|
|
&self,
|
|
|
|
data: Bytes,
|
|
|
|
trace_type: Vec<TraceType>,
|
|
|
|
) -> Result<BlockTrace, ProviderError> {
|
|
|
|
let data = utils::serialize(&data);
|
|
|
|
let trace_type = utils::serialize(&trace_type);
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("trace_rawTransaction", [data, trace_type])
|
2020-10-31 10:44:08 +00:00
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Replays a transaction, returning the traces
|
|
|
|
async fn trace_replay_transaction(
|
|
|
|
&self,
|
|
|
|
hash: H256,
|
|
|
|
trace_type: Vec<TraceType>,
|
|
|
|
) -> Result<BlockTrace, ProviderError> {
|
|
|
|
let hash = utils::serialize(&hash);
|
|
|
|
let trace_type = utils::serialize(&trace_type);
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("trace_replayTransaction", [hash, trace_type])
|
2020-10-31 10:44:08 +00:00
|
|
|
.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<TraceType>,
|
|
|
|
) -> Result<Vec<BlockTrace>, ProviderError> {
|
|
|
|
let block = utils::serialize(&block);
|
|
|
|
let trace_type = utils::serialize(&trace_type);
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("trace_replayBlockTransactions", [block, trace_type])
|
2020-10-31 10:44:08 +00:00
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns traces created at given block
|
|
|
|
async fn trace_block(&self, block: BlockNumber) -> Result<Vec<Trace>, ProviderError> {
|
|
|
|
let block = utils::serialize(&block);
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("trace_block", [block]).await
|
2020-10-31 10:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return traces matching the given filter
|
|
|
|
async fn trace_filter(&self, filter: TraceFilter) -> Result<Vec<Trace>, ProviderError> {
|
|
|
|
let filter = utils::serialize(&filter);
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("trace_filter", vec![filter]).await
|
2020-10-31 10:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns trace at the given position
|
|
|
|
async fn trace_get<T: Into<U64> + Send + Sync>(
|
|
|
|
&self,
|
|
|
|
hash: H256,
|
|
|
|
index: Vec<T>,
|
|
|
|
) -> Result<Trace, ProviderError> {
|
|
|
|
let hash = utils::serialize(&hash);
|
|
|
|
let index: Vec<U64> = index.into_iter().map(|i| i.into()).collect();
|
|
|
|
let index = utils::serialize(&index);
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("trace_get", vec![hash, index]).await
|
2020-10-31 10:44:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns all traces of a given transaction
|
|
|
|
async fn trace_transaction(&self, hash: H256) -> Result<Vec<Trace>, ProviderError> {
|
|
|
|
let hash = utils::serialize(&hash);
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("trace_transaction", vec![hash]).await
|
2020-10-31 10:44:08 +00:00
|
|
|
}
|
2020-11-19 17:30:10 +00:00
|
|
|
|
|
|
|
/// Returns all receipts for that block. Must be done on a parity node.
|
|
|
|
async fn parity_block_receipts<T: Into<BlockNumber> + Send + Sync>(
|
|
|
|
&self,
|
|
|
|
block: T,
|
|
|
|
) -> Result<Vec<TransactionReceipt>, Self::Error> {
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("parity_getBlockReceipts", vec![block.into()])
|
2020-11-19 17:30:10 +00:00
|
|
|
.await
|
|
|
|
}
|
2020-11-30 09:33:06 +00:00
|
|
|
|
|
|
|
async fn subscribe<T, R>(
|
|
|
|
&self,
|
|
|
|
params: T,
|
|
|
|
) -> Result<SubscriptionStream<'_, P, R>, ProviderError>
|
|
|
|
where
|
|
|
|
T: Debug + Serialize + Send + Sync,
|
|
|
|
R: DeserializeOwned + Send + Sync,
|
|
|
|
P: PubsubClient,
|
|
|
|
{
|
2020-12-24 20:23:05 +00:00
|
|
|
let id: U256 = self.request("eth_subscribe", params).await?;
|
2020-11-30 09:33:06 +00:00
|
|
|
SubscriptionStream::new(id, self).map_err(Into::into)
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn unsubscribe<T>(&self, id: T) -> Result<bool, ProviderError>
|
|
|
|
where
|
|
|
|
T: Into<U256> + Send + Sync,
|
|
|
|
P: PubsubClient,
|
|
|
|
{
|
2020-12-24 20:23:05 +00:00
|
|
|
self.request("eth_unsubscribe", [id.into()]).await
|
2020-11-30 09:33:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async fn subscribe_blocks(
|
|
|
|
&self,
|
|
|
|
) -> Result<SubscriptionStream<'_, P, Block<TxHash>>, ProviderError>
|
|
|
|
where
|
|
|
|
P: PubsubClient,
|
|
|
|
{
|
|
|
|
self.subscribe(["newHeads"]).await
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn subscribe_pending_txs(
|
|
|
|
&self,
|
|
|
|
) -> Result<SubscriptionStream<'_, P, TxHash>, ProviderError>
|
|
|
|
where
|
|
|
|
P: PubsubClient,
|
|
|
|
{
|
|
|
|
self.subscribe(["newPendingTransactions"]).await
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn subscribe_logs<'a>(
|
|
|
|
&'a self,
|
|
|
|
filter: &Filter,
|
|
|
|
) -> Result<SubscriptionStream<'a, P, Log>, ProviderError>
|
|
|
|
where
|
|
|
|
P: PubsubClient,
|
|
|
|
{
|
|
|
|
let logs = utils::serialize(&"logs"); // TODO: Make this a static
|
|
|
|
let filter = utils::serialize(filter);
|
|
|
|
self.subscribe([logs, filter]).await
|
|
|
|
}
|
2020-09-24 21:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2020-06-01 21:58:21 +00:00
|
|
|
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
|
2020-06-02 10:36:02 +00:00
|
|
|
.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
|
2020-06-02 10:36:02 +00:00
|
|
|
.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
|
|
|
}
|
|
|
|
|
2020-06-15 08:46:07 +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
|
|
|
}
|
2020-06-22 08:44:08 +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-12-31 17:19:14 +00:00
|
|
|
#[cfg(feature = "ws")]
|
2020-11-30 09:33:06 +00:00
|
|
|
impl Provider<crate::Ws> {
|
|
|
|
/// Direct connection to a websocket endpoint
|
|
|
|
pub async fn connect(
|
2020-12-31 17:19:14 +00:00
|
|
|
url: impl tokio_tungstenite::tungstenite::client::IntoClientRequest + Unpin,
|
2020-11-30 09:33:06 +00:00
|
|
|
) -> Result<Self, ProviderError> {
|
|
|
|
let ws = crate::Ws::connect(url).await?;
|
|
|
|
Ok(Self::new(ws))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-08 06:52:31 +00:00
|
|
|
#[cfg(feature = "ipc")]
|
|
|
|
impl Provider<crate::Ipc> {
|
|
|
|
/// Direct connection to an IPC socket.
|
|
|
|
pub async fn connect_ipc(path: impl AsRef<std::path::Path>) -> Result<Self, ProviderError> {
|
2021-04-08 08:44:48 +00:00
|
|
|
let ipc = crate::Ipc::connect(path).await?;
|
2021-04-08 06:52:31 +00:00
|
|
|
Ok(Self::new(ipc))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-27 12:57:44 +00:00
|
|
|
impl Provider<MockProvider> {
|
|
|
|
/// Returns a `Provider` instantiated with an internal "mock" transport.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
|
|
|
|
/// use ethers::{types::U64, 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-31 08:21:20 +00:00
|
|
|
/// infallible conversion of Bytes to Address/String
|
2020-05-27 11:32:44 +00:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// If the provided bytes were not an interpretation of an address
|
|
|
|
fn decode_bytes<T: Detokenize>(param: ParamType, bytes: Bytes) -> T {
|
2020-12-31 17:19:14 +00:00
|
|
|
let tokens = abi::decode(&[param], &bytes.as_ref())
|
|
|
|
.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> {
|
2020-09-24 21:33:09 +00:00
|
|
|
Ok(Provider(
|
|
|
|
HttpProvider::new(Url::parse(src)?),
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
))
|
2020-05-27 11:32:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-22 13:42:34 +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
|
|
|
}
|
|
|
|
}
|
2020-06-15 08:46:07 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-09-17 11:06:56 +00:00
|
|
|
use crate::Http;
|
2020-06-15 08:46:07 +00:00
|
|
|
use ethers_core::types::H256;
|
2021-06-05 17:09:12 +00:00
|
|
|
use ethers_core::utils::Geth;
|
2020-06-15 08:46:07 +00:00
|
|
|
use futures_util::StreamExt;
|
|
|
|
|
|
|
|
#[tokio::test]
|
2021-06-05 17:09:12 +00:00
|
|
|
#[cfg_attr(feature = "celo", ignore)]
|
2020-06-15 08:46:07 +00:00
|
|
|
async fn test_new_block_filter() {
|
|
|
|
let num_blocks = 3;
|
2021-06-05 17:09:12 +00:00
|
|
|
let geth = Geth::new().block_time(2u64).spawn();
|
|
|
|
let provider = Provider::<Http>::try_from(geth.endpoint())
|
2020-06-22 08:44:08 +00:00
|
|
|
.unwrap()
|
|
|
|
.interval(Duration::from_millis(1000));
|
2021-06-05 17:09:12 +00:00
|
|
|
|
2020-06-15 08:46:07 +00:00
|
|
|
let start_block = provider.get_block_number().await.unwrap();
|
|
|
|
|
2020-06-22 08:44:08 +00:00
|
|
|
let stream = provider.watch_blocks().await.unwrap().stream();
|
2020-06-15 08:46:07 +00:00
|
|
|
|
|
|
|
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
|
2020-09-17 11:06:56 +00:00
|
|
|
.unwrap()
|
2020-06-15 08:46:07 +00:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(*hash, block.hash.unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-28 06:51:53 +00:00
|
|
|
#[tokio::test]
|
2021-06-10 18:27:17 +00:00
|
|
|
#[cfg_attr(feature = "celo", ignore)]
|
2021-01-28 06:51:53 +00:00
|
|
|
async fn test_is_signer() {
|
|
|
|
use ethers_core::utils::Ganache;
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
let ganache = Ganache::new().spawn();
|
|
|
|
let provider = Provider::<Http>::try_from(ganache.endpoint())
|
|
|
|
.unwrap()
|
|
|
|
.with_sender(ganache.addresses()[0]);
|
2021-07-05 11:03:38 +00:00
|
|
|
assert!(provider.is_signer().await);
|
2021-01-28 06:51:53 +00:00
|
|
|
|
|
|
|
let provider = Provider::<Http>::try_from(ganache.endpoint()).unwrap();
|
2021-07-05 11:03:38 +00:00
|
|
|
assert!(!provider.is_signer().await);
|
2021-01-28 06:51:53 +00:00
|
|
|
|
|
|
|
let sender = Address::from_str("635B4764D1939DfAcD3a8014726159abC277BecC")
|
|
|
|
.expect("should be able to parse hex address");
|
|
|
|
let provider = Provider::<Http>::try_from(
|
|
|
|
"https://ropsten.infura.io/v3/fd8b88b56aa84f6da87b60f5441d6778",
|
|
|
|
)
|
|
|
|
.unwrap()
|
|
|
|
.with_sender(sender);
|
2021-07-05 11:03:38 +00:00
|
|
|
assert!(!provider.is_signer().await);
|
2021-01-28 06:51:53 +00:00
|
|
|
}
|
|
|
|
|
2020-06-15 08:46:07 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_new_pending_txs_filter() {
|
|
|
|
let num_txs = 5;
|
|
|
|
|
2021-06-05 17:09:12 +00:00
|
|
|
let geth = Geth::new().block_time(2u64).spawn();
|
|
|
|
let provider = Provider::<Http>::try_from(geth.endpoint())
|
2020-06-22 08:44:08 +00:00
|
|
|
.unwrap()
|
|
|
|
.interval(Duration::from_millis(1000));
|
2020-06-15 08:46:07 +00:00
|
|
|
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 {
|
2020-09-24 21:33:09 +00:00
|
|
|
tx_hashes.push(provider.send_transaction(tx.clone(), None).await.unwrap());
|
2020-06-15 08:46:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let hashes: Vec<H256> = stream.take(num_txs).collect::<Vec<H256>>().await;
|
|
|
|
assert_eq!(tx_hashes, hashes);
|
|
|
|
}
|
2020-09-17 11:06:56 +00:00
|
|
|
|
|
|
|
#[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]);
|
2020-12-17 11:26:01 +00:00
|
|
|
let pending_tx = provider.send_transaction(tx, None).await.unwrap();
|
2020-09-17 11:06:56 +00:00
|
|
|
|
|
|
|
assert!(provider
|
2020-12-17 11:26:01 +00:00
|
|
|
.get_transaction_receipt(*pending_tx)
|
2020-09-17 11:06:56 +00:00
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.is_none());
|
|
|
|
|
2020-12-17 11:26:01 +00:00
|
|
|
let hash = *pending_tx;
|
|
|
|
let receipt = pending_tx.await.unwrap();
|
|
|
|
assert_eq!(receipt.transaction_hash, hash);
|
2020-09-17 11:06:56 +00:00
|
|
|
}
|
2020-11-19 17:30:10 +00:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn parity_block_receipts() {
|
|
|
|
let url = match std::env::var("PARITY") {
|
|
|
|
Ok(inner) => inner,
|
|
|
|
_ => return,
|
|
|
|
};
|
|
|
|
let provider = Provider::<Http>::try_from(url.as_str()).unwrap();
|
|
|
|
let receipts = provider.parity_block_receipts(10657200).await.unwrap();
|
|
|
|
assert!(!receipts.is_empty());
|
|
|
|
}
|
2020-11-30 09:33:06 +00:00
|
|
|
|
|
|
|
#[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::<Vec<_>>()
|
|
|
|
.await;
|
|
|
|
assert_eq!(blocks, vec![1, 2, 3]);
|
|
|
|
}
|
2020-06-15 08:46:07 +00:00
|
|
|
}
|