ethers-rs/ethers-middleware
Andrea Simeoni 16ab3d5820
Feat middleware stack builder (#1890)
* ProviderBuilder struct + docs

* README

* Docs typos

* Removed useless Arc
Rename module

* Unit tests

* cargo +nightly fmt

* CHANGELOG

* CHANGELOG typo

* ci: fix docs job

* review: Removed Option<M>. Builder functions directly consume self.

* feat: `with_signer` builder function

* feat: `nonce_manager` builder function

* feat: `gas_oracle` builder function

* Unit tests

* Docs

* fix: cargo +nightly fmt

* Update ethers-middleware/src/builder.rs

Typo

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>

* Builder functions provided as a trait
Update docs
Update tests

* cargo +nightly fmt

* CHANGELOG

Co-authored-by: Andrea Simeoni <>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2022-11-28 12:16:21 -08:00
..
contracts feat(abigen): extend ethevent trait methods and decoding (#239) 2021-03-19 17:44:59 +02:00
src Feat middleware stack builder (#1890) 2022-11-28 12:16:21 -08:00
tests Feat middleware stack builder (#1890) 2022-11-28 12:16:21 -08:00
Cargo.toml (cargo-release) version 1.0.2 2022-11-27 12:27:58 -08:00
README.md Feat middleware stack builder (#1890) 2022-11-28 12:16:21 -08:00

README.md

Ethers uses a middleware-based architecture. You start the middleware stack with a Provider, and wrap it with additional middleware functionalities that you need.

Available Middleware

  • Signer: Signs transactions locally, with a private key or a hardware wallet
  • Nonce Manager: Manages nonces locally, allowing the rapid broadcast of transactions without having to wait for them to be submitted
  • Gas Escalator: Bumps transaction gas prices in the background
  • Gas Oracle: Allows getting your gas price estimates from places other than eth_gasPrice.
  • Transformer: Allows intercepting and transforming a transaction to be broadcasted via a proxy wallet, e.g. DSProxy.

Example of a middleware stack

use ethers_providers::{Provider, Http};
use ethers_signers::{LocalWallet, Signer};
use ethers_middleware::{
    gas_escalator::{GasEscalatorMiddleware, GeometricGasPrice, Frequency},
    gas_oracle::{GasOracleMiddleware, EthGasStation, GasCategory},
    signer::SignerMiddleware,
    nonce_manager::NonceManagerMiddleware,
};
use ethers_core::rand;
use std::convert::TryFrom;

// Start the stack
let provider = Provider::<Http>::try_from("http://localhost:8545").unwrap();

// Escalate gas prices
let escalator = GeometricGasPrice::new(1.125, 60u64, None::<u64>);
let provider =
    GasEscalatorMiddleware::new(provider, escalator, Frequency::PerBlock);

// Sign transactions with a private key
let signer = LocalWallet::new(&mut rand::thread_rng());
let address = signer.address();
let provider = SignerMiddleware::new(provider, signer);

// Use EthGasStation as the gas oracle
let gas_oracle = EthGasStation::new(None);
let provider = GasOracleMiddleware::new(provider, gas_oracle);

// Manage nonces locally
let provider = NonceManagerMiddleware::new(provider, address);

// ... do something with the provider

Example of a middleware stack using a builder

Each Middleware implements the trait MiddlewareBuilder to help composition of a 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:

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::<LocalWallet>().unwrap();
    let address = signer.address();
    let escalator = GeometricGasPrice::new(1.125, 60_u64, None::<u64>);
    let gas_oracle = EthGasStation::new(None);

    let provider = Provider::<Http>::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::<LocalWallet>().unwrap();
    let address = signer.address();
    let escalator = GeometricGasPrice::new(1.125, 60_u64, None::<u64>);

    let provider = Provider::<Http>::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
}