2020-06-15 08:46:07 +00:00
use crate ::{
2022-06-04 21:18:25 +00:00
call_raw ::CallBuilder ,
2022-02-16 14:25:41 +00:00
ens , erc , maybe ,
2020-11-30 09:33:06 +00:00
pubsub ::{ PubsubClient , SubscriptionStream } ,
2020-09-23 08:04:54 +00:00
stream ::{ FilterWatcher , DEFAULT_POLL_INTERVAL } ,
2022-05-23 21:23:35 +00:00
FromErr , Http as HttpProvider , JsonRpcClient , JsonRpcClientWrapper , LogQuery , MockProvider ,
2022-04-13 20:21:52 +00:00
PendingTransaction , QuorumProvider , RwClient , SyncingStatus ,
2020-06-15 08:46:07 +00:00
} ;
2020-05-27 11:32:44 +00:00
2022-05-27 21:02:16 +00:00
#[ cfg(not(target_arch = " wasm32 " )) ]
2022-06-09 07:45:33 +00:00
use crate ::transports ::{ Authorization , HttpRateLimitRetryPolicy , RetryClient } ;
2022-05-27 21:02:16 +00:00
2021-09-18 06:01:40 +00:00
#[ cfg(feature = " celo " ) ]
use crate ::CeloMiddleware ;
use crate ::Middleware ;
use async_trait ::async_trait ;
2021-11-12 01:23:46 +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 ::{
2021-08-09 00:31:11 +00:00
transaction ::{ eip2718 ::TypedTransaction , eip2930 ::AccessListWithGasUsed } ,
2021-12-13 22:49:49 +00:00
Address , Block , BlockId , BlockNumber , BlockTrace , Bytes , EIP1186ProofResponse , FeeHistory ,
2022-05-16 15:11:25 +00:00
Filter , FilterBlockOption , 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-01-22 09:25:22 +00:00
use hex ::FromHex ;
2022-02-01 10:58:45 +00:00
use serde ::{ de ::DeserializeOwned , Deserialize , Serialize } ;
2020-06-01 21:31:32 +00:00
use thiserror ::Error ;
2020-05-26 10:24:19 +00:00
use url ::{ ParseError , Url } ;
2022-02-16 14:25:41 +00:00
use futures_util ::{ lock ::Mutex , try_join } ;
2022-05-16 15:11:25 +00:00
use std ::{
collections ::VecDeque , convert ::TryFrom , fmt ::Debug , str ::FromStr , sync ::Arc , 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
2021-10-30 13:50:46 +00:00
#[ derive(Copy, Clone) ]
pub enum NodeClient {
Geth ,
Erigon ,
OpenEthereum ,
Nethermind ,
Besu ,
}
impl FromStr for NodeClient {
type Err = ProviderError ;
fn from_str ( s : & str ) -> Result < Self , Self ::Err > {
2021-11-21 16:12:40 +00:00
match s . split ( '/' ) . next ( ) . unwrap ( ) . to_lowercase ( ) . as_str ( ) {
" geth " = > Ok ( NodeClient ::Geth ) ,
" erigon " = > Ok ( NodeClient ::Erigon ) ,
" openethereum " = > Ok ( NodeClient ::OpenEthereum ) ,
" nethermind " = > Ok ( NodeClient ::Nethermind ) ,
2021-10-30 13:50:46 +00:00
" besu " = > Ok ( NodeClient ::Besu ) ,
_ = > Err ( ProviderError ::UnsupportedNodeClient ) ,
}
}
}
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>> {
2021-08-28 21:06:29 +00:00
/// 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) ]
2021-08-08 21:10:40 +00:00
pub struct Provider < P > {
inner : P ,
ens : Option < Address > ,
interval : Option < Duration > ,
from : Option < Address > ,
2021-10-30 13:50:46 +00:00
/// Node client hasn't been checked yet = `None`
/// Unsupported node client = `Some(None)`
/// Supported node client = `Some(Some(NodeClient))`
_node_client : Arc < Mutex < Option < NodeClient > > > ,
2021-08-08 21:10:40 +00:00
}
2020-09-24 21:33:09 +00:00
2020-11-27 12:57:44 +00:00
impl < P > AsRef < P > for Provider < P > {
fn as_ref ( & self ) -> & P {
2021-08-08 21:10:40 +00:00
& self . inner
2020-11-27 12:57:44 +00:00
}
}
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
2022-03-08 14:45:45 +00:00
/// Invalid reverse ENS name
#[ error( " reverse ens name not pointing to itself: {0} " ) ]
EnsNotOwned ( 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
2022-02-16 14:25:41 +00:00
#[ error(transparent) ]
HTTPError ( #[ from ] reqwest ::Error ) ,
2021-01-28 06:51:53 +00:00
#[ error( " custom error: {0} " ) ]
CustomError ( String ) ,
2021-10-30 13:50:46 +00:00
#[ error( " unsupported RPC " ) ]
UnsupportedRPC ,
#[ error( " unsupported node client " ) ]
UnsupportedNodeClient ,
2021-11-14 12:26:02 +00:00
#[ error( " Attempted to sign a transaction with no available signer. Hint: did you mean to use a SignerMiddleware? " ) ]
SignerUnavailable ,
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 {
2021-10-30 13:50:46 +00:00
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 < NodeClient , ProviderError > {
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 ::< NodeClient > ( ) {
Ok ( res ) = > res ,
Err ( _ ) = > return Err ( ProviderError ::UnsupportedNodeClient ) ,
} ;
* node_client = Some ( client_version ) ;
Ok ( client_version )
}
2020-05-24 15:40:16 +00:00
}
2021-12-19 04:28:38 +00:00
#[ must_use ]
2020-09-24 21:33:09 +00:00
pub fn with_sender ( mut self , address : impl Into < Address > ) -> Self {
2021-08-08 21:10:40 +00:00
self . from = Some ( address . into ( ) ) ;
2020-09-24 21:33:09 +00:00
self
2020-05-24 20:11:47 +00:00
}
2022-02-17 12:30:03 +00:00
pub async fn request < T , R > ( & self , method : & str , params : T ) -> Result < R , ProviderError >
2020-12-24 20:23:05 +00:00
where
T : Debug + Serialize + Send + Sync ,
R : Serialize + DeserializeOwned + Debug ,
{
let span =
tracing ::trace_span! ( " rpc " , method = method , params = ? serde_json ::to_string ( & params ) ? ) ;
// https://docs.rs/tracing/0.1.22/tracing/span/struct.Span.html#in-asynchronous-code
let res = async move {
trace! ( " tx " ) ;
2021-10-29 12:29:35 +00:00
let res : R = self . inner . request ( method , params ) . await . map_err ( Into ::into ) ? ;
2020-12-24 20:23:05 +00:00
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 ) ;
2021-10-29 12:29:35 +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 ) ;
2021-10-29 12:29:35 +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
}
2022-06-04 21:18:25 +00:00
/// Analogous to [`Middleware::call`], but returns a [`CallBuilder`] that can either be
/// `.await`d or used to override the parameters sent to `eth_call`.
///
/// See the [`call_raw::spoof`] for functions to construct state override parameters.
///
/// Note: this method _does not_ send a transaction from your account
///
/// [`call_raw::spoof`]: crate::call_raw::spoof
///
/// # Example
/// ```no_run
/// # use ethers_core::{
/// # types::{Address, TransactionRequest, H256},
/// # utils::{parse_ether, Geth},
/// # };
/// # use ethers_providers::{Provider, Http, Middleware, call_raw::{RawCall, spoof}};
/// # use std::convert::TryFrom;
/// #
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let geth = Geth::new().spawn();
/// let provider = Provider::<Http>::try_from(geth.endpoint()).unwrap();
///
/// let adr1: Address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c".parse()?;
/// let adr2: Address = "0x295a70b2de5e3953354a6a8344e616ed314d7251".parse()?;
/// let pay_amt = parse_ether(1u64)?;
///
/// // Not enough ether to pay for the transaction
/// let tx = TransactionRequest::pay(adr2, pay_amt).from(adr1).into();
///
/// // override the sender's balance for the call
/// let mut state = spoof::balance(adr1, pay_amt * 2);
/// provider.call_raw(&tx).state(&state).await?;
/// # Ok(())
/// # }
/// ```
pub fn call_raw < ' a > ( & ' a self , tx : & ' a TypedTransaction ) -> CallBuilder < ' a , P > {
CallBuilder ::new ( self , tx )
}
2020-09-24 21:33:09 +00:00
}
2021-06-15 12:22:53 +00:00
#[ cfg(feature = " celo " ) ]
2021-08-23 09:56:44 +00:00
#[ cfg_attr(target_arch = " wasm32 " , async_trait(?Send)) ]
#[ cfg_attr(not(target_arch = " wasm32 " ), async_trait) ]
2021-06-15 12:22:53 +00:00
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 ( ) ) ;
2021-10-29 12:29:35 +00:00
self . request ( " istanbul_getValidatorsBLSPublicKeys " , [ block_id ] ) . await
2021-06-15 12:22:53 +00:00
}
}
2021-08-23 09:56:44 +00:00
#[ cfg_attr(target_arch = " wasm32 " , async_trait(?Send)) ]
#[ cfg_attr(not(target_arch = " wasm32 " ), 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
}
2021-08-09 00:31:11 +00:00
fn default_sender ( & self ) -> Option < Address > {
self . from
}
2020-09-24 21:33:09 +00:00
////// Blockchain Status
//
// Functions for querying the state of the blockchain
2021-08-08 21:10:40 +00:00
/// Returns the current client version using the `web3_clientVersion` RPC.
async fn client_version ( & self ) -> Result < String , Self ::Error > {
self . request ( " web3_clientVersion " , ( ) ) . await
}
2021-12-15 23:09:09 +00:00
async fn fill_transaction (
& self ,
tx : & mut TypedTransaction ,
block : Option < BlockId > ,
) -> Result < ( ) , Self ::Error > {
if let Some ( default_sender ) = self . default_sender ( ) {
if tx . from ( ) . is_none ( ) {
tx . set_from ( default_sender ) ;
}
}
2022-03-01 12:13:06 +00:00
// TODO: Join the name resolution and gas price future
2021-12-15 23:09:09 +00:00
// set the ENS name
if let Some ( NameOrAddress ::Name ( ref ens_name ) ) = tx . to ( ) {
let addr = self . resolve_name ( ens_name ) . await ? ;
tx . set_to ( addr ) ;
}
2022-03-01 12:13:06 +00:00
// fill gas price
2021-12-15 23:09:09 +00:00
match tx {
TypedTransaction ::Eip2930 ( _ ) | TypedTransaction ::Legacy ( _ ) = > {
let gas_price = maybe ( tx . gas_price ( ) , self . get_gas_price ( ) ) . await ? ;
tx . set_gas_price ( gas_price ) ;
}
TypedTransaction ::Eip1559 ( ref mut inner ) = > {
if inner . max_fee_per_gas . is_none ( ) | | inner . max_priority_fee_per_gas . is_none ( ) {
let ( max_fee_per_gas , max_priority_fee_per_gas ) =
self . estimate_eip1559_fees ( None ) . await ? ;
inner . max_fee_per_gas = Some ( max_fee_per_gas ) ;
inner . max_priority_fee_per_gas = Some ( max_priority_fee_per_gas ) ;
} ;
}
}
2022-03-01 12:13:06 +00:00
// If the tx has an access list but it is empty, it is an Eip1559 or Eip2930 tx,
// and we attempt to populate the acccess list. This may require `eth_estimateGas`,
// in which case we save the result in maybe_gas_res for later
let mut maybe_gas = None ;
if let Some ( starting_al ) = tx . access_list ( ) {
if starting_al . 0. is_empty ( ) {
let ( gas_res , al_res ) = futures_util ::join! (
maybe ( tx . gas ( ) . cloned ( ) , self . estimate_gas ( tx ) ) ,
self . create_access_list ( tx , block )
) ;
let mut gas = gas_res ? ;
if let Ok ( al_with_gas ) = al_res {
// Set access list if it saves gas over the estimated (or previously set) value
if al_with_gas . gas_used < gas {
// Update the gas estimate with the lower amount
gas = al_with_gas . gas_used ;
tx . set_access_list ( al_with_gas . access_list ) ;
}
}
maybe_gas = Some ( gas ) ;
}
}
// Set gas to estimated value only if it was not set by the caller,
// even if the access list has been populated and saves gas
if tx . gas ( ) . is_none ( ) {
let gas_estimate = maybe ( maybe_gas , self . estimate_gas ( tx ) ) . await ? ;
tx . set_gas ( gas_estimate ) ;
}
2021-12-15 23:09:09 +00:00
Ok ( ( ) )
}
2020-09-24 21:33:09 +00:00
/// 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
2021-08-19 14:04:11 +00:00
/// Gets the block uncle count at `block_hash_or_number`
async fn get_uncle_count < T : Into < BlockId > + Send + Sync > (
& self ,
block_hash_or_number : T ,
) -> Result < U256 , Self ::Error > {
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 ) ;
2021-10-29 12:29:35 +00:00
self . request ( " eth_getUncleCountByBlockNumber " , [ num ] ) . await ?
2021-08-19 14:04:11 +00:00
}
} )
}
/// Gets the block uncle at `block_hash_or_number` and `idx`
async fn get_uncle < T : Into < BlockId > + Send + Sync > (
& self ,
block_hash_or_number : T ,
idx : U64 ,
) -> Result < Option < Block < H256 > > , 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 ) ;
2021-10-29 12:29:35 +00:00
self . request ( " eth_getUncleByBlockHashAndIndex " , [ hash , idx ] ) . await ?
2021-08-19 14:04:11 +00:00
}
BlockId ::Number ( num ) = > {
let num = utils ::serialize ( & num ) ;
2021-10-29 12:29:35 +00:00
self . request ( " eth_getUncleByBlockNumberAndIndex " , [ num , idx ] ) . await ?
2021-08-19 14:04:11 +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
}
2021-08-08 21:10:40 +00:00
/// Returns all receipts for a block.
///
2021-11-21 16:14:35 +00:00
/// Note that this uses the `eth_getBlockReceipts` RPC, which is
/// non-standard and currently supported by Erigon.
2021-08-08 21:10:40 +00:00
async fn get_block_receipts < T : Into < BlockNumber > + Send + Sync > (
& self ,
block : T ,
) -> Result < Vec < TransactionReceipt > , Self ::Error > {
2021-11-21 16:14:35 +00:00
self . request ( " eth_getBlockReceipts " , [ block . into ( ) ] ) . await
}
2021-10-30 13:50:46 +00:00
2021-11-21 16:14:35 +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 > {
self . request ( " parity_getBlockReceipts " , vec! [ block . into ( ) ] ) . await
2021-08-08 21:10:40 +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
}
2021-08-19 14:01:40 +00:00
/// 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 < fn ( U256 , Vec < Vec < U256 > > ) -> ( 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 ) )
}
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
}
2022-02-01 10:58:45 +00:00
/// Return current client syncing status. If IsFalse sync is over.
async fn syncing ( & self ) -> Result < SyncingStatus , Self ::Error > {
#[ derive(Debug, Serialize, Deserialize) ]
#[ serde(untagged) ]
pub enum SyncingStatusIntermediate {
/// When client is synced to highest block, eth_syncing with return string "false"
IsFalse ( bool ) ,
/// When client is still syncing past blocks we get IsSyncing information.
IsSyncing { starting_block : U256 , current_block : U256 , highest_block : U256 } ,
}
let intermediate : SyncingStatusIntermediate = self . request ( " eth_syncing " , ( ) ) . await ? ;
match intermediate {
SyncingStatusIntermediate ::IsFalse ( false ) = > Ok ( SyncingStatus ::IsFalse ) ,
SyncingStatusIntermediate ::IsFalse ( true ) = > Err ( ProviderError ::CustomError (
" eth_syncing returned `true` that is undefined value. " . to_owned ( ) ,
) ) ,
SyncingStatusIntermediate ::IsSyncing {
starting_block ,
current_block ,
highest_block ,
} = > Ok ( SyncingStatus ::IsSyncing { starting_block , current_block , highest_block } ) ,
}
}
2021-11-18 18:56:31 +00:00
/// Returns the network version.
async fn get_net_version ( & self ) -> Result < U64 , ProviderError > {
self . request ( " net_version " , ( ) ) . await
}
2020-05-27 11:32:44 +00:00
////// Contract Execution
//
// These are relatively low-level calls. The Contracts API should usually be used instead.
2021-10-29 12:29:35 +00:00
/// 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.
2020-09-24 21:33:09 +00:00
async fn call (
2020-05-25 15:35:38 +00:00
& self ,
2021-08-09 00:31:11 +00:00
tx : & TypedTransaction ,
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
}
2021-10-29 12:29:35 +00:00
/// 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).
2021-08-09 00:31:11 +00:00
async fn estimate_gas ( & self , tx : & TypedTransaction ) -> 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
}
2021-08-09 00:31:11 +00:00
async fn create_access_list (
& self ,
tx : & TypedTransaction ,
block : Option < BlockId > ,
) -> Result < AccessListWithGasUsed , ProviderError > {
let tx = utils ::serialize ( tx ) ;
let block = utils ::serialize ( & block . unwrap_or_else ( | | BlockNumber ::Latest . into ( ) ) ) ;
self . request ( " eth_createAccessList " , [ tx , block ] ) . await
}
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.
2021-08-09 00:31:11 +00:00
async fn send_transaction < T : Into < TypedTransaction > + Send + Sync > (
2020-06-01 21:31:32 +00:00
& self ,
2021-08-09 00:31:11 +00:00
tx : T ,
block : Option < BlockId > ,
2020-12-17 11:26:01 +00:00
) -> Result < PendingTransaction < '_ , P > , ProviderError > {
2021-08-09 00:31:11 +00:00
let mut tx = tx . into ( ) ;
self . fill_transaction ( & mut tx , block ) . await ? ;
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
}
2021-10-29 12:29:35 +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 ,
2021-08-08 22:49:23 +00:00
tx : Bytes ,
2020-12-17 11:26:01 +00:00
) -> Result < PendingTransaction < ' a , P > , ProviderError > {
2021-08-08 22:49:23 +00:00
let rlp = utils ::serialize ( & tx ) ;
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 {
2021-08-08 21:10:40 +00:00
match self . from {
2021-01-28 06:51:53 +00:00
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
}
2021-11-14 12:26:02 +00:00
/// Sign a transaction via RPC call
async fn sign_transaction (
& self ,
_tx : & TypedTransaction ,
_from : Address ,
) -> Result < Signature , Self ::Error > {
Err ( ProviderError ::SignerUnavailable ) . map_err ( FromErr ::from )
}
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
}
2022-05-23 21:23:35 +00:00
fn get_logs_paginated < ' a > ( & ' a self , filter : & Filter , page_size : u64 ) -> LogQuery < ' a , P > {
LogQuery ::new ( self , filter ) . with_page_size ( page_size )
}
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 ,
} ;
2022-05-18 14:49:32 +00:00
// position is a QUANTITY according to the [spec](https://eth.wiki/json-rpc/API#eth_getstorageat): integer of the position in the storage, converting this to a U256
// will make sure the number is formatted correctly as [quantity](https://eips.ethereum.org/EIPS/eip-1474#quantity)
let position = U256 ::from_big_endian ( location . as_bytes ( ) ) ;
let position = utils ::serialize ( & position ) ;
2020-07-31 02:56:28 +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 ( ) ) ) ;
2021-01-22 09:25:22 +00:00
// get the hex encoded value.
2022-05-18 14:49:32 +00:00
let value : String = self . request ( " eth_getStorageAt " , [ from , position , block ] ) . await ? ;
2021-01-22 09:25:22 +00:00
// 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
2021-09-18 06:01:02 +00:00
/// Returns the EIP-1186 proof response
2022-03-19 17:05:39 +00:00
/// <https://github.com/ethereum/EIPs/issues/1186>
2021-09-18 06:01:02 +00:00
async fn get_proof < T : Into < NameOrAddress > + Send + Sync > (
& self ,
from : T ,
locations : Vec < H256 > ,
block : Option < BlockId > ,
) -> Result < EIP1186ProofResponse , 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 ) ;
2021-10-29 12:29:35 +00:00
let locations = locations . iter ( ) . map ( | location | utils ::serialize ( & location ) ) . collect ( ) ;
2021-09-18 06:01:02 +00:00
let block = utils ::serialize ( & block . unwrap_or_else ( | | BlockNumber ::Latest . into ( ) ) ) ;
self . request ( " eth_getProof " , [ from , locations , block ] ) . await
}
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.
//
2021-10-29 12:29:35 +00:00
// ENS also provides the ability for a reverse lookup, which determines the name for an address
// if it has been configured.
2020-05-27 11:32:44 +00:00
/// 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 > {
2021-10-29 12:29:35 +00:00
self . query_resolver ( ParamType ::Address , ens_name , ens ::ADDR_SELECTOR ) . await
2020-05-24 15:40:16 +00:00
}
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 ) ;
2022-03-08 14:45:45 +00:00
let domain : String =
self . query_resolver ( ParamType ::String , & ens_name , ens ::NAME_SELECTOR ) . await ? ;
let reverse_address = self . resolve_name ( & domain ) . await ? ;
if address ! = reverse_address {
Err ( ProviderError ::EnsNotOwned ( domain ) )
} else {
Ok ( domain )
}
2020-05-27 11:32:44 +00:00
}
2022-02-16 14:25:41 +00:00
/// Returns the avatar HTTP link of the avatar that the `ens_name` resolves to (or None
/// if not configured)
///
/// # Example
/// ```no_run
/// # use ethers_providers::{Provider, Http as HttpProvider, Middleware};
/// # use std::convert::TryFrom;
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// # let provider = Provider::<HttpProvider>::try_from("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27").unwrap();
/// let avatar = provider.resolve_avatar("parishilton.eth").await.unwrap();
/// assert_eq!(avatar.to_string(), "https://i.imgur.com/YW3Hzph.jpg");
/// # }
/// ```
///
/// # Panics
///
/// If the bytes returned from the ENS registrar/resolver cannot be interpreted as
/// a string. This should theoretically never happen.
async fn resolve_avatar ( & self , ens_name : & str ) -> Result < Url , ProviderError > {
let ( field , owner ) =
try_join! ( self . resolve_field ( ens_name , " avatar " ) , self . resolve_name ( ens_name ) ) ? ;
let url = Url ::from_str ( & field ) . map_err ( | e | ProviderError ::CustomError ( e . to_string ( ) ) ) ? ;
match url . scheme ( ) {
" https " | " data " = > Ok ( url ) ,
" ipfs " = > erc ::http_link_ipfs ( url ) . map_err ( ProviderError ::CustomError ) ,
" eip155 " = > {
let token =
erc ::ERCNFT ::from_str ( url . path ( ) ) . map_err ( ProviderError ::CustomError ) ? ;
match token . type_ {
erc ::ERCNFTType ::ERC721 = > {
let tx = TransactionRequest {
data : Some (
[ & erc ::ERC721_OWNER_SELECTOR [ .. ] , & token . id ] . concat ( ) . into ( ) ,
) ,
to : Some ( NameOrAddress ::Address ( token . contract ) ) ,
.. Default ::default ( )
} ;
let data = self . call ( & tx . into ( ) , None ) . await ? ;
if decode_bytes ::< Address > ( ParamType ::Address , data ) ! = owner {
return Err ( ProviderError ::CustomError ( " Incorrect owner. " . to_string ( ) ) )
}
}
erc ::ERCNFTType ::ERC1155 = > {
let tx = TransactionRequest {
data : Some (
[
& erc ::ERC1155_BALANCE_SELECTOR [ .. ] ,
& [ 0x0 ; 12 ] ,
& owner . 0 ,
& token . id ,
]
. concat ( )
. into ( ) ,
) ,
to : Some ( NameOrAddress ::Address ( token . contract ) ) ,
.. Default ::default ( )
} ;
let data = self . call ( & tx . into ( ) , None ) . await ? ;
if decode_bytes ::< u64 > ( ParamType ::Uint ( 64 ) , data ) = = 0 {
return Err ( ProviderError ::CustomError ( " Incorrect balance. " . to_string ( ) ) )
}
}
}
let image_url = self . resolve_nft ( token ) . await ? ;
match image_url . scheme ( ) {
" https " | " data " = > Ok ( image_url ) ,
" ipfs " = > erc ::http_link_ipfs ( image_url ) . map_err ( ProviderError ::CustomError ) ,
_ = > Err ( ProviderError ::CustomError (
" Unsupported scheme for the image " . to_string ( ) ,
) ) ,
}
}
_ = > Err ( ProviderError ::CustomError ( " Unsupported scheme " . to_string ( ) ) ) ,
}
}
/// Returns the URL (not necesserily HTTP) of the image behind a token.
///
/// # Example
/// ```no_run
/// # use ethers_providers::{Provider, Http as HttpProvider, Middleware};
/// # use std::{str::FromStr, convert::TryFrom};
/// # #[tokio::main(flavor = "current_thread")]
/// # async fn main() {
/// # let provider = Provider::<HttpProvider>::try_from("https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27").unwrap();
/// let token = ethers_providers::erc::ERCNFT::from_str("erc721:0xc92ceddfb8dd984a89fb494c376f9a48b999aafc/9018").unwrap();
/// let token_image = provider.resolve_nft(token).await.unwrap();
/// assert_eq!(token_image.to_string(), "https://creature.mypinata.cloud/ipfs/QmNwj3aUzXfG4twV3no7hJRYxLLAWNPk6RrfQaqJ6nVJFa/9018.jpg");
/// # }
/// ```
///
/// # Panics
///
/// If the bytes returned from the ENS registrar/resolver cannot be interpreted as
/// a string. This should theoretically never happen.
async fn resolve_nft ( & self , token : erc ::ERCNFT ) -> Result < Url , ProviderError > {
let selector = token . type_ . resolution_selector ( ) ;
let tx = TransactionRequest {
data : Some ( [ & selector [ .. ] , & token . id ] . concat ( ) . into ( ) ) ,
to : Some ( NameOrAddress ::Address ( token . contract ) ) ,
.. Default ::default ( )
} ;
let data = self . call ( & tx . into ( ) , None ) . await ? ;
let mut metadata_url = Url ::parse ( & decode_bytes ::< String > ( ParamType ::String , data ) )
. map_err ( | e | ProviderError ::CustomError ( format! ( " Invalid metadata url: {} " , e ) ) ) ? ;
if token . type_ = = erc ::ERCNFTType ::ERC1155 {
metadata_url
. set_path ( & metadata_url . path ( ) . replace ( " %7Bid%7D " , & hex ::encode ( & token . id ) ) ) ;
}
if metadata_url . scheme ( ) = = " ipfs " {
metadata_url = erc ::http_link_ipfs ( metadata_url ) . map_err ( ProviderError ::CustomError ) ? ;
}
let metadata : erc ::Metadata = reqwest ::get ( metadata_url ) . await ? . json ( ) . await ? ;
Url ::parse ( & metadata . image ) . map_err ( | e | ProviderError ::CustomError ( e . to_string ( ) ) )
}
/// Fetch a field for the `ens_name` (no 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 resolve_field ( & self , ens_name : & str , field : & str ) -> Result < String , ProviderError > {
let field : String = self
. query_resolver_parameters (
ParamType ::String ,
ens_name ,
ens ::FIELD_SELECTOR ,
Some ( & ens ::parameterhash ( field ) ) ,
)
. await ? ;
Ok ( field )
}
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
2021-08-09 00:31:11 +00:00
async fn trace_call < T : Into < TypedTransaction > + Send + Sync > (
2020-10-31 10:44:08 +00:00
& self ,
2021-08-09 00:31:11 +00:00
req : T ,
2020-10-31 10:44:08 +00:00
trace_type : Vec < TraceType > ,
block : Option < BlockNumber > ,
) -> Result < BlockTrace , ProviderError > {
2021-08-09 00:31:11 +00:00
let req = req . into ( ) ;
2020-10-31 10:44:08 +00:00
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
}
2022-01-15 10:41:43 +00:00
/// Executes given calls and returns a number of possible traces for each call
async fn trace_call_many < T : Into < TypedTransaction > + Send + Sync > (
& self ,
req : Vec < ( T , Vec < TraceType > ) > ,
block : Option < BlockNumber > ,
) -> Result < Vec < BlockTrace > , ProviderError > {
let req : Vec < ( TypedTransaction , Vec < TraceType > ) > =
req . into_iter ( ) . map ( | ( tx , trace_type ) | ( tx . into ( ) , trace_type ) ) . collect ( ) ;
let req = utils ::serialize ( & req ) ;
let block = utils ::serialize ( & block . unwrap_or ( BlockNumber ::Latest ) ) ;
self . request ( " trace_callMany " , [ req , 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 ) ;
2021-10-29 12:29:35 +00:00
self . request ( " trace_rawTransaction " , [ data , trace_type ] ) . await
2020-10-31 10:44:08 +00:00
}
/// 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 ) ;
2021-10-29 12:29:35 +00:00
self . request ( " trace_replayTransaction " , [ hash , trace_type ] ) . await
2020-10-31 10:44:08 +00:00
}
/// 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 ) ;
2021-10-29 12:29:35 +00:00
self . request ( " trace_replayBlockTransactions " , [ block , trace_type ] ) . await
2020-10-31 10:44:08 +00:00
}
/// 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
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 ,
{
2022-05-16 15:11:25 +00:00
let loaded_logs = match filter . block_option {
FilterBlockOption ::Range { from_block , to_block : _ } = > {
if from_block . is_none ( ) {
vec! [ ]
} else {
self . get_logs ( filter ) . await ?
}
}
FilterBlockOption ::AtBlockHash ( _block_hash ) = > self . get_logs ( filter ) . await ? ,
} ;
let loaded_logs = VecDeque ::from ( loaded_logs ) ;
2020-11-30 09:33:06 +00:00
let logs = utils ::serialize ( & " logs " ) ; // TODO: Make this a static
let filter = utils ::serialize ( filter ) ;
2022-05-16 15:11:25 +00:00
self . subscribe ( [ logs , filter ] ) . await . map ( | mut stream | {
stream . set_loaded_elements ( loaded_logs ) ;
stream
} )
2020-11-30 09:33:06 +00:00
}
2021-08-09 00:31:11 +00:00
2021-12-10 15:49:27 +00:00
async fn fee_history < T : Into < U256 > + Send + Sync > (
2021-08-09 00:31:11 +00:00
& self ,
2021-08-19 16:44:15 +00:00
block_count : T ,
2021-08-09 00:31:11 +00:00
last_block : BlockNumber ,
reward_percentiles : & [ f64 ] ,
) -> Result < FeeHistory , Self ::Error > {
2021-12-10 16:16:20 +00:00
let block_count = block_count . into ( ) ;
2021-08-09 00:31:11 +00:00
let last_block = utils ::serialize ( & last_block ) ;
let reward_percentiles = utils ::serialize ( & reward_percentiles ) ;
2021-08-19 16:44:15 +00:00
// 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.
2021-08-09 00:31:11 +00:00
self . request (
" eth_feeHistory " ,
2021-12-10 16:16:20 +00:00
[ utils ::serialize ( & block_count ) , last_block . clone ( ) , reward_percentiles . clone ( ) ] ,
2021-08-09 00:31:11 +00:00
)
. await
2021-08-19 16:44:15 +00:00
. or ( self
. request (
" eth_feeHistory " ,
2021-12-10 16:16:20 +00:00
[ utils ::serialize ( & block_count . as_u64 ( ) ) , last_block , reward_percentiles ] ,
2021-08-19 16:44:15 +00:00
)
. await )
2021-08-09 00:31:11 +00:00
}
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 ,
2022-02-16 14:25:41 +00:00
) -> Result < T , ProviderError > {
self . query_resolver_parameters ( param , ens_name , selector , None ) . await
}
async fn query_resolver_parameters < T : Detokenize > (
& self ,
param : ParamType ,
ens_name : & str ,
selector : Selector ,
parameters : Option < & [ u8 ] > ,
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
2021-08-08 21:10:40 +00:00
let ens_addr = self . ens . 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
2021-10-29 12:29:35 +00:00
let data = self . call ( & ens ::get_resolver ( ens_addr , ens_name ) . into ( ) , None ) . await ? ;
2020-05-27 11:32:44 +00:00
2022-02-24 16:42:32 +00:00
// otherwise, decode_bytes panics
if data . 0. is_empty ( ) {
return Err ( ProviderError ::EnsError ( ens_name . to_owned ( ) ) )
}
2020-05-27 11:32:44 +00:00
let resolver_address : Address = decode_bytes ( ParamType ::Address , data ) ;
if resolver_address = = Address ::zero ( ) {
2021-10-29 12:29:35 +00:00
return Err ( ProviderError ::EnsError ( ens_name . to_owned ( ) ) )
2020-05-27 11:32:44 +00:00
}
// resolve
2022-02-16 14:25:41 +00:00
let data = self
. call ( & ens ::resolve ( resolver_address , selector , ens_name , parameters ) . into ( ) , None )
. await ? ;
2020-05-27 11:32:44 +00:00
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) ]
2022-06-01 15:22:39 +00:00
/// Anvil and Ganache-only function for mining empty blocks
2020-06-15 08:46:07 +00:00
pub async fn mine ( & self , num_blocks : usize ) -> Result < ( ) , ProviderError > {
for _ in 0 .. num_blocks {
2021-10-29 12:29:35 +00:00
self . inner . request ::< _ , U256 > ( " evm_mine " , None ::< ( ) > ) . await . map_err ( Into ::into ) ? ;
2020-06-15 08:46:07 +00:00
}
Ok ( ( ) )
}
2020-06-10 08:58:27 +00:00
/// Sets the ENS Address (default: mainnet)
2021-12-19 04:28:38 +00:00
#[ must_use ]
2020-05-27 15:43:43 +00:00
pub fn ens < T : Into < Address > > ( mut self , ens : T ) -> Self {
2021-08-08 21:10:40 +00:00
self . ens = 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)
2021-12-19 04:28:38 +00:00
#[ must_use ]
2020-06-22 08:44:08 +00:00
pub fn interval < T : Into < Duration > > ( mut self , interval : T ) -> Self {
2021-08-08 21:10:40 +00:00
self . interval = Some ( interval . into ( ) ) ;
2020-06-22 08:44:08 +00:00
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 {
2021-08-08 21:10:40 +00:00
self . interval . unwrap_or ( DEFAULT_POLL_INTERVAL )
2020-06-22 08:44:08 +00:00
}
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
2021-08-23 09:56:44 +00:00
#[ cfg(not(target_arch = " wasm32 " )) ]
2020-11-30 09:33:06 +00:00
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-08-23 09:56:44 +00:00
2022-06-09 07:45:33 +00:00
#[ cfg(not(target_arch = " wasm32 " )) ]
pub async fn connect_with_auth (
url : impl tokio_tungstenite ::tungstenite ::client ::IntoClientRequest + Unpin ,
auth : Authorization ,
) -> Result < Self , ProviderError > {
let ws = crate ::Ws ::connect_with_auth ( url , auth ) . await ? ;
Ok ( Self ::new ( ws ) )
}
2021-08-23 09:56:44 +00:00
/// Direct connection to a websocket endpoint
#[ cfg(target_arch = " wasm32 " ) ]
pub async fn connect ( url : & str ) -> Result < Self , ProviderError > {
let ws = crate ::Ws ::connect ( url ) . await ? ;
Ok ( Self ::new ( ws ) )
}
2020-11-30 09:33:06 +00:00
}
2021-12-13 21:25:10 +00:00
#[ cfg(all(target_family = " unix " , feature = " ipc " )) ]
2021-04-08 06:52:31 +00:00
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 ) )
}
}
2022-04-13 20:21:52 +00:00
impl < Read , Write > Provider < RwClient < Read , Write > >
where
Read : JsonRpcClient + 'static ,
< Read as JsonRpcClient > ::Error : Sync + Send + 'static ,
Write : JsonRpcClient + 'static ,
< Write as JsonRpcClient > ::Error : Sync + Send + 'static ,
{
/// Creates a new [Provider] with a [RwClient]
pub fn rw ( r : Read , w : Write ) -> Self {
Self ::new ( RwClient ::new ( r , w ) )
}
}
2021-08-29 11:03:53 +00:00
impl < T : JsonRpcClientWrapper > Provider < QuorumProvider < T > > {
/// Provider that uses a quorum
pub fn quorum ( inner : QuorumProvider < T > ) -> Self {
Self ::new ( inner )
}
}
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>> {
2021-08-28 21:06:29 +00:00
/// use ethers_core::types::U64;
/// use ethers_providers::{Middleware, Provider};
2020-11-27 12:57:44 +00:00
/// // 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 {
2021-07-24 18:53:40 +00:00
let tokens = abi ::decode ( & [ param ] , bytes . as_ref ( ) )
2020-12-31 17:19:14 +00:00
. 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 > {
2021-08-08 21:10:40 +00:00
Ok ( Provider ::new ( HttpProvider ::new ( Url ::parse ( src ) ? ) ) )
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 ( ) )
}
}
2022-03-27 21:37:15 +00:00
impl < ' a > TryFrom < & ' a String > for Provider < HttpProvider > {
type Error = ParseError ;
fn try_from ( src : & ' a String ) -> Result < Self , Self ::Error > {
Provider ::try_from ( src . as_str ( ) )
}
}
2022-05-27 21:02:16 +00:00
#[ cfg(not(target_arch = " wasm32 " )) ]
impl Provider < RetryClient < HttpProvider > > {
pub fn new_client ( src : & str , max_retry : u32 , initial_backoff : u64 ) -> Result < Self , ParseError > {
Ok ( Provider ::new ( RetryClient ::new (
HttpProvider ::new ( Url ::parse ( src ) ? ) ,
Box ::new ( HttpRateLimitRetryPolicy ) ,
max_retry ,
initial_backoff ,
) ) )
}
}
2021-12-03 17:11:09 +00:00
/// A middleware supporting development-specific JSON RPC methods
///
/// # Example
///
///```
/// use ethers_providers::{Provider, Http, Middleware, DevRpcMiddleware};
/// use ethers_core::types::TransactionRequest;
2022-06-01 15:22:39 +00:00
/// use ethers_core::utils::Anvil;
2021-12-03 17:11:09 +00:00
/// use std::convert::TryFrom;
///
2022-02-02 17:20:01 +00:00
/// # #[tokio::main(flavor = "current_thread")]
2021-12-03 17:11:09 +00:00
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
2022-06-01 15:22:39 +00:00
/// let anvil = Anvil::new().spawn();
/// let provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap();
2021-12-03 17:11:09 +00:00
/// let client = DevRpcMiddleware::new(provider);
///
/// // snapshot the initial state
/// let block0 = client.get_block_number().await.unwrap();
/// let snap_id = client.snapshot().await.unwrap();
///
/// // send a transaction
/// let accounts = client.get_accounts().await?;
/// let from = accounts[0];
/// let to = accounts[1];
/// let balance_before = client.get_balance(to, None).await?;
/// let tx = TransactionRequest::new().to(to).value(1000).from(from);
/// client.send_transaction(tx, None).await?.await?;
/// let balance_after = client.get_balance(to, None).await?;
/// assert_eq!(balance_after, balance_before + 1000);
///
/// // revert to snapshot
/// client.revert_to_snapshot(snap_id).await.unwrap();
/// let balance_after_revert = client.get_balance(to, None).await?;
/// assert_eq!(balance_after_revert, balance_before);
/// # Ok(())
/// # }
/// ```
#[ cfg(feature = " dev-rpc " ) ]
pub mod dev_rpc {
use crate ::{ FromErr , Middleware , ProviderError } ;
use async_trait ::async_trait ;
use ethers_core ::types ::U256 ;
use thiserror ::Error ;
use std ::fmt ::Debug ;
#[ derive(Clone, Debug) ]
pub struct DevRpcMiddleware < M > ( M ) ;
#[ derive(Error, Debug) ]
pub enum DevRpcMiddlewareError < M : Middleware > {
#[ error( " {0} " ) ]
MiddlewareError ( M ::Error ) ,
#[ error( " {0} " ) ]
ProviderError ( ProviderError ) ,
#[ error( " Could not revert to snapshot " ) ]
NoSnapshot ,
}
#[ async_trait ]
impl < M : Middleware > Middleware for DevRpcMiddleware < M > {
type Error = DevRpcMiddlewareError < M > ;
type Provider = M ::Provider ;
type Inner = M ;
fn inner ( & self ) -> & M {
& self . 0
}
}
impl < M : Middleware > FromErr < M ::Error > for DevRpcMiddlewareError < M > {
fn from ( src : M ::Error ) -> DevRpcMiddlewareError < M > {
DevRpcMiddlewareError ::MiddlewareError ( src )
}
}
impl < M > From < ProviderError > for DevRpcMiddlewareError < M >
where
M : Middleware ,
{
fn from ( src : ProviderError ) -> Self {
Self ::ProviderError ( src )
}
}
impl < M : Middleware > DevRpcMiddleware < M > {
pub fn new ( inner : M ) -> Self {
Self ( inner )
}
2022-06-01 15:22:39 +00:00
// Ganache, Hardhat and Anvil increment snapshot ID even if no state has changed
2021-12-03 17:11:09 +00:00
pub async fn snapshot ( & self ) -> Result < U256 , DevRpcMiddlewareError < M > > {
self . provider ( ) . request ::< ( ) , U256 > ( " evm_snapshot " , ( ) ) . await . map_err ( From ::from )
}
pub async fn revert_to_snapshot ( & self , id : U256 ) -> Result < ( ) , DevRpcMiddlewareError < M > > {
let ok = self
. provider ( )
. request ::< [ U256 ; 1 ] , bool > ( " evm_revert " , [ id ] )
. await
. map_err ( DevRpcMiddlewareError ::ProviderError ) ? ;
if ok {
Ok ( ( ) )
} else {
Err ( DevRpcMiddlewareError ::NoSnapshot )
}
}
}
#[ cfg(test) ]
// Celo blocks can not get parsed when used with Ganache
#[ cfg(not(feature = " celo " )) ]
mod tests {
use super ::* ;
use crate ::{ Http , Provider } ;
2022-06-01 15:22:39 +00:00
use ethers_core ::utils ::Anvil ;
2021-12-03 17:11:09 +00:00
use std ::convert ::TryFrom ;
#[ tokio::test ]
async fn test_snapshot ( ) {
2022-06-01 15:22:39 +00:00
let anvil = Anvil ::new ( ) . spawn ( ) ;
let provider = Provider ::< Http > ::try_from ( anvil . endpoint ( ) ) . unwrap ( ) ;
2021-12-03 17:11:09 +00:00
let client = DevRpcMiddleware ::new ( provider ) ;
// snapshot initial state
let block0 = client . get_block_number ( ) . await . unwrap ( ) ;
let time0 = client . get_block ( block0 ) . await . unwrap ( ) . unwrap ( ) . timestamp ;
let snap_id0 = client . snapshot ( ) . await . unwrap ( ) ;
// mine a new block
client . provider ( ) . mine ( 1 ) . await . unwrap ( ) ;
// snapshot state
let block1 = client . get_block_number ( ) . await . unwrap ( ) ;
let time1 = client . get_block ( block1 ) . await . unwrap ( ) . unwrap ( ) . timestamp ;
let snap_id1 = client . snapshot ( ) . await . unwrap ( ) ;
// mine some blocks
client . provider ( ) . mine ( 5 ) . await . unwrap ( ) ;
// snapshot state
let block2 = client . get_block_number ( ) . await . unwrap ( ) ;
let time2 = client . get_block ( block2 ) . await . unwrap ( ) . unwrap ( ) . timestamp ;
let snap_id2 = client . snapshot ( ) . await . unwrap ( ) ;
// mine some blocks
client . provider ( ) . mine ( 5 ) . await . unwrap ( ) ;
// revert_to_snapshot should reset state to snap id
client . revert_to_snapshot ( snap_id2 ) . await . unwrap ( ) ;
let block = client . get_block_number ( ) . await . unwrap ( ) ;
let time = client . get_block ( block ) . await . unwrap ( ) . unwrap ( ) . timestamp ;
assert_eq! ( block , block2 ) ;
assert_eq! ( time , time2 ) ;
client . revert_to_snapshot ( snap_id1 ) . await . unwrap ( ) ;
let block = client . get_block_number ( ) . await . unwrap ( ) ;
let time = client . get_block ( block ) . await . unwrap ( ) . unwrap ( ) . timestamp ;
assert_eq! ( block , block1 ) ;
assert_eq! ( time , time1 ) ;
// revert_to_snapshot should throw given non-existent or
// previously used snapshot
let result = client . revert_to_snapshot ( snap_id1 ) . await ;
assert! ( result . is_err ( ) ) ;
client . revert_to_snapshot ( snap_id0 ) . await . unwrap ( ) ;
let block = client . get_block_number ( ) . await . unwrap ( ) ;
let time = client . get_block ( block ) . await . unwrap ( ) . unwrap ( ) . timestamp ;
assert_eq! ( block , block0 ) ;
assert_eq! ( time , time0 ) ;
}
}
}
2020-05-27 11:32:44 +00:00
#[ cfg(test) ]
2021-08-23 09:56:44 +00:00
#[ cfg(not(target_arch = " wasm32 " )) ]
mod tests {
2020-05-27 11:32:44 +00:00
use super ::* ;
2021-08-23 09:56:44 +00:00
use crate ::Http ;
2021-10-29 12:29:35 +00:00
use ethers_core ::{
2022-03-01 12:13:06 +00:00
types ::{
transaction ::eip2930 ::AccessList , Eip1559TransactionRequest , TransactionRequest , H256 ,
} ,
2022-06-01 15:22:39 +00:00
utils ::Anvil ,
2021-10-29 12:29:35 +00:00
} ;
2021-08-23 09:56:44 +00:00
use futures_util ::StreamExt ;
2020-05-27 11:32:44 +00:00
2022-05-18 14:49:32 +00:00
#[ test ]
fn convert_h256_u256_quantity ( ) {
let hash : H256 = H256 ::zero ( ) ;
let quantity = U256 ::from_big_endian ( hash . as_bytes ( ) ) ;
assert_eq! ( format! ( " {quantity:#x} " ) , " 0x0 " ) ;
assert_eq! ( utils ::serialize ( & quantity ) . to_string ( ) , " \" 0x0 \" " ) ;
let address : Address = " 0x295a70b2de5e3953354a6a8344e616ed314d7251 " . parse ( ) . unwrap ( ) ;
let block = BlockNumber ::Latest ;
let params =
[ utils ::serialize ( & address ) , utils ::serialize ( & quantity ) , utils ::serialize ( & block ) ] ;
let params = serde_json ::to_string ( & params ) . unwrap ( ) ;
assert_eq! ( params , r # "["0x295a70b2de5e3953354a6a8344e616ed314d7251","0x0","latest"]"# ) ;
}
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 ( ) {
2022-03-13 16:04:09 +00:00
let provider = crate ::test_provider ::MAINNET . provider ( ) ;
2020-05-27 11:32:44 +00:00
2021-10-29 12:29:35 +00:00
let addr = provider . resolve_name ( " registrar.firefly.eth " ) . await . unwrap ( ) ;
assert_eq! ( addr , " 6fC21092DA55B392b045eD78F4732bff3C580e2c " . parse ( ) . unwrap ( ) ) ;
2020-05-27 11:32:44 +00:00
// 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
2021-10-29 12:29:35 +00:00
provider . resolve_name ( " asdfasdf.registrar.firefly.eth " ) . await . 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 ( ) {
2022-03-13 16:04:09 +00:00
let provider = crate ::MAINNET . provider ( ) ;
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
2022-02-16 14:25:41 +00:00
#[ tokio::test ]
2022-06-08 12:13:03 +00:00
#[ ignore ]
2022-02-16 14:25:41 +00:00
async fn mainnet_resolve_avatar ( ) {
2022-03-13 16:04:09 +00:00
let provider = crate ::MAINNET . provider ( ) ;
2022-02-16 14:25:41 +00:00
for ( ens_name , res ) in & [
// HTTPS
( " alisha.eth " , " https://ipfs.io/ipfs/QmeQm91kAdPGnUKsE74WvkqYKUeHvc2oHd2FW11V3TrqkQ " ) ,
// ERC-1155
2022-06-07 15:45:13 +00:00
( " nick.eth " , " https://img.seadn.io/files/3ae7be6c41ad4767bf3ecbc0493b4bfb.png " ) ,
2022-02-16 14:25:41 +00:00
// HTTPS
( " parishilton.eth " , " https://i.imgur.com/YW3Hzph.jpg " ) ,
// ERC-721 with IPFS link
( " ikehaya-nft.eth " , " https://ipfs.io/ipfs/QmdKkwCE8uVhgYd7tWBfhtHdQZDnbNukWJ8bvQmR6nZKsk " ) ,
// ERC-1155 with IPFS link
( " vitalik.eth " , " https://ipfs.io/ipfs/QmSP4nq9fnN9dAiCj42ug9Wa79rqmQerZXZch82VqpiH7U/image.gif " ) ,
// IPFS
( " cdixon.eth " , " https://ipfs.io/ipfs/QmYA6ZpEARgHvRHZQdFPynMMX8NtdL2JCadvyuyG2oA88u " ) ,
( " 0age.eth " , " data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOmJsYWNrIiB2aWV3Qm94PSIwIDAgNTAwIDUwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB4PSIxNTUiIHk9IjYwIiB3aWR0aD0iMTkwIiBoZWlnaHQ9IjM5MCIgZmlsbD0iIzY5ZmYzNyIvPjwvc3ZnPg== " )
] {
println! ( " Resolving: {} " , ens_name ) ;
assert_eq! ( provider . resolve_avatar ( ens_name ) . await . unwrap ( ) , Url ::parse ( res ) . unwrap ( ) ) ;
}
}
2020-06-15 08:46:07 +00:00
#[ 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 ;
2022-06-01 15:22:39 +00:00
let geth = Anvil ::new ( ) . block_time ( 2 u64 ) . spawn ( ) ;
2021-06-05 17:09:12 +00:00
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 ( ) {
2021-10-29 12:29:35 +00:00
let block = provider . get_block ( start_block + i as u64 + 1 ) . await . unwrap ( ) . unwrap ( ) ;
2020-06-15 08:46:07 +00:00
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 ( ) {
2022-06-01 15:22:39 +00:00
use ethers_core ::utils ::Anvil ;
2021-01-28 06:51:53 +00:00
use std ::str ::FromStr ;
2022-06-01 15:22:39 +00:00
let anvil = Anvil ::new ( ) . spawn ( ) ;
let provider =
Provider ::< Http > ::try_from ( anvil . endpoint ( ) ) . unwrap ( ) . with_sender ( anvil . addresses ( ) [ 0 ] ) ;
2021-07-05 11:03:38 +00:00
assert! ( provider . is_signer ( ) . await ) ;
2021-01-28 06:51:53 +00:00
2022-06-01 15:22:39 +00:00
let provider = Provider ::< Http > ::try_from ( anvil . 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 ;
2022-06-01 15:22:39 +00:00
let geth = Anvil ::new ( ) . block_time ( 2 u64 ) . spawn ( ) ;
2021-06-05 17:09:12 +00:00
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 ( ) ;
2021-10-29 12:29:35 +00:00
let stream = provider . watch_pending_transactions ( ) . await . unwrap ( ) . stream ( ) ;
2020-06-15 08:46:07 +00:00
let mut tx_hashes = Vec ::new ( ) ;
2021-10-29 12:29:35 +00:00
let tx = TransactionRequest ::new ( ) . from ( accounts [ 0 ] ) . to ( accounts [ 0 ] ) . value ( 1e18 as u64 ) ;
2020-06-15 08:46:07 +00:00
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 ,
2022-06-01 15:22:39 +00:00
utils ::{ parse_ether , Anvil } ,
2020-09-17 11:06:56 +00:00
} ;
2022-06-01 15:22:39 +00:00
let anvil = Anvil ::new ( ) . block_time ( 2 u64 ) . spawn ( ) ;
let provider = Provider ::< Http > ::try_from ( anvil . endpoint ( ) ) . unwrap ( ) ;
2020-09-17 11:06:56 +00:00
let accounts = provider . get_accounts ( ) . await . unwrap ( ) ;
let tx = TransactionRequest ::pay ( accounts [ 0 ] , parse_ether ( 1 u64 ) . 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
2021-10-29 12:29:35 +00:00
assert! ( provider . get_transaction_receipt ( * pending_tx ) . await . unwrap ( ) . is_none ( ) ) ;
2020-09-17 11:06:56 +00:00
2020-12-17 11:26:01 +00:00
let hash = * pending_tx ;
2021-07-06 08:06:18 +00:00
let receipt = pending_tx . await . unwrap ( ) . unwrap ( ) ;
2020-12-17 11:26:01 +00:00
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 ( ) ;
2021-11-21 16:14:35 +00:00
let receipts = provider . parity_block_receipts ( 10657200 ) . await . unwrap ( ) ;
2020-11-19 17:30:10 +00:00
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 ( ) {
2022-06-01 15:22:39 +00:00
use ethers_core ::utils ::Anvil ;
2020-11-30 09:33:06 +00:00
use futures_util ::StreamExt ;
2022-06-01 15:22:39 +00:00
let anvil = Anvil ::new ( ) . block_time ( 2 u64 ) . spawn ( ) ;
let provider = Provider ::connect ( anvil . ws_endpoint ( ) ) . await . unwrap ( ) ;
2020-11-30 09:33:06 +00:00
let stream = provider . subscribe_blocks ( ) . await . unwrap ( ) ;
2021-10-29 12:29:35 +00:00
let blocks = stream . take ( 3 ) . map ( | x | x . number . unwrap ( ) . as_u64 ( ) ) . collect ::< Vec < _ > > ( ) . await ;
2020-11-30 09:33:06 +00:00
assert_eq! ( blocks , vec! [ 1 , 2 , 3 ] ) ;
}
2021-08-09 00:31:11 +00:00
#[ tokio::test ]
#[ cfg_attr(feature = " celo " , ignore) ]
async fn fee_history ( ) {
let provider = Provider ::< Http > ::try_from (
" https://goerli.infura.io/v3/fd8b88b56aa84f6da87b60f5441d6778 " ,
)
. unwrap ( ) ;
2021-10-29 12:29:35 +00:00
let history =
provider . fee_history ( 10 u64 , BlockNumber ::Latest , & [ 10.0 , 40.0 ] ) . await . unwrap ( ) ;
2021-08-09 00:31:11 +00:00
dbg! ( & history ) ;
}
2022-01-15 10:41:43 +00:00
#[ tokio::test ]
#[ ignore ]
2022-01-15 15:53:46 +00:00
#[ cfg(feature = " ws " ) ]
2022-01-15 10:41:43 +00:00
async fn test_trace_call_many ( ) {
2022-01-15 15:53:46 +00:00
use ethers_core ::types ::H160 ;
2022-01-15 10:41:43 +00:00
// TODO: Implement ErigonInstance, so it'd be possible to test this.
2022-01-15 15:53:46 +00:00
let provider = Provider ::new ( crate ::Ws ::connect ( " ws://127.0.0.1:8545 " ) . await . unwrap ( ) ) ;
2022-01-15 10:41:43 +00:00
let traces = provider
. trace_call_many (
vec! [
(
TransactionRequest ::new ( )
. from ( Address ::zero ( ) )
. to ( " 0x0000000000000000000000000000000000000001 "
. parse ::< H160 > ( )
. unwrap ( ) )
. value ( U256 ::from ( 10000000000000000 u128 ) ) ,
vec! [ TraceType ::StateDiff ] ,
) ,
(
TransactionRequest ::new ( )
. from (
" 0x0000000000000000000000000000000000000001 "
. parse ::< H160 > ( )
. unwrap ( ) ,
)
. to ( " 0x0000000000000000000000000000000000000002 "
. parse ::< H160 > ( )
. unwrap ( ) )
. value ( U256 ::from ( 10000000000000000 u128 ) ) ,
vec! [ TraceType ::StateDiff ] ,
) ,
] ,
None ,
)
. await
. unwrap ( ) ;
dbg! ( traces ) ;
}
2022-03-01 12:13:06 +00:00
#[ tokio::test ]
async fn test_fill_transaction_1559 ( ) {
let ( mut provider , mock ) = Provider ::mocked ( ) ;
provider . from = Some ( " 0x6fC21092DA55B392b045eD78F4732bff3C580e2c " . parse ( ) . unwrap ( ) ) ;
let gas = U256 ::from ( 21000_ usize ) ;
2022-03-19 16:41:03 +00:00
let max_fee = U256 ::from ( 25_ usize ) ;
2022-03-01 12:13:06 +00:00
let prio_fee = U256 ::from ( 25_ usize ) ;
let access_list : AccessList = vec! [ Default ::default ( ) ] . into ( ) ;
// --- leaves a filled 1559 transaction unchanged, making no requests
let from : Address = " 0x0000000000000000000000000000000000000001 " . parse ( ) . unwrap ( ) ;
let to : Address = " 0x0000000000000000000000000000000000000002 " . parse ( ) . unwrap ( ) ;
let mut tx = Eip1559TransactionRequest ::new ( )
. from ( from )
. to ( to )
. gas ( gas )
2022-03-19 16:41:03 +00:00
. max_fee_per_gas ( max_fee )
2022-03-01 12:13:06 +00:00
. max_priority_fee_per_gas ( prio_fee )
. access_list ( access_list . clone ( ) )
. into ( ) ;
provider . fill_transaction ( & mut tx , None ) . await . unwrap ( ) ;
assert_eq! ( tx . from ( ) , Some ( & from ) ) ;
assert_eq! ( tx . to ( ) , Some ( & to . into ( ) ) ) ;
assert_eq! ( tx . gas ( ) , Some ( & gas ) ) ;
2022-03-19 16:41:03 +00:00
assert_eq! ( tx . gas_price ( ) , Some ( max_fee ) ) ;
2022-03-01 12:13:06 +00:00
assert_eq! ( tx . access_list ( ) , Some ( & access_list ) ) ;
// --- fills a 1559 transaction, leaving the existing gas limit unchanged, but including
// access list if cheaper
let gas_with_al = gas - 1 ;
let mut tx = Eip1559TransactionRequest ::new ( )
. gas ( gas )
2022-03-19 16:41:03 +00:00
. max_fee_per_gas ( max_fee )
2022-03-01 12:13:06 +00:00
. max_priority_fee_per_gas ( prio_fee )
. into ( ) ;
mock . push ( AccessListWithGasUsed {
access_list : access_list . clone ( ) ,
gas_used : gas_with_al ,
} )
. unwrap ( ) ;
provider . fill_transaction ( & mut tx , None ) . await . unwrap ( ) ;
assert_eq! ( tx . from ( ) , provider . from . as_ref ( ) ) ;
assert! ( tx . to ( ) . is_none ( ) ) ;
assert_eq! ( tx . gas ( ) , Some ( & gas ) ) ;
assert_eq! ( tx . access_list ( ) , Some ( & access_list ) ) ;
// --- fills a 1559 transaction, ignoring access list if more expensive
let gas_with_al = gas + 1 ;
let mut tx = Eip1559TransactionRequest ::new ( )
2022-03-19 16:41:03 +00:00
. max_fee_per_gas ( max_fee )
2022-03-01 12:13:06 +00:00
. max_priority_fee_per_gas ( prio_fee )
. into ( ) ;
mock . push ( AccessListWithGasUsed {
access_list : access_list . clone ( ) ,
gas_used : gas_with_al ,
} )
. unwrap ( ) ;
mock . push ( gas ) . unwrap ( ) ;
provider . fill_transaction ( & mut tx , None ) . await . unwrap ( ) ;
assert_eq! ( tx . from ( ) , provider . from . as_ref ( ) ) ;
assert! ( tx . to ( ) . is_none ( ) ) ;
assert_eq! ( tx . gas ( ) , Some ( & gas ) ) ;
assert_eq! ( tx . access_list ( ) , Some ( & Default ::default ( ) ) ) ;
// --- fills a 1559 transaction, using estimated gas if create_access_list() errors
let mut tx = Eip1559TransactionRequest ::new ( )
2022-03-19 16:41:03 +00:00
. max_fee_per_gas ( max_fee )
2022-03-01 12:13:06 +00:00
. max_priority_fee_per_gas ( prio_fee )
. into ( ) ;
// bad mock value causes error response for eth_createAccessList
mock . push ( b 'b' ) . unwrap ( ) ;
mock . push ( gas ) . unwrap ( ) ;
provider . fill_transaction ( & mut tx , None ) . await . unwrap ( ) ;
assert_eq! ( tx . from ( ) , provider . from . as_ref ( ) ) ;
assert! ( tx . to ( ) . is_none ( ) ) ;
assert_eq! ( tx . gas ( ) , Some ( & gas ) ) ;
assert_eq! ( tx . access_list ( ) , Some ( & Default ::default ( ) ) ) ;
// --- propogates estimate_gas() error
let mut tx = Eip1559TransactionRequest ::new ( )
2022-03-19 16:41:03 +00:00
. max_fee_per_gas ( max_fee )
2022-03-01 12:13:06 +00:00
. max_priority_fee_per_gas ( prio_fee )
. into ( ) ;
// bad mock value causes error response for eth_estimateGas
mock . push ( b 'b' ) . unwrap ( ) ;
let res = provider . fill_transaction ( & mut tx , None ) . await ;
assert! ( matches! ( res , Err ( ProviderError ::JsonRpcClientError ( _ ) ) ) ) ;
}
#[ tokio::test ]
async fn test_fill_transaction_legacy ( ) {
let ( mut provider , mock ) = Provider ::mocked ( ) ;
provider . from = Some ( " 0x6fC21092DA55B392b045eD78F4732bff3C580e2c " . parse ( ) . unwrap ( ) ) ;
let gas = U256 ::from ( 21000_ usize ) ;
let gas_price = U256 ::from ( 50_ usize ) ;
// --- leaves a filled legacy transaction unchanged, making no requests
let from : Address = " 0x0000000000000000000000000000000000000001 " . parse ( ) . unwrap ( ) ;
let to : Address = " 0x0000000000000000000000000000000000000002 " . parse ( ) . unwrap ( ) ;
let mut tx =
TransactionRequest ::new ( ) . from ( from ) . to ( to ) . gas ( gas ) . gas_price ( gas_price ) . into ( ) ;
provider . fill_transaction ( & mut tx , None ) . await . unwrap ( ) ;
assert_eq! ( tx . from ( ) , Some ( & from ) ) ;
assert_eq! ( tx . to ( ) , Some ( & to . into ( ) ) ) ;
assert_eq! ( tx . gas ( ) , Some ( & gas ) ) ;
assert_eq! ( tx . gas_price ( ) , Some ( gas_price ) ) ;
assert! ( tx . access_list ( ) . is_none ( ) ) ;
// --- fills an empty legacy transaction
let mut tx = TransactionRequest ::new ( ) . into ( ) ;
mock . push ( gas ) . unwrap ( ) ;
mock . push ( gas_price ) . unwrap ( ) ;
provider . fill_transaction ( & mut tx , None ) . await . unwrap ( ) ;
assert_eq! ( tx . from ( ) , provider . from . as_ref ( ) ) ;
assert! ( tx . to ( ) . is_none ( ) ) ;
assert_eq! ( tx . gas ( ) , Some ( & gas ) ) ;
assert_eq! ( tx . gas_price ( ) , Some ( gas_price ) ) ;
assert! ( tx . access_list ( ) . is_none ( ) ) ;
}
2020-06-15 08:46:07 +00:00
}