Cointime

Download App
iOS & Android

Releasing Alloy

From paradigm by Georgios Konstantopoulos, James Prestwich, Matthias Seitz, DaniPopes, Yash Atreya, Zerosnacks, Enrique Ortiz

Contents

The story so far

One year ago, we announced Alloy, our full rewrite of the low-level building blocks for interacting with EVM blockchains, from the RPC types, down to the ABI encoders and primitive types.

Today, we’re releasing Alloy 0.1 (crates), the first release of the Alloy project including all the necessary utilities for interacting with an EVM-based blockchain. This release includes multiple API improvements and new features which we are excited to finally share with our users. As part of this release, we’re also officially stopping maintenance of ethers-rs, and we’re encouraging everyone to move to Alloy.

While we are only now releasing Alloy as the top-level package including all JSON-RPC functionality, the packages inside of Alloy Core with low-level type & ABI coders have been released for a few months now, and many of them have >1 million all time downloads already!

To help users get up to speed with Alloy, we’re publishing the Alloy Book (including an ethers to alloy migration guide), the Alloy and Alloy Core docs, as well as a lot of examples showing all sorts of ways you can consume Alloy.

Alloy is already integrated in many Rust codebases, as well as all the projects we’re working on: RevmFoundry and Reth. We’ve been using it in production ourselves for months, and we’re excited for it to be a performant and stable foundation for the Rust Ethereum ecosystem, as we envisioned in our original announcement.

With that out of the way, let’s dive in!

What are the highlights of the Alloy release?

Alloy is written from the bottom-up to be performant, safe to use, and intuitive. We took our learnings from 4 years of ethers-rs development and put them all into designing a great client-side library for interacting with EVM-based blockchains.

This took a lot of time, 1 year since we announced the Alloy project, but we’re proud of the result.

Today we are excited to reveal some exciting new features co-architected and co-developed by our core team alongside James Prestwich who’s been maintaining Alloy with the rest of us over the last year. Big shoutout & thank you to James for pushing us to go beyond what ethers-rs allowed us before, and for being a core input to the successful delivery of today’s release.

Reworked Provider Middleware architecture

The most important abstraction in a RPC client library is the Provider, which lets you interact with the network. Users commonly want to extend basic provider operations: different gas estimations, gas escalators, alternative signing pipelines, and more. You want to do this in a modular way which empowers the developer to extend the behavior of the core library, instead of having to modify it.

In ethers-rs, we had defined the Middleware as the one-stop shop for modifying your RPC client’s behavior. If you wanted nonce management, it’s a middleware. Gas escalation, it’s a middleware. Signing transactions? Middleware. Flashbots bundles? Middleware. This approach worked because it gave us a lot of flexibility to override things as we wanted, but it also was too error prone, and had poor developer experience (e.g. expecting devs to panic when a function that’s required by the Middleware trait does not make sense to implement).

Alloy redesigned that abstraction from the ground up. We split the Middleware in 3 overridable abstractions:

  • Transport Layers: The Transport is the “wire” that carries over your data to the node. This now implement’s Tower’s Service Layers which allows tapping into the Tower Ecosystem for common middleware such as request retries, rate limits and more.
  • Provider Layers: Modeled after Tower’s Layers this allows overriding various Provider functionalities, e.g. if you wanted to hijack the get_storage_at method, you’d implement a ProviderLayer.
  • Fillers: This is probably the most exciting abstraction, Fillers handle everything about the transaction’s lifecycle. A user generally submits a partially populated transaction, and the provider is responsible for figuring out what data is missing and filling it in. Fillers can be installed independent of order from each other, solving a major footgun from ethers-rs. We provide the .with_recommended_fillers() method which aggregates commonly used Fillers via multiple JoinFill ProviderLayers for convenience:Wallet Filler: Signs a transaction with a credential from a wide suite: a local private key parsed from a hex string, a 12/24-word mnemonic, a keystore file, a hardware wallet like Trezor or Ledger, or even from a productionized key management system such as YubiHSM, AWS KMS or GCP KMS.Nonce Filler: Automatically manages nonces across all accounts.Gas Filler: Handles calculating the gas price and the gas limit for each transaction.ChainId Filler: Embeds the correct chain ID into transactions depending on which chain is connected.
  • Wallet Filler: Signs a transaction with a credential from a wide suite: a local private key parsed from a hex string, a 12/24-word mnemonic, a keystore file, a hardware wallet like Trezor or Ledger, or even from a productionized key management system such as YubiHSM, AWS KMS or GCP KMS.
  • Nonce Filler: Automatically manages nonces across all accounts.
  • Gas Filler: Handles calculating the gas price and the gas limit for each transaction.
  • ChainId Filler: Embeds the correct chain ID into transactions depending on which chain is connected.

