docs: add more docs (#130)

This commit is contained in:
Georgios Konstantopoulos 2020-12-31 21:08:12 +02:00 committed by GitHub
parent 5c1f8f532a
commit 5f292670fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 140 additions and 49 deletions

View File

@ -88,3 +88,12 @@ This library would not have been possibly without the great work done in:
A lot of the code was inspired and adapted from them, to a unified and opinionated interface, A lot of the code was inspired and adapted from them, to a unified and opinionated interface,
built with async/await and std futures from the ground up. built with async/await and std futures from the ground up.
## Projects using ethers-rs
- [Yield Liquidator](https://github.com/yieldprotocol/yield-liquidator/): Liquidator for Yield Protocol
- [MEV Inspect](https://github.com/flashbots/mev-inspect-rs/): Miner Extractable Value inspector
- [Ethers Fireblocks](https://github.com/gakonst/ethers-fireblocks): Ethers middleware and signer for [Fireblocks](https://fireblocks.io)' API
- [Celo Threshold BLS DKG](https://github.com/celo-org/celo-threshold-bls-rs/): CLI for using Celo as a data availability network for the Joint-Feldman BLS DKG
- [Celo Plumo Prover](https://github.com/celo-org/plumo-prover): Creates Celo's ultralight client proof from on-chain data
- [Celo SNARK Setup Coordinator](https://github.com/celo-org/snark-setup-operator): Coordinator for executing a pipelined Groth16 SNARK setup

View File

@ -53,7 +53,7 @@ impl BaseContract {
/// versions, consider using `encode_with_selector` /// versions, consider using `encode_with_selector`
pub fn encode<T: Tokenize>(&self, name: &str, args: T) -> Result<Bytes, AbiError> { pub fn encode<T: Tokenize>(&self, name: &str, args: T) -> Result<Bytes, AbiError> {
let function = self.abi.function(name)?; let function = self.abi.function(name)?;
encode_fn(function, args) encode_function_data(function, args)
} }
/// Returns the ABI encoded data for the provided function selector and arguments /// Returns the ABI encoded data for the provided function selector and arguments
@ -63,7 +63,7 @@ impl BaseContract {
args: T, args: T,
) -> Result<Bytes, AbiError> { ) -> Result<Bytes, AbiError> {
let function = self.get_from_signature(signature)?; let function = self.get_from_signature(signature)?;
encode_fn(function, args) encode_function_data(function, args)
} }
/// Decodes the provided ABI encoded function arguments with the selected function name. /// Decodes the provided ABI encoded function arguments with the selected function name.
@ -76,7 +76,7 @@ impl BaseContract {
bytes: T, bytes: T,
) -> Result<D, AbiError> { ) -> Result<D, AbiError> {
let function = self.abi.function(name)?; let function = self.abi.function(name)?;
decode_fn(function, bytes, true) decode_function_data(function, bytes, true)
} }
/// Decodes for a given event name, given the `log.topics` and /// Decodes for a given event name, given the `log.topics` and
@ -98,7 +98,7 @@ impl BaseContract {
bytes: T, bytes: T,
) -> Result<D, AbiError> { ) -> Result<D, AbiError> {
let function = self.get_from_signature(signature)?; let function = self.get_from_signature(signature)?;
decode_fn(function, bytes, true) decode_function_data(function, bytes, true)
} }
fn get_from_signature(&self, signature: Selector) -> Result<&Function, AbiError> { fn get_from_signature(&self, signature: Selector) -> Result<&Function, AbiError> {
@ -147,14 +147,14 @@ pub(crate) fn decode_event<D: Detokenize>(
Ok(D::from_tokens(tokens)?) Ok(D::from_tokens(tokens)?)
} }
// Helper for encoding arguments for a specific function /// Helper for ABI encoding arguments for a specific function
pub(crate) fn encode_fn<T: Tokenize>(function: &Function, args: T) -> Result<Bytes, AbiError> { pub fn encode_function_data<T: Tokenize>(function: &Function, args: T) -> Result<Bytes, AbiError> {
let tokens = args.into_tokens(); let tokens = args.into_tokens();
Ok(function.encode_input(&tokens).map(Into::into)?) Ok(function.encode_input(&tokens).map(Into::into)?)
} }
// Helper for decoding bytes from a specific function /// Helper for ABI decoding raw data based on a function's input or output.
pub fn decode_fn<D: Detokenize, T: AsRef<[u8]>>( pub fn decode_function_data<D: Detokenize, T: AsRef<[u8]>>(
function: &Function, function: &Function,
bytes: T, bytes: T,
is_input: bool, is_input: bool,

View File

@ -1,4 +1,4 @@
use super::base::{decode_fn, AbiError}; use super::base::{decode_function_data, AbiError};
use ethers_core::{ use ethers_core::{
abi::{Detokenize, Function, InvalidOutputType}, abi::{Detokenize, Function, InvalidOutputType},
types::{Address, BlockNumber, Bytes, TransactionRequest, U256}, types::{Address, BlockNumber, Bytes, TransactionRequest, U256},
@ -120,7 +120,7 @@ where
.map_err(ContractError::MiddlewareError)?; .map_err(ContractError::MiddlewareError)?;
// decode output // decode output
let data = decode_fn(&self.function, &bytes, false)?; let data = decode_function_data(&self.function, &bytes, false)?;
Ok(data) Ok(data)
} }

View File

@ -1,5 +1,5 @@
use super::{ use super::{
base::{encode_fn, AbiError, BaseContract}, base::{encode_function_data, AbiError, BaseContract},
call::ContractCall, call::ContractCall,
event::Event, event::Event,
}; };
@ -227,7 +227,7 @@ impl<M: Middleware> Contract<M> {
function: &Function, function: &Function,
args: T, args: T,
) -> Result<ContractCall<M, D>, AbiError> { ) -> Result<ContractCall<M, D>, AbiError> {
let data = encode_fn(function, args)?; let data = encode_function_data(function, args)?;
// create the tx object // create the tx object
let tx = TransactionRequest { let tx = TransactionRequest {

View File

@ -1,4 +1,4 @@
use crate::{base::decode_event, ContractError, EventStream}; use crate::{base::decode_event, stream::EventStream, ContractError};
use ethers_core::{ use ethers_core::{
abi::{Detokenize, Event as AbiEvent}, abi::{Detokenize, Event as AbiEvent},

View File

@ -17,7 +17,7 @@ mod contract;
pub use contract::Contract; pub use contract::Contract;
mod base; mod base;
pub use base::{decode_fn, BaseContract}; pub use base::{decode_function_data, encode_function_data, BaseContract};
mod call; mod call;
pub use call::ContractError; pub use call::ContractError;
@ -28,7 +28,6 @@ pub use factory::ContractFactory;
mod event; mod event;
mod stream; mod stream;
pub use stream::EventStream;
mod multicall; mod multicall;
pub use multicall::Multicall; pub use multicall::Multicall;

View File

@ -28,6 +28,7 @@ struct EthGasStationResponse {
} }
impl EthGasStation { impl EthGasStation {
/// Creates a new [EthGasStation](https://docs.ethgasstation.info/) gas oracle
pub fn new(api_key: Option<&'static str>) -> Self { pub fn new(api_key: Option<&'static str>) -> Self {
let url = match api_key { let url = match api_key {
Some(key) => format!("{}?api-key={}", ETH_GAS_STATION_URL_PREFIX, key), Some(key) => format!("{}?api-key={}", ETH_GAS_STATION_URL_PREFIX, key),
@ -43,6 +44,7 @@ impl EthGasStation {
} }
} }
/// Sets the gas price category to be used when fetching the gas price.
pub fn category(mut self, gas_category: GasCategory) -> Self { pub fn category(mut self, gas_category: GasCategory) -> Self {
self.gas_category = gas_category; self.gas_category = gas_category;
self self

View File

@ -39,6 +39,7 @@ struct EtherchainResponse {
} }
impl Etherchain { impl Etherchain {
/// Creates a new [Etherchain](https://etherchain.org/tools/gasPriceOracle) gas price oracle.
pub fn new() -> Self { pub fn new() -> Self {
let url = Url::parse(ETHERCHAIN_URL).expect("invalid url"); let url = Url::parse(ETHERCHAIN_URL).expect("invalid url");
@ -49,6 +50,7 @@ impl Etherchain {
} }
} }
/// Sets the gas price category to be used when fetching the gas price.
pub fn category(mut self, gas_category: GasCategory) -> Self { pub fn category(mut self, gas_category: GasCategory) -> Self {
self.gas_category = gas_category; self.gas_category = gas_category;
self self

View File

@ -39,6 +39,7 @@ struct EtherscanResponseInner {
} }
impl Etherscan { impl Etherscan {
/// Creates a new [Etherscan](https://etherscan.io/gastracker) gas price oracle.
pub fn new(api_key: Option<&str>) -> Self { pub fn new(api_key: Option<&str>) -> Self {
let url = match api_key { let url = match api_key {
Some(key) => format!("{}&apikey={}", ETHERSCAN_URL_PREFIX, key), Some(key) => format!("{}&apikey={}", ETHERSCAN_URL_PREFIX, key),
@ -54,6 +55,7 @@ impl Etherscan {
} }
} }
/// Sets the gas price category to be used when fetching the gas price.
pub fn category(mut self, gas_category: GasCategory) -> Self { pub fn category(mut self, gas_category: GasCategory) -> Self {
self.gas_category = gas_category; self.gas_category = gas_category;
self self

View File

@ -40,6 +40,7 @@ struct GasNowResponseInner {
} }
impl GasNow { impl GasNow {
/// Creates a new [GasNow](https://gasnow.org) gas price oracle.
pub fn new() -> Self { pub fn new() -> Self {
let url = Url::parse(GAS_NOW_URL).expect("invalid url"); let url = Url::parse(GAS_NOW_URL).expect("invalid url");
@ -50,6 +51,7 @@ impl GasNow {
} }
} }
/// Sets the gas price category to be used when fetching the gas price.
pub fn category(mut self, gas_category: GasCategory) -> Self { pub fn category(mut self, gas_category: GasCategory) -> Self {
self.gas_category = gas_category; self.gas_category = gas_category;
self self

View File

@ -1,14 +1,18 @@
//! # Ethers Middleware //! # Ethers Middleware
//! //!
//! Ethers uses a middleware architecture. You start the middleware stack with //! Ethers uses a middleware-based architecture. You start the middleware stack with
//! a [`Provider`](ethers_providers::Provider), and wrap it with additional //! a [`Provider`](ethers_providers::Provider), and wrap it with additional
//! middleware functionalities that you need. //! middleware functionalities that you need.
//! //!
//! ## Available Middleware //! ## Available Middleware
//! - Signer //! - [`Signer`](crate::SignerMiddleware): Signs transactions locally, with a private
//! - Nonce Manager //! key or a hardware wallet
//! - Gas Escalator //! - [`Nonce Manager`](crate::NonceManagerMiddleware): Manages nonces locally, allowing
//! - Gas Oracle //! the rapid broadcast of transactions without having to wait for them to be submitted
//! - [`Gas Escalator`](crate::gas_escalator::GasEscalatorMiddleware): Bumps transaction
//! gas prices in the background
//! - [`Gas Oracle`](crate::gas_oracle): Allows getting your gas price estimates from
//! places other than `eth_gasPrice`.
//! //!
//! ## Example of a middleware stack //! ## Example of a middleware stack
//! //!
@ -49,19 +53,22 @@
//! // ... do something with the provider //! // ... do something with the provider
//! ``` //! ```
/// The gas escalator middleware is used to re-broadcast transactions with an /// The [Gas Escalator middleware](crate::gas_escalator::GasEscalatorMiddleware)
/// increasing gas price to guarantee their timely inclusion /// is used to re-broadcast transactions with an increasing gas price to guarantee
/// their timely inclusion.
pub mod gas_escalator; pub mod gas_escalator;
/// The gas oracle middleware is used to get the gas price from a list of gas oracles /// The gas oracle middleware is used to get the gas price from a list of gas oracles
/// instead of using eth_gasPrice /// instead of using eth_gasPrice. For usage examples, refer to the
/// [`GasOracle`](crate::gas_oracle::GasOracle) trait.
pub mod gas_oracle; pub mod gas_oracle;
/// The nonce manager middleware is used to locally calculate nonces instead of /// The [Nonce Manager](crate::NonceManagerMiddleware) is used to locally calculate nonces instead of
/// using eth_getTransactionCount /// using eth_getTransactionCount
pub mod nonce_manager; pub mod nonce_manager;
pub use nonce_manager::NonceManagerMiddleware;
/// The signer middleware is used to locally sign transactions and messages /// The [Signer](crate::SignerMiddleware) is used to locally sign transactions and messages
/// instead of using eth_sendTransaction and eth_sign /// instead of using eth_sendTransaction and eth_sign
pub mod signer; pub mod signer;
pub use signer::SignerMiddleware; pub use signer::SignerMiddleware;

View File

@ -8,17 +8,18 @@ use thiserror::Error;
/// Middleware used for calculating nonces locally, useful for signing multiple /// Middleware used for calculating nonces locally, useful for signing multiple
/// consecutive transactions without waiting for them to hit the mempool /// consecutive transactions without waiting for them to hit the mempool
pub struct NonceManagerMiddleware<M> { pub struct NonceManagerMiddleware<M> {
pub inner: M, inner: M,
pub initialized: AtomicBool, initialized: AtomicBool,
pub nonce: AtomicU64, nonce: AtomicU64,
pub address: Address, address: Address,
} }
impl<M> NonceManagerMiddleware<M> impl<M> NonceManagerMiddleware<M>
where where
M: Middleware, M: Middleware,
{ {
/// Instantiates the nonce manager with a 0 nonce. /// Instantiates the nonce manager with a 0 nonce. The `address` should be the
/// address which you'll be sending transactions from
pub fn new(inner: M, address: Address) -> Self { pub fn new(inner: M, address: Address) -> Self {
Self { Self {
initialized: false.into(), initialized: false.into(),

View File

@ -113,13 +113,70 @@ pub trait FromErr<T> {
#[async_trait] #[async_trait]
#[auto_impl(&, Box, Arc)] #[auto_impl(&, Box, Arc)]
/// A middleware allows customizing requests send and received from an ethereum node.
///
/// Writing a middleware is as simple as:
/// 1. implementing the [`inner`](crate::Middleware::inner) method to point to the next layer in the "middleware onion",
/// 2. implementing the [`FromErr`](crate::FromErr) trait on your middleware's error type
/// 3. implementing any of the methods you want to override
///
/// ```rust
/// use ethers::{providers::{Middleware, FromErr}, types::{U64, TransactionRequest, U256}};
/// use thiserror::Error;
/// use async_trait::async_trait;
///
/// #[derive(Debug)]
/// struct MyMiddleware<M>(M);
///
/// #[derive(Error, Debug)]
/// pub enum MyError<M: Middleware> {
/// #[error("{0}")]
/// MiddlewareError(M::Error),
///
/// // Add your middleware's specific errors here
/// }
///
/// impl<M: Middleware> FromErr<M::Error> for MyError<M> {
/// fn from(src: M::Error) -> MyError<M> {
/// MyError::MiddlewareError(src)
/// }
/// }
///
/// #[async_trait]
/// impl<M> Middleware for MyMiddleware<M>
/// where
/// M: Middleware,
/// {
/// type Error = MyError<M>;
/// type Provider = M::Provider;
/// type Inner = M;
///
/// fn inner(&self) -> &M {
/// &self.0
/// }
///
/// /// Overrides the default `get_block_number` method to always return 0
/// async fn get_block_number(&self) -> Result<U64, Self::Error> {
/// Ok(U64::zero())
/// }
///
/// /// Overrides the default `estimate_gas` method to log that it was called,
/// /// before forwarding the call to the next layer.
/// async fn estimate_gas(&self, tx: &TransactionRequest) -> Result<U256, Self::Error> {
/// println!("Estimating gas...");
/// self.inner().estimate_gas(tx).await.map_err(FromErr::from)
/// }
/// }
/// ```
pub trait Middleware: Sync + Send + Debug { pub trait Middleware: Sync + Send + Debug {
type Error: Sync + Send + Error + FromErr<<Self::Inner as Middleware>::Error>; type Error: Sync + Send + Error + FromErr<<Self::Inner as Middleware>::Error>;
type Provider: JsonRpcClient; type Provider: JsonRpcClient;
type Inner: Middleware<Provider = Self::Provider>; type Inner: Middleware<Provider = Self::Provider>;
/// The next middleware in the stack
fn inner(&self) -> &Self::Inner; fn inner(&self) -> &Self::Inner;
/// The HTTP or Websocket provider.
fn provider(&self) -> &Provider<Self::Provider> { fn provider(&self) -> &Provider<Self::Provider> {
self.inner().provider() self.inner().provider()
} }

View File

@ -26,6 +26,7 @@ pub trait PubsubClient: JsonRpcClient {
#[must_use = "subscriptions do nothing unless you stream them"] #[must_use = "subscriptions do nothing unless you stream them"]
#[pin_project(PinnedDrop)] #[pin_project(PinnedDrop)]
/// Streams data from an installed filter via `eth_subscribe`
pub struct SubscriptionStream<'a, P: PubsubClient, R: DeserializeOwned> { pub struct SubscriptionStream<'a, P: PubsubClient, R: DeserializeOwned> {
/// The subscription's installed id on the ethereum node /// The subscription's installed id on the ethereum node
pub id: U256, pub id: U256,

View File

@ -31,6 +31,7 @@ enum FilterWatcherState<'a, R> {
#[must_use = "filters do nothing unless you stream them"] #[must_use = "filters do nothing unless you stream them"]
#[pin_project] #[pin_project]
/// Streams data from an installed filter via `eth_getFilterCahnges`
pub struct FilterWatcher<'a, P, R> { pub struct FilterWatcher<'a, P, R> {
/// The filter's installed id on the ethereum node /// The filter's installed id on the ethereum node
pub id: U256, pub id: U256,

View File

@ -4,9 +4,13 @@ use std::fmt;
use thiserror::Error; use thiserror::Error;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
/// Ledger wallet type
pub enum DerivationType { pub enum DerivationType {
/// Ledger Live-generated HD path
LedgerLive(usize), LedgerLive(usize),
/// Legacy generated HD Path
Legacy(usize), Legacy(usize),
/// Any other path
Other(String), Other(String),
} }
@ -25,6 +29,7 @@ impl fmt::Display for DerivationType {
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
/// Error when using the Ledger transport
pub enum LedgerError { pub enum LedgerError {
/// Underlying ledger transport error /// Underlying ledger transport error
#[error(transparent)] #[error(transparent)]
@ -34,10 +39,8 @@ pub enum LedgerError {
UnexpectedNullResponse, UnexpectedNullResponse,
#[error(transparent)] #[error(transparent)]
/// Error when converting from a hex string
HexError(#[from] hex::FromHexError), HexError(#[from] hex::FromHexError),
#[error("Error when decoding UTF8 Response: {0}")]
Utf8Error(#[from] std::str::Utf8Error),
} }
pub const P1_FIRST: u8 = 0x00; pub const P1_FIRST: u8 = 0x00;

View File

@ -7,14 +7,15 @@
//! and the [`TransactionRequest`] to a [`Transaction`], look at the signing middleware. //! and the [`TransactionRequest`] to a [`Transaction`], look at the signing middleware.
//! //!
//! Supported signers: //! Supported signers:
//! - Private key //! - [Private key](crate::LocalWallet)
//! - Ledger //! - [Ledger](crate::Ledger)
//! - [YubiHSM2](crate::YubiWallet)
//! //!
//! ```no_run //! ```no_run
//! # use ethers::{ //! # use ethers::{
//! signers::{LocalWallet, Signer}, //! # signers::{LocalWallet, Signer},
//! core::{k256::ecdsa::SigningKey, types::TransactionRequest}, //! # core::{k256::ecdsa::SigningKey, types::TransactionRequest},
//! }; //! # };
//! # async fn foo() -> Result<(), Box<dyn std::error::Error>> { //! # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
//! // instantiate the wallet //! // instantiate the wallet
//! let wallet = "dcf2cbdd171a21c480aa7f53d77f31bb102282b3ff099c78e3118b37348c72f7" //! let wallet = "dcf2cbdd171a21c480aa7f53d77f31bb102282b3ff099c78e3118b37348c72f7"
@ -37,8 +38,6 @@
//! //!
//! [`Transaction`]: ethers_core::types::Transaction //! [`Transaction`]: ethers_core::types::Transaction
//! [`TransactionRequest`]: ethers_core::types::TransactionRequest //! [`TransactionRequest`]: ethers_core::types::TransactionRequest
// mod wallet;
// pub use wallet::Wallet;
mod wallet; mod wallet;
pub use wallet::Wallet; pub use wallet::Wallet;

View File

@ -14,10 +14,6 @@
//! //!
//! > ethers-rs is a port of [ethers-js](github.com/ethers-io/ethers.js) in Rust. //! > ethers-rs is a port of [ethers-js](github.com/ethers-io/ethers.js) in Rust.
//! //!
//! _Note: All examples using `await` are assuming that they are called from inside an `async`
//! function which is run in an async runtime. You are free to use any runtime and executor of
//! your preference._
//!
//! ## Quickstart: `prelude` //! ## Quickstart: `prelude`
//! //!
//! A prelude is provided which imports all the important data types and traits for you. Use this //! A prelude is provided which imports all the important data types and traits for you. Use this
@ -55,8 +51,9 @@
//! //!
//! ## `signers` //! ## `signers`
//! //!
//! For security reasons, you typically do not want your private keys to be stored on the nodes. //! This module provides a [`Signer`] trait which can be used for signing messages
//! This module provides a [`Wallet`] type for connecting to a private key or a YubiHSM2 //! or transactions. A [`Wallet`] type is implemented which can be used with a
//! raw private key, or a YubiHSM2. We also provide Ledger support.
//! //!
//! ## `contract` //! ## `contract`
//! //!
@ -71,14 +68,23 @@
//! [`Contract`] and [`ContractFactory`] abstractions so that you do not have to worry about that. //! [`Contract`] and [`ContractFactory`] abstractions so that you do not have to worry about that.
//! It also provides typesafe bindings via the [`abigen`] macro and the [`Abigen` builder]. //! It also provides typesafe bindings via the [`abigen`] macro and the [`Abigen` builder].
//! //!
//! [`Provider`]: providers::Provider //! ## `middleware`
//! [`Wallet`]: signers::Wallet
//! //!
//! In order to keep the ethers architecture as modular as possible, providers define a [`Middleware`]
//! trait which defines all the methods to interact with an Ethereum node. By implementing the
//! middleware trait, you are able to override the default behavior of methods and do things such
//! as using other gas oracles, escalating your transactions' gas prices, or signing your transactions
//! with a [`Signer`]. The middleware architecture allows users to either use one of the existing
//! middleware, or they are free to write on of their own.
//!
//! [`Provider`]: providers::Provider
//! [`Middleware`]: providers::Middleware
//! [`Wallet`]: signers::Wallet
//! [`Signer`]: signers::Signer
//! [`ContractFactory`]: contract::ContractFactory //! [`ContractFactory`]: contract::ContractFactory
//! [`Contract`]: contract::Contract //! [`Contract`]: contract::Contract
//! [`abigen`]: ./contract/macro.abigen.html //! [`abigen`]: ./contract/macro.abigen.html
//! [`Abigen` builder]: contract::Abigen //! [`Abigen` builder]: contract::Abigen
//!
//! [`utils`]: core::utils //! [`utils`]: core::utils
//! [`abi`]: core::abi //! [`abi`]: core::abi
//! [`types`]: core::types //! [`types`]: core::types