In 2020, we saw a shortfall in the performance and reliability of off-chain Ethereum infrastructure. To address this, we (gakonst & prestwich) started building in the Rust Ethereum ecosystem.
At the time, most Ethereum off-chain actors were written in Python or Javascript. These actors included MEV bots for liquidation and arbitrage (sandwiches were not that popular back then) and indexers for ETLing Ethereum events into structured data. Most codebases (open source or not) were at best PoC-level mature and could not provide the scale or reliability that crypto deserves.
We’ve come a long way since then. Using Parity’s Ethereum Types and Ethabi, we built ethers-rs, a Rust re-interpretation of the popular ethers.js library. Today, ethers-rs is the premier Rust tooling for the entire EVM ecosystem. It has a core team of dedicated maintainers, over 200 contributors, and covers everything from RPC to the Solidity compiler.
Ethers-rs is now is a staple in Rust Ethereum codebases, used by:
For the last 3 years, ethers-rs has provided strongly-typed, well-documented, tested and efficient abstractions for submitting transactions, subscribing and transforming chain data, and interacting with smart contracts. The Rust Ethereum community flourished over this time period, and we are proud of the work we’ve done to enable that. Ethers-rs far outgrew our expectations.
However, like all software, maintenance is a constant battle. Ethers-rs was our first foray into Rust in production, and it has been a learning experience for everyone involved. Over time, we merged code we should have been more thoughtful about, accumulated tech debt, and entrenched suboptimal abstractions in many codebases due to our mistakes.
We recently started a concentrated effort to address these growing pains. In this post we will share our progress so far, as well as our plans for the Rust Ethereum ecosystem for the rest of this year.
Firstly, we are rebranding.
Ethers is now called Alloy and lives under a new Github Organization: github.com/alloy-rs. Current versions of ethers will continue to live in gakonst/ethers-rs, and be otherwise in maintenance mode for v2. Future major versions will use the new name and organization.
Ethers-rs was built for stability, to be used as a well-tested and robust foundation of composable pieces to build powerful applications. We believe that Alloy communicates that well.
The project has grown enough that we felt the need to have a unique identity, which communicates our values, and allows us to be consistent about them, while also avoiding brand confusion with ricmoo’s popular ethers.js, which we’re thankful for the inspiration in the early iterations of ethers-rs.
We also rewrote our stack from scratch. Today, we’re excited to announce alloy-rs/core, a rewrite of the popular ethers-core package and the ethabi crate. Core offers:
syn
-powered Solidity parser, specifically designed for Rust procedural macros. It aims to mimic the behavior of the official Solidity compiler (Solc) when it comes to parsing valid Solidity code. This is a powerful abstraction which we leverage for building performant compile-time functionalities, like static ABI coders (see below).Here is a low-level example of how you can use these new primitives:
use alloy_primitives::Address; use alloy_sol_types::{sol, SolType}; // Type definition: generates a new struct that implements `SolType` sol! { type MyType is uint256; } // Type aliases type B32 = sol! { bytes32 }; // This is equivalent to the following: // type B32 = alloy_sol_types::sol_data::Bytes<32>; type SolArrayOf<T> = sol! { T[] }; type SolTuple = sol! { tuple(address, bytes, string) }; let _ = <sol!(bool)>::encode_single(&true); let _ = B32::encode_single(&[0; 32]); let _ = SolArrayOf::<sol!(bool)>::encode_single(&vec![true, false]); let _ = SolTuple::encode_single(&(Address::ZERO, vec![0; 32], "hello".to_string()));
Here's a higher-level example, showing a roundtrip ABI encoding of an ERC20 transfer:
use alloy_primitives::{Address, U256}; use alloy_sol_types::{sol, SolCall}; use hex_literal::hex; sol! { #[derive(Debug, PartialEq)] interface IERC20 { function transfer(address to, uint256 amount) external returns (bool); } } // random mainnet ERC20 transfer // https://etherscan.io/tx/0x947332ff624b5092fb92e8f02cdbb8a50314e861a4b39c29a286b3b75432165e let data = hex!( "a9059cbb" "0000000000000000000000008bc47be1e3abbaba182069c89d08a61fa6c2b292" "0000000000000000000000000000000000000000000000000000000253c51700" ); let expected = IERC20::transferCall { to: Address::from(hex!("8bc47be1e3abbaba182069c89d08a61fa6c2b292")), amount: U256::from(9995360000_u64), }; assert_eq!(data[..4], IERC20::transferCall::SELECTOR); let decoded = IERC20::IERC20Calls::decode(&data, true).unwrap(); assert_eq!(decoded, IERC20::IERC20Calls::transfer(expected)); assert_eq!(decoded.encode(), data);
Alloy is really powerful!
These crates will act as the solid foundation we’ve always wanted for Ethereum in Rust, informed by the lessons from our last 3 years of Rust Ethereum engineering. The code for these crates is under active development on github. We love new contributors. The pre-1.0.0 version of these crates are available on crates.io, and docs.rs.
In 2020 with ethers-rs, we made a decision to re-use Parity’s existing type libraries. These types are deeply embedded in the codebase and exposed to dependencies via U256, H256, and other commonly-used structs. Over time Rust has improved faster than Parity’s implementations could keep up.
Reth and Revm did not use Parity types, choosing modern Rust equivalents instead. Other Paradigm-supported projects like Foundry or Artemis have not migrated yet. Reth and revm are incompatible by default with Foundry and ethers-rs. This creates a gap in the ecosystem, requiring extreme amounts of type conversion at the boundary.
To address this, we will be migrating the Rust Ethereum ecosystem built on our libraries to shared types. These types will live in alloy-rs/core, and will reuse the excellent work in Remco’s ruint library. We will phase out the Parity types. The alloy-types library will be the root of the Rust Ethereum ecosystem. This migration will take place over the next 6 months.
Going forward, alloy-rs/core will form the shared base for all our Rust Ethereum projects.
To be clear, there is no action needed from developers right now. Ethers-rs will continue to be available under the same great name in the same great repo for the foreseeable future. We will publish migration plans in advance. Until we have more integrations in the rest of the ecosystem, we encourage developers to try out alloy in their low-level projects and share their feedback on the abstractions, documentation and performance.
Using our learnings from the last 3 years of building in the Rust Ethereum ecosystem, we are excited to release Alloy, our rewrite of ethers-rs with exciting new features, documentation, and a new brand.
As part of that, we are excited to be also formalizing maintainership of ethers-rs/alloy as James Prestwich, Georgios Konstantopoulos and DaniPopes.
These upgrades will pay down a lot of tech debt, remove weird type conversions, improve performance across the board, and overall set us up for the next 5 years of high-assurance Rust Ethereum engineering.
If you are excited about contributing, please reach out to georgios@paradigm.xyz or james@prestwi.ch. You can find links to all the support and development chatrooms in the Alloy Core README.
Copyright © 2024 Paradigm Operations LP All rights reserved. “Paradigm” is a trademark, and the triangular mobius symbol is a registered trademark of Paradigm Operations LP