Putting it all together, the stack of features we have can be seen below:

This stack is bundled and exposed to the user via the ProviderBuilder which has a very ergonomic API:

// Create a signer from a random private key. let signer = PrivateKeySigner::random(); let provider = ProviderBuilder::new() // configures all the fillers .with_recommended_fillers() // sets the signer, allows configuring more than 1 signer which will be picked based on your transaction's `from` field. .wallet(EthereumWallet::from(signer)) // connects to the chain // can also use `.on_http`, `.on_ws`, or `.on_ipc` for no dyn dispatch // can also use `.on_anvil` for local testing // can also use `.on_client` for configuring auth options e.g. bearer auth. .on_builtin("ws://localhost:8545") .await?; let tx = TransactionRequest::new().with_to(...).with_value(...); let receipt = provider.send_transaction(tx).await?.get_receipt().await?; // do something with the receipt

Oh, we also made sure the Provider and Signers are object-safe, to make it easier to avoid generics in your code by Boxing them. For more on how to consume providers, see the book.

RPC Types Abstraction

The world is going multichain, and that means more differences in RPC types! That was one of the most painful things in ethers-rs where we supported e.g. Celo-related fields with a feature flag. This meant that if you wanted to have a type-safe connection to both Celo and Ethereum you had to choose between importing the library twice, or expecting that the Celo fields would be None in all cases in Ethereum. This is a problem that we set out to fix.

In Alloy, we define the Network trait which defines the “shape” of every network for all its RPC requests and responses where each type must implement certain traits, e.g. Eip2718EnvelopeTxReceiptTransactionBuilder, summarized below:

