use crate::{ens, http::Provider as HttpProvider, networks::Network, JsonRpcClient};
use ethers_abi::{Detokenize, ParamType};
use ethers_types::{
Address, Block, BlockId, BlockNumber, Bytes, Filter, Log, Selector, Transaction,
TransactionReceipt, TransactionRequest, TxHash, U256,
};
use ethers_utils as utils;
use serde::Deserialize;
use url::{ParseError, Url};
use std::{convert::TryFrom, fmt::Debug, marker::PhantomData};
/// An abstract provider for interacting with the [Ethereum JSON RPC
/// API](https://github.com/ethereum/wiki/wiki/JSON-RPC)
#[derive(Clone, Debug)]
pub struct Provider
{
////// Blockchain Status
//
// Functions for querying the state of the blockchain
/// Gets the latest block number via the `eth_BlockNumber` API
pub async fn get_block_number(&self) -> Result {
self.0.request("eth_blockNumber", None::<()>).await
}
/// Gets the block at `block_hash_or_number` (transaction hashes only)
pub async fn get_block(
&self,
block_hash_or_number: impl Into,
) -> Result, P::Error> {
self.get_block_gen(block_hash_or_number.into(), false).await
}
/// Gets the block at `block_hash_or_number` (full transactions included)
pub async fn get_block_with_txs(
&self,
block_hash_or_number: impl Into,
) -> Result, P::Error> {
self.get_block_gen(block_hash_or_number.into(), true).await
}
async fn get_block_gen Deserialize<'a>>(
&self,
id: BlockId,
include_txs: bool,
) -> Result, P::Error> {
let include_txs = utils::serialize(&include_txs);
match id {
BlockId::Hash(hash) => {
let hash = utils::serialize(&hash);
let args = vec![hash, include_txs];
self.0.request("eth_getBlockByHash", Some(args)).await
}
BlockId::Number(num) => {
let num = utils::serialize(&num);
let args = vec![num, include_txs];
self.0.request("eth_getBlockByNumber", Some(args)).await
}
}
}
/// Gets the transaction with `transaction_hash`
pub async fn get_transaction>(
&self,
transaction_hash: T,
) -> Result {
let hash = transaction_hash.into();
self.0.request("eth_getTransactionByHash", Some(hash)).await
}
/// Gets the transaction receipt with `transaction_hash`
pub async fn get_transaction_receipt>(
&self,
transaction_hash: T,
) -> Result {
let hash = transaction_hash.into();
self.0
.request("eth_getTransactionReceipt", Some(hash))
.await
}
/// Gets the current gas price as estimated by the node
pub async fn get_gas_price(&self) -> Result {
self.0.request("eth_gasPrice", None::<()>).await
}
/// Gets the accounts on the node
pub async fn get_accounts(&self) -> Result, P::Error> {
self.0.request("eth_accounts", None::<()>).await
}
/// Returns the nonce of the address
pub async fn get_transaction_count(
&self,
from: Address,
block: Option,
) -> Result {
let from = utils::serialize(&from);
let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest));
self.0
.request("eth_getTransactionCount", Some(&[from, block]))
.await
}
/// Returns the account's balance
pub async fn get_balance(
&self,
from: Address,
block: Option,
) -> Result {
let from = utils::serialize(&from);
let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest));
self.0.request("eth_getBalance", Some(&[from, block])).await
}
////// Contract Execution
//
// These are relatively low-level calls. The Contracts API should usually be used instead.
/// Send 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.
pub async fn call(
&self,
tx: TransactionRequest,
block: Option,
) -> Result {
let tx = utils::serialize(&tx);
let block = utils::serialize(&block.unwrap_or(BlockNumber::Latest));
self.0.request("eth_call", Some(vec![tx, block])).await
}
/// Send 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).
pub async fn estimate_gas(
&self,
tx: &TransactionRequest,
block: Option,
) -> Result {
let tx = utils::serialize(tx);
let args = match block {
Some(block) => vec![tx, utils::serialize(&block)],
None => vec![tx],
};
self.0.request("eth_estimateGas", Some(args)).await
}
/// Send the transaction to the entire Ethereum network and returns the transaction's hash
/// This will consume gas from the account that signed the transaction.
pub async fn send_transaction(&self, tx: TransactionRequest) -> Result {
self.0.request("eth_sendTransaction", Some(tx)).await
}
/// 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.
pub async fn send_raw_transaction(&self, tx: &Transaction) -> Result {
let rlp = utils::serialize(&tx.rlp());
self.0.request("eth_sendRawTransaction", Some(rlp)).await
}
////// Contract state
/// Returns an array (possibly empty) of logs that match the filter
pub async fn get_logs(&self, filter: &Filter) -> Result, P::Error> {
self.0.request("eth_getLogs", Some(filter)).await
}
// TODO: get_code, get_storage_at
////// 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.
pub async fn resolve_name(&self, ens_name: &str) -> Result