diff --git a/ethers-middleware/README.md b/ethers-middleware/README.md index 186c571c..97d37ab2 100644 --- a/ethers-middleware/README.md +++ b/ethers-middleware/README.md @@ -1,23 +1,71 @@ -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. +Your ethers application interacts with the blockchain through a [`Provider`](ethers_providers::Provider) abstraction. [`Provider`](ethers_providers::Provider) is a special type of [`Middleware`](ethers_providers::Middleware) that can be composed with others to obtain a layered architecture. This approach promotes "Open Closed Principle", "Single Responsibility" and composable patterns. The building process happens in a wrapping fashion, and starts from a [`Provider`](ethers_providers::Provider) being the first element in the stack. This process continues having new middlewares being pushed on top of a layered data structure. + ## Available Middleware -- [`Signer`](./signer/struct.SignerMiddleware.html): Signs transactions locally, - with a private key or a hardware wallet -- [`Nonce Manager`](./nonce_manager/struct.NonceManagerMiddleware.html): Manages - nonces locally, allowing the rapid broadcast of transactions without having to - wait for them to be submitted -- [`Gas Escalator`](./gas_escalator/struct.GasEscalatorMiddleware.html): Bumps - transaction gas prices in the background +- [`Signer`](./signer/struct.SignerMiddleware.html): Signs transactions locally, with a private key or a hardware wallet. +- [`Nonce Manager`](./nonce_manager/struct.NonceManagerMiddleware.html): Manages nonces locally. Allows to sign multiple consecutive transactions without waiting for them to hit the mempool. +- [`Gas Escalator`](./gas_escalator/struct.GasEscalatorMiddleware.html): Bumps transactions gas price in the background to avoid getting them stuck in the memory pool. A [`GasEscalatorMiddleware`](crate::gas_escalator::GasEscalatorMiddleware) supports different escalation strategies (see [GasEscalator](crate::gas_escalator::GasEscalator)) and bump frequencies (see [Frequency](crate::gas_escalator::Frequency)). - [`Gas Oracle`](./gas_oracle/struct.GasOracleMiddleware.html): Allows getting - your gas price estimates from places other than `eth_gasPrice`. + your gas price estimates from places other than `eth_gasPrice`, including REST based gas stations (i.e. Etherscan, ETH Gas Station etc.). - [`Transformer`](./transformer/trait.Transformer.html): Allows intercepting and transforming a transaction to be broadcasted via a proxy wallet, e.g. [`DSProxy`](./transformer/struct.DsProxy.html). -## Example of a middleware stack +## Stacking middlewares using a builder + +Each [`Middleware`](ethers_providers::Middleware) implements the trait [MiddlewareBuilder](crate::MiddlewareBuilder). This trait helps a developer to compose a custom [`Middleware`](ethers_providers::Middleware) stack. + +The following example shows how to build a composed [`Middleware`](ethers_providers::Middleware) starting from a [`Provider`](ethers_providers::Provider): + +```rust +use ethers_providers::{Middleware, Provider, Http}; +use std::sync::Arc; +use std::convert::TryFrom; +use ethers_signers::{LocalWallet, Signer}; +use ethers_middleware::{*,gas_oracle::*}; + +fn builder_example() { + let key = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169"; + let signer = key.parse::().unwrap(); + let address = signer.address(); + let gas_oracle = EthGasStation::new(None); + + let provider = Provider::::try_from("http://localhost:8545") + .unwrap() + .gas_oracle(gas_oracle) + .with_signer(signer) + .nonce_manager(address); // Outermost layer +} +``` + +The [wrap_into](crate::MiddlewareBuilder::wrap_into) function can be used to wrap [`Middleware`](ethers_providers::Middleware) layers explicitly. This is useful when pushing [`Middleware`](ethers_providers::Middleware)s not directly handled by the builder interface. + +```rust +use ethers_providers::{Middleware, Provider, Http}; +use std::sync::Arc; +use std::convert::TryFrom; +use ethers_signers::{LocalWallet, Signer}; +use ethers_middleware::{*,gas_escalator::*,gas_oracle::*}; + +fn builder_example_wrap_into() { + let key = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169"; + let signer = key.parse::().unwrap(); + let address = signer.address(); + let escalator = GeometricGasPrice::new(1.125, 60_u64, None::); + + let provider = Provider::::try_from("http://localhost:8545") + .unwrap() + .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) + .wrap_into(|p| SignerMiddleware::new(p, signer)) + .wrap_into(|p| GasOracleMiddleware::new(p, EthGasStation::new(None))) + .wrap_into(|p| NonceManagerMiddleware::new(p, address)); // Outermost layer +} +``` + + +## Stacking middlewares manually +A [`Middleware`](ethers_providers::Middleware) stack can be also constructed manually. This is achieved by explicitly wrapping layers. ```rust no_run use ethers_providers::{Provider, Http}; @@ -36,8 +84,7 @@ let provider = Provider::::try_from("http://localhost:8545").unwrap(); // Escalate gas prices let escalator = GeometricGasPrice::new(1.125, 60u64, None::); -let provider = - GasEscalatorMiddleware::new(provider, escalator, Frequency::PerBlock); +let provider = GasEscalatorMiddleware::new(provider, escalator, Frequency::PerBlock); // Sign transactions with a private key let signer = LocalWallet::new(&mut rand::thread_rng()); @@ -53,43 +100,3 @@ let provider = NonceManagerMiddleware::new(provider, address); // ... do something with the provider ``` -## Example of a middleware stack using a builder - -Each [`Middleware`](ethers_providers::Middleware) implements the trait [MiddlewareBuilder](crate::MiddlewareBuilder) to help composition of a [`Middleware`](ethers_providers::Middleware) stack. As usual the composition acts in a wrapping fashion. Adding a new layer results in wrapping its predecessor. -Builder can be used as follows: -```rust -use ethers_providers::{Middleware, Provider, Http}; -use std::sync::Arc; -use std::convert::TryFrom; -use ethers_signers::{LocalWallet, Signer}; -use ethers_middleware::{*,gas_escalator::*,gas_oracle::*}; - -fn builder_example() { - let key = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169"; - let signer = key.parse::().unwrap(); - let address = signer.address(); - let escalator = GeometricGasPrice::new(1.125, 60_u64, None::); - let gas_oracle = EthGasStation::new(None); - - let provider = Provider::::try_from("http://localhost:8545") - .unwrap() - .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) - .gas_oracle(gas_oracle) - .with_signer(signer) - .nonce_manager(address); // Outermost layer -} - -fn builder_example_raw_wrap() { - let key = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169"; - let signer = key.parse::().unwrap(); - let address = signer.address(); - let escalator = GeometricGasPrice::new(1.125, 60_u64, None::); - - let provider = Provider::::try_from("http://localhost:8545") - .unwrap() - .wrap_into(|p| GasEscalatorMiddleware::new(p, escalator, Frequency::PerBlock)) - .wrap_into(|p| SignerMiddleware::new(p, signer)) - .wrap_into(|p| GasOracleMiddleware::new(p, EthGasStation::new(None))) - .wrap_into(|p| NonceManagerMiddleware::new(p, address)); // Outermost layer -} -``` diff --git a/ethers-middleware/src/builder.rs b/ethers-middleware/src/builder.rs index c9801962..b1827407 100644 --- a/ethers-middleware/src/builder.rs +++ b/ethers-middleware/src/builder.rs @@ -50,8 +50,8 @@ use ethers_signers::Signer; pub trait MiddlewareBuilder: Middleware + Sized + 'static { /// Wraps `self` inside a new [`Middleware`](ethers_providers::Middleware). /// - /// `f` Consumes `self`, must be used to return a new - /// [`Middleware`](ethers_providers::Middleware) around `self`. + /// `f` Consumes `self`. Must be used to return a new + /// [`Middleware`](ethers_providers::Middleware) wrapping `self`. fn wrap_into(self, f: F) -> T where F: FnOnce(Self) -> T, @@ -62,7 +62,7 @@ pub trait MiddlewareBuilder: Middleware + Sized + 'static { /// Wraps `self` inside a [`SignerMiddleware`](crate::SignerMiddleware). /// - /// [`Signer`] ethers_signers::Signer + /// [`Signer`](ethers_signers::Signer) fn with_signer(self, s: S) -> SignerMiddleware where S: Signer, @@ -72,14 +72,14 @@ pub trait MiddlewareBuilder: Middleware + Sized + 'static { /// Wraps `self` inside a [`NonceManagerMiddleware`](crate::NonceManagerMiddleware). /// - /// [`Address`] ethers_core::types::Address + /// [`Address`](ethers_core::types::Address) fn nonce_manager(self, address: Address) -> NonceManagerMiddleware { NonceManagerMiddleware::new(self, address) } /// Wraps `self` inside a [`GasOracleMiddleware`](crate::gas_oracle::GasOracleMiddleware). /// - /// [`Address`] ethers_core::types::Address + /// [`GasOracle`](crate::gas_oracle::GasOracle) fn gas_oracle(self, gas_oracle: G) -> GasOracleMiddleware where G: GasOracle,