pub trait Network { /// The network transaction type enum. type TxType /// The network transaction envelope type. type TxEnvelope: Eip2718Envelope + Debug; /// An enum over the various transaction types. type UnsignedTx: From<Self::TxEnvelope>; /// The network receipt envelope type. type ReceiptEnvelope: Eip2718Envelope + TxReceipt; /// The network header type. type Header; /// The JSON body of a transaction request. type TransactionRequest: RpcObject + TransactionBuilder<Self> + Debug + From<Self::TxEnvelope> + From<Self::UnsignedTx>; /// The JSON body of a transaction response. type TransactionResponse: RpcObject + TransactionResponse; /// The JSON body of a transaction receipt. type ReceiptResponse: RpcObject + ReceiptResponse; /// The JSON body of a header response. type HeaderResponse: RpcObject; }

This allows us to import the library once without any feature flags, and depending on what network we specify we get different type abstractions. This is great! Type-safety, without redundant overhead! This is also the approach we’re taking in Reth for allowing any developer to build a chain with custom RPC types on the server side, such as a network with native account abstraction.

We provide two network implementations Ethereum and AnyNetwork. Ethereum contains all the types you know and love. AnyNetwork however, wraps every RPC type with the WithOtherFields<T> type which acts as a catch-all for any RPC response fields that do not match the Ethereum structure.

A developer can choose their Network implementation using the .network::() method on the ProviderBuilder. This allows us to support more networks than just Ethereum in a principled way, without burdening the core maintenance process. All you need to do is implement the network trait and all its associated types, import it in your code, and you’re done!

Follow our work on defining the OpStackNetwork in the op-alloy crate, and reach out if you want to implement your own network!

The sol! Macro

We first talked about the sol macro in our initial post. It is a rework of our previous abigen macro, which was used to generate type-safe bindings to a contract’s ABI. The sol macro is not a compiler, but it is a complete representation of the Solidity type system in Rust, which means you can just paste Solidity in it, and it’ll codegen bindings for it, even allowing support for custom types!

The sol macro also codegens JSON RPC bindings via the #[sol(rpc)] attribute. A deployer method is also generated if you pass it the #[sol(bytecode = "...")] attribute. For example, the code below would generate a Counter::deploy function as well as a Counter::increment(&self) method which you can use to increment the counter.

sol! { // solc v0.8.26; solc a.sol --via-ir --optimize --bin #[sol(rpc, bytecode="608080...")] contract Counter { uint256 public number; function increment() public { number++; } } }

To learn more about the sol macro, check the page on the book and its detailed documentation. Interacting with a smart contract is similar to ethers-rs (note, no more Arcs!), with minor underlying changes in the API for fetching a transaction’s receipt.

let provider = ProviderBuilder::new().on_builtin("...").await?; // Deploy the contract. let contract = Counter::deploy(&provider).await?; println!("Deployed contract at address: {}", contract.address()); let receipt = contract.setNumber(U256::from(42)).send().await?.get_receipt().await?; println!("Receipt: {receipt}"); // Increment the number to 43 (without waiting for the receipt) let tx_hash = contract.increment().send().await?.watch().await?; println!("Incremented number: {tx_hash}"); // Retrieve the number, which should be 43. let number = contract.number().await?.number.to_string(); println!("Retrieved number: {number}");

The sol macro’s functionality is also integrated with Foundry in forge bind for generating type-safe bindings for all your client-side integrations. Check out the updated Foundry Rust template if that’s of interest to you!

Extensive documentation and tests

We want our users to be equipped with high-level tutorials & examples for consuming the project, as a library. To achieve that, we provide a large surface area of documentation:

  • Alloy Docs: Rustdoc documentation for each function on the Alloy repository.
  • Alloy Core Docs: Rustdoc documentation for each function on the Alloy Core repository.
  • Alloy Examples: Wide range of code examples on how to consume Alloy in your day to day.
  • Alloy Book: Tutorials and long form writeups about all things Alloy.

Making the docs excellent is a top priority for us, please open issues on the book with more tutorials you’d like to see.

What is next for Alloy?

Today’s 0.1 release marks Alloy at feature parity with ethers-rs and beyond, while also being performant, well-tested and well-documented. We think Alloy is the tool of choice for power users, yet it is simple and intuitive enough for anyone to work with.

Our next priority is the 1.0 release, which means we’ll be polishing our APIs and working towards proper stability. While we don’t offer any formal stability guarantees, most of the APIs are baked, and we do not expect large changes.

To help achieve Alloy’s long term success, we’re looking to add 1 full-time staff-level engineer to the Alloy team who will help drive the day to day of the project, as well as help us grow the contributor base. If the above sounds interesting, please reach out to [email protected].

We’re excited for every ethers-rs user to port their code to use Alloy, as well as new services to be built with it! Go read the docs of alloy-core and alloy, the examples, and the book!

Until then, see you on Github!

Comments

All Comments

Recommended for you

  • SpaceX Shares Drop 10% Intraday

    On June 22, SpaceX (SPCX.O) shares fell for the third consecutive trading day, with intraday losses widening to 10%.

  • BitMine Increases ETH Holdings by Over 52,000, Total Holdings Exceed 5.67 Million ETH

    As of June 21, Eastern Time, BitMine's total cryptocurrency and cash holdings, including the 'Moon Landing Project', amount to $10.7 billion. BitMine holds 5,672,956 ETH (an increase of 52,202 ETH from last week), which represents 4.7% of the total Ethereum supply of 120.7 million ETH. Additionally, BitMine holds 205 BTC, shares in Beast Industries worth $180 million, $104 million in Eightco Holdings (NASDAQ: ORBS), and $601 million in unsecured cash. As of June 21, 2026, the total amount of ETH staked by BitMine is 4,718,677 ETH, valued at $8.2 billion based on an ETH price of $1,733.

  • BTC Surpasses $65,000

    Market data shows that BTC has surpassed $65,000, currently priced at $65,013.22, with a 24-hour increase of 1.59%. The market is highly volatile, so please ensure proper risk management.

  • Intercontinental Exchange ICE and OKX Establish Joint Venture OKXICE

    On June 22, the Intercontinental Exchange (ICE), the parent company of the New York Stock Exchange, announced the establishment of a joint venture named OKXICE with OKX in the cryptocurrency sector, with both parties holding a 50% stake. The specific business scope and operational details of the joint venture have not yet been disclosed. ICE previously operated the cryptocurrency futures platform Bakkt, and this collaboration with OKX may further strengthen its strategic positioning in the digital asset space.

  • SpaceX Begins Issuing Unsecured Senior Notes

    On June 22, according to filings with the U.S. Securities and Exchange Commission, SpaceX (SPCX.O) has started issuing unsecured senior notes, which will be used to repay outstanding borrowings. SpaceX (SPCX.O) will offer the notes to qualified institutional buyers and non-U.S. persons.

  • Strategy Acquires 520 Bitcoins for $34.9 Million

    Strategy acquired 520 bitcoins for a total price of $34.9 million last week.

  • Crypto Trading Company Fomo Completes $75 Million Series B Financing at $550 Million Valuation

    On June 22, crypto trading startup Fomo announced the completion of a $75 million Series B financing round, led by Index Ventures, with participation from Union Square Ventures, Zynga co-founder Mark Pincus, Discord CEO Humam Sakhnini, and Eventbrite co-founder Kevin Hartz, bringing the company's valuation to $550 million. Fomo was founded in 2025 by former dYdX team members Paul Erlanger, Se Yong Park, and Prashan Dharmasena.

  • Iran Central Bank Begins Efforts to Unfreeze Frozen Assets

    On the 22nd local time, Iranian Finance Minister Madani Zadeh stated that the Central Bank of Iran has begun taking necessary measures to promote the unfreezing of Iran's frozen assets, although "specific details and amounts are unclear." The Governor of the Central Bank, Abdolnaser Hemmati, mentioned that the recent talks in Switzerland were "intense and challenging," but the final outcome has progressed "largely according to the goals set by the Iranian delegation." Hemmati expressed satisfaction with the significant progress made regarding the issue of Iran's frozen assets. In the coming days, these funds will be gradually and systematically activated under the decisions and directives of the Central Bank of Iran. (CCTV)

  • Bank of America: Fed Expected to Raise Rates by 25 Basis Points in September, October, and December 2026

    On June 22, Bank of America stated that it expects the Federal Reserve to raise interest rates by 25 basis points in September, October, and December 2026, revising its previous forecast that rates would remain unchanged this year.

  • Nomura: The Fed's June Meeting May Mark a Key Turning Point for the Credit Cycle and AI Boom

    On June 22, Nomura Securities stated that the Federal Reserve's June meeting may not be an ordinary pause but could be viewed as a critical turning point marking the end of the credit cycle and the AI boom a decade from now. Naka Matsuzawa, Nomura's Chief Macro Strategist, pointed out in a recent report that the market has overestimated the urgency of immediate rate hikes by the Fed this year while severely underestimating the depth and persistence of the long-term rate hike path, showing a lack of caution towards real medium- to long-term risks. The firm anticipates that the Fed will likely remain on hold throughout 2026, and once the policy stance of Waller becomes clearer, along with inflation expectations being confirmed during a decline in oil prices, the current market pricing of about 1.5 rate hikes is expected to be swiftly adjusted. However, Matsuzawa emphasized that the real risk does not lie in whether the Fed raises rates once or twice this year, but rather in how likely these preventive measures could evolve into a complete and sustained tightening cycle.