Skip to content

Alloy: Fast, battle-tested and well-documented building blocks for Ethereum, in Rust

Compare
Choose a tag to compare
@gakonst gakonst released this 23 Jun 18:18
· 511 commits to main since this release
e1fbe23

image

We’re excited to announce alloy-rs/core, a rewrite of the popular ethers-core package and the ethabi crate. Core offers:

  • Alloy Primitives: We provide new fundamental types for hashes, fixed-byte arrays, and signed and unsigned integers (built on Remco’s uint library). These types implement common traits sanely (no more truncated hashes like “0x1234…5678”), aim for no_std compatibility, and provide common helper functions for usage in applications.
  • Alloy RLP: A canonical RLP implementation.
  • Alloy RPC Types: All the RPC types when talking to a chain are available using our newly defined types.
  • Syn Solidity: A syn-powered Solidtiy 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).
  • Alloy Solidity Types: A compile-time representation of Ethereum's type system with ABI and EIP-712 support. This allows for clean UX and high encoding/decoding speed without redundant layers of abstraction. This static abi encoder is built on a robust representation of Solidity’s type system in Rust. Users access it via the sol! procedural macro, which parses Solidity snippets to generate native Rust code. The static encoder benches at 2-3x faster (!!) than current Rust implementations which are 1+ order of magnitude faster than other languages.

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](https://crates.
io/crates/alloy-primitives), and docs.rs.

PRs included in this release:

New Contributors

Full Changelog: https://github.com/alloy-rs/core/commits/v0.2.0

(This was going to be a v0.1.0 release, but we did an internal one first)