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,
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`
pub fn encode<T: Tokenize>(&self, name: &str, args: T) -> Result<Bytes, AbiError> {
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
@ -63,7 +63,7 @@ impl BaseContract {
args: T,
) -> Result<Bytes, AbiError> {
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.
@ -76,7 +76,7 @@ impl BaseContract {
bytes: T,
) -> Result<D, AbiError> {
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
@ -98,7 +98,7 @@ impl BaseContract {
bytes: T,
) -> Result<D, AbiError> {
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> {
@ -147,14 +147,14 @@ pub(crate) fn decode_event<D: Detokenize>(
Ok(D::from_tokens(tokens)?)
}
// Helper for encoding arguments for a specific function
pub(crate) fn encode_fn<T: Tokenize>(function: &Function, args: T) -> Result<Bytes, AbiError> {
/// Helper for ABI encoding arguments for a specific function
pub fn encode_function_data<T: Tokenize>(function: &Function, args: T) -> Result<Bytes, AbiError> {
let tokens = args.into_tokens();
Ok(function.encode_input(&tokens).map(Into::into)?)
}
// Helper for decoding bytes from a specific function
pub fn decode_fn<D: Detokenize, T: AsRef<[u8]>>(
/// Helper for ABI decoding raw data based on a function's input or output.
pub fn decode_function_data<D: Detokenize, T: AsRef<[u8]>>(
function: &Function,
bytes: T,
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::{
abi::{Detokenize, Function, InvalidOutputType},
types::{Address, BlockNumber, Bytes, TransactionRequest, U256},
@ -120,7 +120,7 @@ where
.map_err(ContractError::MiddlewareError)?;
// decode output
let data = decode_fn(&self.function, &bytes, false)?;
let data = decode_function_data(&self.function, &bytes, false)?;
Ok(data)
}

View File

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

View File

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

View File

@ -28,6 +28,7 @@ struct EthGasStationResponse {
}
impl EthGasStation {
/// Creates a new [EthGasStation](https://docs.ethgasstation.info/) gas oracle
pub fn new(api_key: Option<&'static str>) -> Self {
let url = match api_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 {
self.gas_category = gas_category;
self

View File

@ -39,6 +39,7 @@ struct EtherchainResponse {
}
impl Etherchain {
/// Creates a new [Etherchain](https://etherchain.org/tools/gasPriceOracle) gas price oracle.
pub fn new() -> Self {
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 {
self.gas_category = gas_category;
self

View File

@ -39,6 +39,7 @@ struct EtherscanResponseInner {
}
impl Etherscan {
/// Creates a new [Etherscan](https://etherscan.io/gastracker) gas price oracle.
pub fn new(api_key: Option<&str>) -> Self {
let url = match api_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 {
self.gas_category = gas_category;
self

View File

@ -40,6 +40,7 @@ struct GasNowResponseInner {
}
impl GasNow {
/// Creates a new [GasNow](https://gasnow.org) gas price oracle.
pub fn new() -> Self {
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 {
self.gas_category = gas_category;
self

View File

@ -1,14 +1,18 @@
//! # 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
//! middleware functionalities that you need.
//!
//! ## Available Middleware
//! - Signer
//! - Nonce Manager
//! - Gas Escalator
//! - Gas Oracle
//! - [`Signer`](crate::SignerMiddleware): Signs transactions locally, with a private
//! key or a hardware wallet
//! - [`Nonce Manager`](crate::NonceManagerMiddleware): Manages nonces locally, allowing
//! 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
//!
@ -49,19 +53,22 @@
//! // ... do something with the provider
//! ```
/// The gas escalator middleware is used to re-broadcast transactions with an
/// increasing gas price to guarantee their timely inclusion
/// The [Gas Escalator middleware](crate::gas_escalator::GasEscalatorMiddleware)
/// is used to re-broadcast transactions with an increasing gas price to guarantee
/// their timely inclusion.
pub mod gas_escalator;
/// 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;
/// 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
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
pub mod signer;
pub use signer::SignerMiddleware;

View File

@ -8,17 +8,18 @@ use thiserror::Error;
/// Middleware used for calculating nonces locally, useful for signing multiple
/// consecutive transactions without waiting for them to hit the mempool
pub struct NonceManagerMiddleware<M> {
pub inner: M,
pub initialized: AtomicBool,
pub nonce: AtomicU64,
pub address: Address,
inner: M,
initialized: AtomicBool,
nonce: AtomicU64,
address: Address,
}
impl<M> NonceManagerMiddleware<M>
where
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 {
Self {
initialized: false.into(),

View File

@ -113,13 +113,70 @@ pub trait FromErr<T> {
#[async_trait]
#[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 {
type Error: Sync + Send + Error + FromErr<<Self::Inner as Middleware>::Error>;
type Provider: JsonRpcClient;
type Inner: Middleware<Provider = Self::Provider>;
/// The next middleware in the stack
fn inner(&self) -> &Self::Inner;
/// The HTTP or Websocket provider.
fn provider(&self) -> &Provider<Self::Provider> {
self.inner().provider()
}

View File

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

View File

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

View File

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

View File

@ -7,14 +7,15 @@
//! and the [`TransactionRequest`] to a [`Transaction`], look at the signing middleware.
//!
//! Supported signers:
//! - Private key
//! - Ledger
//! - [Private key](crate::LocalWallet)
//! - [Ledger](crate::Ledger)
//! - [YubiHSM2](crate::YubiWallet)
//!
//! ```no_run
//! # use ethers::{
//! signers::{LocalWallet, Signer},
//! core::{k256::ecdsa::SigningKey, types::TransactionRequest},
//! };
//! # signers::{LocalWallet, Signer},
//! # core::{k256::ecdsa::SigningKey, types::TransactionRequest},
//! # };
//! # async fn foo() -> Result<(), Box<dyn std::error::Error>> {
//! // instantiate the wallet
//! let wallet = "dcf2cbdd171a21c480aa7f53d77f31bb102282b3ff099c78e3118b37348c72f7"
@ -37,8 +38,6 @@
//!
//! [`Transaction`]: ethers_core::types::Transaction
//! [`TransactionRequest`]: ethers_core::types::TransactionRequest
// mod wallet;
// pub use wallet::Wallet;
mod 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.
//!
//! _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`
//!
//! A prelude is provided which imports all the important data types and traits for you. Use this
@ -55,8 +51,9 @@
//!
//! ## `signers`
//!
//! For security reasons, you typically do not want your private keys to be stored on the nodes.
//! This module provides a [`Wallet`] type for connecting to a private key or a YubiHSM2
//! This module provides a [`Signer`] trait which can be used for signing messages
//! 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`
//!
@ -71,14 +68,23 @@
//! [`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].
//!
//! [`Provider`]: providers::Provider
//! [`Wallet`]: signers::Wallet
//! ## `middleware`
//!
//! 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
//! [`Contract`]: contract::Contract
//! [`abigen`]: ./contract/macro.abigen.html
//! [`Abigen` builder]: contract::Abigen
//!
//! [`utils`]: core::utils
//! [`abi`]: core::abi
//! [`types`]: core::types