diff --git a/crates/ethers-providers/Cargo.toml b/crates/ethers-providers/Cargo.toml index fd090ccb..232c6910 100644 --- a/crates/ethers-providers/Cargo.toml +++ b/crates/ethers-providers/Cargo.toml @@ -17,4 +17,4 @@ url = { version = "2.1.1", default-features = false } [dev-dependencies] rustc-hex = "2.1.0" -tokio = { version = "0.2.21", features = ["macros"] } +tokio = { version = "0.2.21", default-features = false, features = ["rt-core", "macros"] } diff --git a/crates/ethers-providers/src/ens.rs b/crates/ethers-providers/src/ens.rs index 40e712ce..856dbd22 100644 --- a/crates/ethers-providers/src/ens.rs +++ b/crates/ethers-providers/src/ens.rs @@ -1,3 +1,4 @@ +/// [Ethereum Name Service](https://docs.ens.domains/) support // Adapted from https://github.com/hhatto/rust-ens/blob/master/src/lib.rs use ethers_types::{Address, NameOrAddress, Selector, TransactionRequest, H256}; use ethers_utils::keccak256; @@ -6,13 +7,13 @@ use ethers_utils::keccak256; const ENS_REVERSE_REGISTRAR_DOMAIN: &str = "addr.reverse"; -// resolver(bytes32) +/// resolver(bytes32) const RESOLVER: Selector = [1, 120, 184, 191]; -// addr(bytes32) +/// addr(bytes32) pub const ADDR_SELECTOR: Selector = [59, 59, 87, 222]; -// name(bytes32) +/// name(bytes32) pub const NAME_SELECTOR: Selector = [105, 31, 52, 49]; /// Returns a transaction request for calling the `resolver` method on the ENS server @@ -26,6 +27,7 @@ pub fn get_resolver>(ens_address: T, name: &str) -> Transaction } } +/// Returns a transaction request for calling pub fn resolve>( resolver_address: T, selector: Selector, diff --git a/crates/ethers-providers/src/lib.rs b/crates/ethers-providers/src/lib.rs index 16a03700..203366a6 100644 --- a/crates/ethers-providers/src/lib.rs +++ b/crates/ethers-providers/src/lib.rs @@ -1,16 +1,59 @@ -//! Ethereum compatible providers -//! Currently supported: -//! - Raw HTTP POST requests +//! # Clients for interacting with Ethereum nodes //! -//! TODO: WebSockets, multiple backends, popular APIs etc. +//! This crate provides asynchronous [Ethereum JSON-RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) +//! compliant clients. The client is network-specific in order to provide ENS support and EIP-155 +//! replay protection. If you are testing and do not want to use EIP-155, you may use the `Any` +//! network type and override the provider's ENS address with the `ens` method. +//! +//! ```rust +//! use ethers_providers::{HttpProvider, networks::Any}; +//! use std::convert::TryFrom; +//! use tokio::runtime::Runtime; +//! +//! let provider = HttpProvider::::try_from( +//! "https://mainnet.infura.io/v3/9408f47dedf04716a03ef994182cf150" +//! ).unwrap(); +//! +//! // Since this is an async function, we need to run it from an async runtime, +//! // such as `tokio` +//! let mut runtime = Runtime::new().expect("Failed to create Tokio runtime"); +//! let block = runtime.block_on(provider.get_block(100u64)).unwrap(); +//! println!("Got block: {}", serde_json::to_string(&block).unwrap()); +//! ``` +//! +//! # Ethereum Name Service +//! +//! The provider may also be used to resolve [Ethereum Name Service](https://ens.domains) (ENS) names +//! to addresses (and vice versa). The address of the deployed ENS contract per network is specified in +//! the `networks` module. If you want to use mainnet ENS, you should instantiate your provider as +//! follows: +//! +//! ```rust +//! # use ethers_providers::{HttpProvider, networks::Mainnet}; +//! # use std::convert::TryFrom; +//! # use tokio::runtime::Runtime; +//! # let provider = HttpProvider::::try_from( +//! # "https://mainnet.infura.io/v3/9408f47dedf04716a03ef994182cf150" +//! # ).unwrap(); +//! # let mut runtime = Runtime::new().expect("Failed to create Tokio runtime"); +//! // Resolve ENS name to Address +//! let name = "vitalik.eth"; +//! let address = runtime.block_on(provider.resolve_name(name)).unwrap(); +//! let address = address.unwrap(); +//! +//! // Lookup ENS name given Address +//! let resolved_name = runtime.block_on(provider.lookup_address(address)).unwrap(); +//! let resolved_name = resolved_name.unwrap(); +//! assert_eq!(name, resolved_name); +//! ``` mod http; mod provider; pub mod networks; -/// ENS support -pub mod ens; +// ENS support +mod ens; use async_trait::async_trait; use serde::{Deserialize, Serialize}; @@ -22,11 +65,13 @@ pub use provider::Provider; pub type HttpProvider = Provider; #[async_trait] -/// Implement this trait in order to plug in different backends +/// Trait which must be implemented by data transports to be used with the Ethereum +/// JSON-RPC provider. pub trait JsonRpcClient: Debug { + /// A JSON-RPC Error type Error: Error; - /// Sends a request with the provided method and the params serialized as JSON + /// Sends a request with the provided JSON-RPC and parameters serialized as JSON async fn request Deserialize<'a>>( &self, method: &str, diff --git a/crates/ethers-providers/src/networks.rs b/crates/ethers-providers/src/networks.rs index 60f3f892..d95b1e62 100644 --- a/crates/ethers-providers/src/networks.rs +++ b/crates/ethers-providers/src/networks.rs @@ -1,16 +1,21 @@ -//! Networks are used inside wallets to ensure type-safety across networks. That way -//! a transaction that is designed to work with testnet does not accidentally work -//! with mainnet because the URL was changed. - +//! Networks are used inside wallets and providers to ensure replay protection across networks, +//! as well as to allow functions to be called with ENS names instead of Addresses. use ethers_types::{Address, H160, U64}; +/// Trait for specifying network specific metadata, such as the Chain Id or the ENS +/// address. pub trait Network { + /// The network's Chain Id. If None, then EIP-155 is not used and as a result + /// transactions **will not have replay protection** const CHAIN_ID: Option; + + /// The network's ENS address. const ENS_ADDRESS: Option
; // TODO: Default providers? e.g. `mainnet.infura.io/XXX`? } +/// Ethereum Mainnet, pre-specified ENS address and ChainID = 1 (for EIP-155) #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Mainnet; @@ -24,7 +29,9 @@ impl Network for Mainnet { ])); } -/// No EIP155 +/// Any other network, ChainID is not specified so **there is no replay protection when +/// using this network type**. ENS is also not specified, so any calls to the provider's +/// `lookup_address` and `resolve_name` _will fail_. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Any; diff --git a/crates/ethers-providers/src/provider.rs b/crates/ethers-providers/src/provider.rs index b12ffaf0..8eb52433 100644 --- a/crates/ethers-providers/src/provider.rs +++ b/crates/ethers-providers/src/provider.rs @@ -13,7 +13,9 @@ 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) +/// API](https://github.com/ethereum/wiki/wiki/JSON-RPC). Must be instantiated +/// with a [`Network`](networks/trait.Network.html) and a data transport +/// (e.g. HTTP, Websockets etc.) #[derive(Clone, Debug)] pub struct Provider(P, PhantomData, Option
); @@ -204,6 +206,10 @@ impl Provider { } /// Returns the ENS name the `address` resolves to (or None if not configured). + /// # Panics + /// + /// If the bytes returned from the ENS registrar/resolver cannot be interpreted as + /// a string. This should theoretically never happen. pub async fn lookup_address(&self, address: Address) -> Result, P::Error> { let ens_name = ens::reverse_address(address); self.query_resolver(ParamType::String, &ens_name, ens::NAME_SELECTOR) @@ -244,6 +250,7 @@ impl Provider { Ok(Some(decode_bytes(param, data))) } + /// Overrides the default ENS address set by the provider's `Network` type. pub fn ens>(mut self, ens: T) -> Self { self.2 = Some(ens.into()); self diff --git a/crates/ethers-types/src/chainstate/block.rs b/crates/ethers-types/src/chainstate/block.rs index 54c9d922..d00bfe80 100644 --- a/crates/ethers-types/src/chainstate/block.rs +++ b/crates/ethers-types/src/chainstate/block.rs @@ -74,6 +74,12 @@ pub enum BlockId { Number(BlockNumber), } +impl From for BlockId { + fn from(num: u64) -> Self { + BlockNumber::Number(num.into()).into() + } +} + impl From for BlockId { fn from(num: U64) -> Self { BlockNumber::Number(num).into()