ethers-rs/ethers-middleware
DaniPopes c3748e95ea
Merge branch 'master' into ci/improve
2023-02-25 12:50:11 +01:00
..
contracts feat(abigen): extend ethevent trait methods and decoding (#239) 2021-03-19 17:44:59 +02:00
src Expose contract revert errors in the ContractError struct (#2172) 2023-02-22 14:52:25 -08:00
tests/it Merge branch 'master' into ci/improve 2023-02-25 12:50:11 +01:00
Cargo.toml chore(deps): bump once_cell from 1.17.0 to 1.17.1 (#2156) 2023-02-15 10:55:04 -08:00
README.md fix(middleware): oracles, tests (#1944) 2022-12-18 13:45:47 +02:00

README.md

Your ethers application interacts with the blockchain through a Provider abstraction. Provider is a special type of 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 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: Signs transactions locally, with a private key or a hardware wallet.
  • Nonce Manager: Manages nonces locally. Allows to sign multiple consecutive transactions without waiting for them to hit the mempool.
  • Gas Escalator: Bumps transactions gas price in the background to avoid getting them stuck in the memory pool. A GasEscalatorMiddleware supports different escalation strategies (see GasEscalator) and bump frequencies (see Frequency).
  • Gas Oracle: Allows getting your gas price estimates from places other than eth_gasPrice, including REST based gas stations (i.e. Etherscan, ETH Gas Station etc.).
  • Transformer: Allows intercepting and transforming a transaction to be broadcasted via a proxy wallet, e.g. DSProxy.

Stacking middlewares using a builder

Each Middleware implements the trait MiddlewareBuilder. This trait helps a developer to compose a custom Middleware stack.

The following example shows how to build a composed Middleware starting from a Provider:

use ethers_providers::{Middleware, Provider, Http};
use std::sync::Arc;
use std::convert::TryFrom;
use ethers_signers::{LocalWallet, Signer};
use ethers_middleware::{gas_oracle::{GasOracle, GasNow}, MiddlewareBuilder};

fn builder_example() {
    let key = "fdb33e2105f08abe41a8ee3b758726a31abdd57b7a443f470f23efce853af169";
    let signer = key.parse::<LocalWallet>().unwrap();
    let address = signer.address();
    let gas_oracle = GasNow::new();

    let provider = Provider::<Http>::try_from("http://localhost:8545")
        .unwrap()
        .gas_oracle(gas_oracle)
        .with_signer(signer)
        .nonce_manager(address); // Outermost layer
}

The wrap_into function can be used to wrap Middleware layers explicitly. This is useful when pushing Middlewares not directly handled by the builder interface.

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::<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, GasNow::new()))
        .wrap_into(|p| NonceManagerMiddleware::new(p, address)); // Outermost layer
}

Stacking middlewares manually

A Middleware stack can be also constructed manually. This is achieved by explicitly wrapping layers.

use ethers_providers::{Provider, Http};
use ethers_signers::{LocalWallet, Signer};
use ethers_middleware::{
    gas_escalator::{GasEscalatorMiddleware, GeometricGasPrice, Frequency},
    gas_oracle::{GasOracleMiddleware, GasCategory, GasNow},
    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 GasNow as the gas oracle
let gas_oracle = GasNow::new();
let provider = GasOracleMiddleware::new(provider, gas_oracle);

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

// ... do something with the provider