Alloy is the next-generation Rust library for interacting with Ethereum and all EVM-compatible blockchains. It is maintained by the Reth team at Paradigm and is the successor to ethers-rs. Alloy provides a modular, type-safe, and performant toolkit for every layer of blockchain interaction: providers, signers, contracts, transactions, and network primitives.
Crate Architecture
| Crate | Purpose |
|---|---|
alloy |
Meta-crate that re-exports everything — start here for simple projects |
alloy-provider |
RPC providers (HTTP, WS, IPC) with layered middleware |
alloy-signer-* |
Wallet/signer implementations (local, Ledger, Trezor, AWS, GCP, YubiKey) |
alloy-network |
Network abstractions (Ethereum, custom chains) |
alloy-primitives |
Core types: Address, U256, Bytes, FixedBytes, B256 |
alloy-sol-types |
Solidity type system: ABI encode/decode, the sol! macro |
alloy-contract |
High-level contract interaction: deploy, call, events |
alloy-transport |
Transport layer: HTTP, WS, IPC connections |
alloy-consensus |
Transaction types and consensus logic |
alloy-json-rpc |
JSON-RPC type definitions |
alloy-node-bindings |
Local node management: Anvil, Geth, Reth |
alloy-chains |
Chain definitions and ID mappings |
Quick Start: Minimal Dependency Setup
Add the meta-crate for the fastest setup — it re-exports the most commonly needed items:
# Cargo.toml
[dependencies]
alloy = { version = "2.0", features = ["full"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
eyre = "0.6"
For finer-grained control, use individual crates:
[dependencies]
alloy-primitives = "2.0"
alloy-sol-types = "2.0"
alloy-provider = { version = "2.0", features = ["reqwest"] }
alloy-signer-local = "2.0"
alloy-consensus = "2.0"
alloy-contract = "2.0"
alloy-network = { version = "2.0", features = ["ethereum"] }
alloy-transport = { version = "2.0", features = ["http", "ws"] }
Quick Reference: Which Guide Do I Need?
The user's task determines which reference file to read:
- Signing messages & creating digital signatures -> Read
references/signatures-wallets.md - Sending transactions (ETH & ERC-20 tokens) -> Read
references/transactions-payments.md - Verifying incoming payments & monitoring transfers -> Read
references/payment-verification.md - Setting up providers (HTTP/WS/IPC) & middleware layers -> Read
references/providers-networking.md - Deploying & calling smart contracts, sol! macro -> Read
references/contracts-abi.md - Subscribing to events, blocks, logs -> Read
references/subscriptions-events.md - Address, U256, Bytes, hashing utilities -> Read
references/primitives-types.md - Anvil, Geth, Reth local node integration -> Read
references/node-bindings.md - Cargo setup, feature flags, ethers-rs migration -> Read
references/cargo-setup.md
Core Patterns at a Glance
1. Create a Provider and Send a Transaction
This is the most common starting pattern. Every blockchain interaction flows through a provider.
use alloy::network::EthereumWallet;
use alloy::primitives::address;
use alloy::providers::{Provider, ProviderBuilder};
use alloy::signers::local::PrivateKeySigner;
#[tokio::main]
async fn main() -> eyre::Result<()> {
// Set up signer from private key
let signer: PrivateKeySigner = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
.parse()?;
let wallet = EthereumWallet::from(signer.clone());
// Build provider with wallet (HTTP by default). Recommended fillers (gas, nonce, chain ID)
// are already enabled by ProviderBuilder::new().
let rpc_url = "https://eth.llamarpc.com".parse()?;
let provider = ProviderBuilder::new()
.wallet(wallet)
.connect_http(rpc_url);
// Get balance
let address = signer.address();
let balance = provider.get_balance(address).await?;
println!("Balance: {}", balance);
Ok(())
}
2. Transfer Native ETH
use alloy::primitives::{address, U256, Bytes};
use alloy::providers::{Provider, ProviderBuilder};
use alloy::rpc::types::TransactionRequest;
use alloy::network::TransactionBuilder;
use alloy::signers::local::PrivateKeySigner;
use alloy::network::EthereumWallet;
let signer: PrivateKeySigner = "0x...".parse()?;
let wallet = EthereumWallet::from(signer.clone());
let provider = ProviderBuilder::new()
.wallet(wallet)
.connect_http("https://eth.llamarpc.com".parse()?);
// Build and send the transaction
let tx = TransactionRequest::default()
.to(address!("0xRecipientAddress"))
.value(U256::from(1_000_000_000_000_000_000u128)) // 1 ETH (18 decimals)
.with_input(Bytes::new());
let pending = provider.send_transaction(tx).await?;
// Wait for confirmation and fetch receipt
let receipt = pending.get_receipt().await?;
println!("Status: {:?}", receipt.status());
3. Sign a Message (EIP-191 Personal Sign)
use alloy::signers::{Signer, local::PrivateKeySigner};
let signer: PrivateKeySigner = "0x...".parse()?;
let message = "Hello, Ethereum!";
// Sign with EIP-191 prefix (personal_sign)
let signature = signer.sign_message(message.as_bytes()).await?;
println!("Signature: 0x{}", signature);
// Verify
let recovered = signature.recover_address_from_msg(message)?;
assert_eq!(recovered, signer.address());
4. ERC-20 Token Transfer
use alloy::primitives::{address, U256};
use alloy::sol;
// Define the ERC-20 interface using sol! macro
sol! {
#[sol(rpc)]
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function decimals() external view returns (uint8);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
}
}
let token_address = address!("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"); // USDC on mainnet
let token = IERC20::new(token_address, &provider);
// Check balance
let balance = token.balanceOf(signer.address()).call().await?;
println!("USDC balance: {}", balance);
// Check decimals
let decimals = token.decimals().call().await?;
println!("USDC decimals: {}", decimals);
// Transfer 100 USDC
let tx_hash = token.transfer(address!("0xRecipient"), U256::from(100_000_000u128))
.send()
.await?
.watch()
.await?;
5. Deploy a Contract
sol! {
#[sol(abi)]
contract MyContract {
constructor(uint256 initial_value);
function value() external view returns (uint256);
}
}
// Get the compiled bytecode (from forge build or solc)
let bytecode = hex::decode("6080604052...")?;
let deploy_tx = MyContract::deploy_builder(&provider, U256::from(42))
.deploy()
.await?;
let contract_address = deploy_tx.address();
println!("Deployed to: {}", contract_address);
// Interact
let contract = MyContract::new(contract_address, &provider);
let value = contract.value().call().await?;
Key Concepts
Provider Layer Stack
Providers are built in layers. Each layer adds functionality (middleware pattern):
Your code
|
SignerLayer (adds wallet/signer for signing)
|
GasFiller (auto-estimates gas price)
|
NonceFiller (manages nonce sequence)
|
ChainIdFiller (auto-fills chain ID)
|
Transport (HTTP / WS / IPC)
|
RPC Node
Use ProviderBuilder::new() — it comes with all recommended fillers (gas, nonce, chain ID) pre-configured. The wallet layer is added on top so it signs after all fillers have populated their fields.
Supported Transaction Types
| Type | EIP | Key Feature |
|---|---|---|
| Legacy | - | Gas price only |
| EIP-1559 | 1559 | Base fee + priority fee, gas tip market |
| EIP-4844 | 4844 | Blob transactions (data availability) |
| EIP-7702 | 7702 | Account abstraction via delegation |
| EIP-7594 | 7594 | PeerDAS (peer data availability sampling) |
| Private | - | Private transaction pools (mev-share, flashbots) |
Signer Types
| Signer | Crate | Use Case |
|---|---|---|
PrivateKeySigner |
alloy-signer-local |
Local private key, testing, scripts |
MnemonicSigner |
alloy-signer-local |
HD wallet from BIP-39 mnemonic phrase |
LedgerSigner |
alloy-signer-ledger |
Ledger hardware wallet |
TrezorSigner |
alloy-signer-trezor |
Trezor hardware wallet |
AwsSigner |
alloy-signer-aws |
AWS KMS signing |
GcpSigner |
alloy-signer-gcp |
Google Cloud KMS signing |
YubiSigner |
alloy-signer-yubihsm |
YubiKey HSM signing |
All signers implement the Signer trait, so they are interchangeable — swap a PrivateKeySigner for a LedgerSigner without changing any other code.
The sol! Macro
The sol! macro generates type-safe Rust bindings from Solidity source. This is the preferred way to define contract interfaces:
sol! {
#[sol(rpc)] // generates both ABI bindings AND an RPC contract struct
interface IERC20 {
function balanceOf(address who) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
}
Key attributes:
#[sol(rpc)]— generates an RPC-ready contract struct#[sol(abi)]— generates only ABI types (no RPC, for deploy)#[derive(Debug, PartialEq)]— standard Rust derives on generated types
Common Pitfalls
-
Missing fillers —
ProviderBuilder::new()already includes recommended fillers by default in Alloy 2.0. Use.disable_recommended_fillers()if you need to opt out and manage gas, nonce, and chain ID manually. -
Decimals confusion — ERC-20 tokens use different decimals (USDC = 6, WETH = 18). Always check with
decimals()call or useparse_units/format_unitsutilities. -
Chain ID mismatches — The
ChainIdFillerhandles this automatically. If you skip it, transactions will be rejected. Never hardcode chain IDs. -
Blocking in async context — All provider methods are async. Hardware signers may block briefly. Use
tokio::task::spawn_blockingif needed. -
Integer overflow — Always use
U256for on-chain values, not native Rust integers. TheU256::from()constructor acceptsu64oru128.
Reference Files
For detailed information on any topic, read the appropriate reference file:
references/signatures-wallets.md— All signer types, EIP-191/EIP-712 signing, message verification, keystore creation, permit signatures, hardware wallet setupreferences/transactions-payments.md— EIP-1559/legacy/4844/7702 transactions, ETH transfers, ERC-20 transfers, permit2 flows, gas estimation, private transactions, transaction lifecyclereferences/payment-verification.md— Verifying incoming payments, monitoring transaction receipts, parsing transfer events, filtering logs, building payment listeners, reconciliation patternsreferences/providers-networking.md— HTTP/WS/IPC providers, provider builder, layers (retry, fallback, logging, delay), batched RPC, embedded consensus, authenticated connections, dynamic providersreferences/contracts-abi.md— sol! macro deep dive, contract deployment from artifact/bytecode, static vs dynamic ABI, JSON ABI loading, revert decoding, multi-call batching, library linkingreferences/subscriptions-events.md— Block subscriptions, log filtering, pending transaction streams, event multiplexer, poll-based log watching, ENS resolutionreferences/primitives-types.md— Address, B256, Bytes, FixedBytes, U256/U128, Uint/Bint, parse_units/format_units, keccak256, ecrecover, conversion between typesreferences/node-bindings.md— Anvil (launch, fork, set storage), Geth integration, Reth local instances, foundry test helpersreferences/cargo-setup.md— Feature flags table, minimal vs full crate selection, ethers-rs migration guide, type conversion reference, Cargo.toml patterns per use case