ETHER.RS
Rust × Ethereum

From ethers-rs
to Alloy.

A small first migration, a useful API map, and the checks that keep your amounts intact.

Reviewed 18 September 2026 · Targets Alloy 2.4.2 · Independent guide

Migrate one read before the whole application.

The ethers-rs repository is archived and its maintainers direct users to Alloy. Existing applications do not automatically stop working, but a migration needs more than a dependency rename. Read the ethers-rs notice.

Our suggested first milestone is a separate executable that reads a block number. Keep the old application running while you compare one feature at a time. This makes it easier to distinguish a migration regression from an RPC outage.

The API cheat sheet

ethers-rsAlloy / replacementMigration note
ethersalloyStart with the umbrella crate.
ethers::types::Addressalloy::primitives::AddressUpdate public interfaces as well as imports.
ethers::types::U256alloy::primitives::U256Same-sized integer, different Rust type.
ethers::types::H256alloy::primitives::B256A fixed-size hash, not a numeric amount.
ethers::providersalloy::providersBuild a provider and bring the Provider trait into scope.
ethers::middlewareProvider fillers and layersReview each behavior; this is not a direct rename.
ethers::solcfoundry-compilersCompiler support lives outside Alloy.
ethers::etherscanfoundry-block-explorersExplorer APIs have a separate replacement.

Mappings checked against Alloy's migration reference. Some middleware has no planned direct replacement; inventory your dependencies before removing ethers.

Your first Alloy provider

Create a small binary project with cargo new alloy-read-check, then open its folder. Add these dependencies to its existing Cargo.toml. Keep default features enabled for this first example.

Cargo.toml — dependencies

[dependencies]
alloy = "=2.4.2"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

src/main.rs

use alloy::providers::{Provider, ProviderBuilder};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let endpoint = std::env::var("RPC_URL")?;
    let client = ProviderBuilder::new().connect_http(endpoint.parse()?);
    let height = client.get_block_number().await?;
    println!("Connected. Latest block: {height}");
    Ok(())
}

Set RPC_URL to an Ethereum HTTP endpoint that you can access. These examples use a public endpoint; availability and rate limits can vary.

Linux / macOS shell

RPC_URL=https://ethereum-rpc.publicnode.com cargo run

Windows PowerShell

$env:RPC_URL = "https://ethereum-rpc.publicnode.com"
cargo run

The expected result is a block height, not a fixed number. This program reads chain data and sends no transaction. The exact version pin makes the initial migration target explicit; review upgrades separately.

API shape checked against the official HTTP example and installation guide. These snippets have been documentation-reviewed, not compiled by Ether.rs. Run cargo check in your own toolchain before integrating them.

Preserve amounts as integers.

Replace src/main.rs temporarily with this separate conversion example. A decimal string lets you express 0.001 ETH without first introducing floating-point rounding.

Standalone unit conversion

use alloy::primitives::utils::{parse_ether, format_ether};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let amount = parse_ether("0.001")?;
    println!("Wei: {amount}");
    println!("ETH: {}", format_ether(amount));
    Ok(())
}

The Wei value should be 1000000000000000. Compare it with the Wei → ETH converter. For gas, remember that a price in Gwei is a price per unit of gas; use the gas calculator to check a complete fee scenario.

Formatting helpers are exposed under Alloy's primitive utilities. Do not assume every ERC-20 token uses ETH's 18 decimals.

When the first compile fails

A provider method cannot be found

Check that the Provider trait is imported, the intended feature is enabled, and the example matches your installed version. The example above uses connect_http; copying constructors from a different Alloy release can produce misleading errors.

Two U256 values will not combine

Check where each type comes from. An ethers integer and an Alloy integer are different Rust types. During a staged migration, keep conversions at explicit module boundaries and test values larger than 64 bits, zero, and boundary values.

The amounts match, but the fee does not

Compare the same block and fee assumptions. Do not compare a maximum fee cap in one implementation with an effective gas price in the other. Record the chain, block, gas estimate and units alongside each comparison.

An old middleware chain has no obvious equivalent

List what each layer actually does: nonce selection, signing, gas estimation, retries, or a custom policy. Port and verify each behavior independently rather than assuming one builder option replaces the whole chain.

A practical migration checkpoint

  • Confirm the RPC endpoint uses the intended chain.
  • Compare a read against a fixed block so moving chain state does not obscure differences.
  • Check integer amounts, ABI inputs and returned values at module boundaries.
  • Test a failed RPC call and a reverted contract call, not only successful reads.
  • Move signing and transaction submission only after the read path passes; exercise writes on a local fork or test network first.
  • Run your project tests and inspect the dependency tree before removing ethers.

Commit Cargo.lock for your application and retain your comparison fixtures. A migration is complete when the behaviors your application relies on agree—not simply when the imports compile.

Ether.rs is independent of ethers-rs, Alloy and the Ethereum Foundation. This is a focused starting guide, not an automatic migration tool.