alloy-rs/core

repository·main·Indexed 21 days ago

https://github.com/alloy-rs/core

A high-performance rewrite of ethers-rs providing core primitives, ABI handling, and Solidity integration for the Rust Ethereum ecosystem. Includes crates for shared Ethereum types (alloy-primitives), static and dynamic Solidity type encoding/decoding (alloy-sol-types, alloy-dyn-abi), JSON ABI parsing (alloy-json-abi), and the sol! macro for generating Rust types from Solidity source.

Tokens
70K
Snippets
203
Records
319
Agent score
75%

What's inside alloy-rs-core

  1. Overview of Alloy crates

    main

    Alloy is a high-performance rewrite of ethers-rs for the Rust Ethereum ecosystem. The repository is organized into several specialized crates:

    • alloy-core: The meta-crate for the entire project.
    • alloy-primitives: Provides primitive integer and byte types.
    • alloy-sol-types: Provides compile-time ABI and EIP-712 implementations.
    • alloy-sol-macro: Contains the sol! procedural macro for Solidity integration.
    • alloy-dyn-abi: Provides run-time ABI and EIP-712 implementations.
    • alloy-json-abi: A full implementation of the Ethereum JSON-ABI.
    • alloy-sol-type-parser: A simple parser for Solidity type strings.
    • syn-solidity: A Solidity parser powered by syn.
  2. Use the `sol!` macro to generate Rust types from Solidity

    main

    The alloy-sol-macro crate provides the sol! procedural macro. This macro allows you to write Solidity syntax directly within your Rust code. It parses the Solidity code and automatically generates corresponding Rust types that implement the alloy-sol-types traits, enabling seamless interaction between Rust and Ethereum smart contracts.

    // Example usage (refer to macro documentation for specific syntax)
    // sol! {
    //     contract MyContract {
    //         function doSomething(uint256 x) external returns (bool);
    //     }
    // }
  3. Use alloy-sol-types for Solidity type modeling and ABI encoding

    main

    alloy-sol-types provides tools to express Solidity types in Rust and encode/decode them into ABI blobs for smart contract interaction.

    It uses the SolType trait to map Solidity types to their corresponding Rust types via SolType::RustType.

    Key Capabilities:

    • Type Modeling: Represent Solidity types (like bool[2]) as native Rust types.
    • ABI Encoding/Decoding: Convert Rust data to ABI blobs and vice versa.
    • EIP-712 Support: Provides signing support for structured data via the SolStruct trait.
    • User-defined Value Types: Support for Solidity's user-defined value types (UDTs) via wrapper types.
    use alloy_sol_types::{sol_data::*, SolType, SolValue};
    
    // Represent a Solidity type in rust
    type MySolType = FixedArray<Bool, 2>;
    
    let data = [true, false];
    
    // SolTypes expose their Solidity name
    assert_eq!(MySolType::SOL_NAME, "bool[2]");
    
    // Transform Rust into ABI blobs
    let encoded: Vec<u8> = MySolType::abi_encode(&data);
    let decoded: [bool; 2] = MySolType::abi_decode(&encoded).unwrap();
    assert_eq!(data, decoded);
  4. Use alloy-dyn-abi for dynamic Solidity type encoding and decoding

    main

    The alloy-dyn-abi crate provides a runtime representation of Ethereum's type system. Use this library when Solidity types are not known at compile time, such as when implementing EIP-712 signing interfaces.

    Warning: This dynamic approach is significantly more expensive and error-prone than the static encoder/decoder provided by alloy-sol-types. Use the static encoder whenever possible. The dynamic version does not enforce the mapping between Solidity and Rust types at compile time.

    use alloy_dyn_abi::{DynSolType, DynSolValue};
    use alloy_primitives::hex;
    
    // parse a type from a string
    let my_type: DynSolType = "uint16[2][]".parse().unwrap();
    
    // decode
    let my_data = hex!(
        "0000000000000000000000000000000000000000000000000000000000000020" // offset
        "0000000000000000000000000000000000000000000000000000000000000001" // length
        "0000000000000000000000000000000000000000000000000000000000000002" // .[0][0]
        "0000000000000000000000000000000000000000000000000000000000000003" // .[0][1]
    );
    let decoded = my_type.abi_decode(&my_data)?;
    
    let expected = DynSolValue::Array(vec![DynSolValue::FixedArray(vec![2u16.into(), 3u16.into()])]);
    assert_eq!(decoded, expected);
    
    // roundtrip
    let encoded = decoded.abi_encode();
    assert_eq!(encoded, my_data);
  5. What is alloy-sol-macro-expander?

    main

    The alloy-sol-macro-expander crate provides the core logic for expanding a Solidity proc_macro2::TokenStream. It is designed to be used as a library crate to expand Solidity source code and generate corresponding Rust bindings.

    Note that this is not the procedural macro crate itself; instead, it is the underlying engine used by the [sol!] macro in the [alloy-sol-macro] crate to perform the actual expansion work.

  6. What is DynToken::decode_populate and when to use it?

    main

    Because the shape of data is only known at runtime, alloy-dyn-abi cannot perform compile-time memory allocation for decoded data.

    DynToken::decode_populate allows you to pre-allocate a DynToken with the expected shape (containing empty values) and then populate those empty values with decoded data.

    Note: Direct use of DynToken is not recommended. Instead, use the abi_decode and abi_encode methods provided on DynSolType for a more ergonomic experience.

  7. Understand Tokenization and Encoding in `alloy-sol-types`

    main

    The library uses a two-step process for handling ABI data:

    1. Tokenization/Detokenization: Converting between a Rust type and an ABI Token.

      • Use SolType::tokenize() to go from Rust $\rightarrow$ Token.
      • Use SolType::detokenize() to go from Token $\rightarrow$ Rust.
      • Note: This is primarily for power users implementing custom SolTypes.
    2. Encoding/Decoding: Converting between a Token and a serialized ABI blob (bytes).

      • Recommendation: Most users should avoid interacting with Token directly. Instead, use the SolType or SolValue methods (like abi_encode and abi_decode) to operate directly on Rust types.
  8. Identify limitations of the syn-solidity parser

    main

    Because syn-solidity is limited to valid Rust tokens, certain Solidity constructs are not supported.

    Unsupported Constructs:

    • Identifiers with dollar signs: $ is not allowed inside identifiers.
    • Single quote strings: Only double-quoted strings are supported.
    • Literal prefixes: hex and unicode string literal prefixes are not supported (as they are reserved in Rust 2021 edition and above).
    • Unicode escapes: Uses Rust's "\u{XXXX}" format instead of Solidity's "\uXXXX".
    • Nested block comments: Invalid nested block comments like /*/*/ will fail to parse.
  9. How dynamic encoding and decoding works

    main

    The dynamic system relies on three core enums that represent the Solidity type system at runtime:

    1. DynSolType: Represents a Solidity type (equivalent to an enum over types implementing the SolType trait). This is the primary entry point for decoding.
    2. DynSolValue: Represents the Rust-side shape of a decoded Solidity value. Users must manually convert these values to their own Rust types using From implementations or fallible casts.
    3. DynToken: Represents an ABI token (equivalent to types implementing alloy_sol_types::abi::Token).

    The Detokenizing Process: DynSolType + DynToken = DynSolValue.

    Unlike static encoding, which uses the Rust type system to define expectations, the dynamic encoder requires an instance of DynSolType to be provided at runtime to guide the decoding process.

  10. Understand the scope and limitations of alloy-sol-type-parser

    main

    The alloy-sol-type-parser is a lightweight tool specifically designed for parsing type strings found in ecosystem tooling (like JSON ABIs). It is not a full Solidity parser.

    Use this crate for:

    • Syntax-checking JSON ABI files.
    • Providing input to alloy-dyn-abi.
    • Porting ethers.js code to Rust.

    Do NOT use this crate for:

    • Parsing Solidity source code.
    • Generating Rust code from Solidity source code.
    • Generating Solidity source code from Rust code.

    For full Solidity syntax parsing and code generation, use syn-solidity instead.

  11. Use unsigned and signed integers

    main

    Integer types are provided for common Ethereum requirements:

    • Unsigned integers: Re-exported from ruint (e.g., U256).
    • Signed integers: Provided as wrappers around ruint integers (e.g., I256).

    These types support standard parsing from strings and arithmetic operations.

    use alloy_primitives::{I256, U256};
    
    // Unsigned integer usage
    let mut n: U256 = "42".parse().unwrap();
    n += U256::from(10);
    assert_eq!(n.to_string(), "52");
    
    // Signed integer usage
    let mut n: I256 = "-42".parse().unwrap();
    n = -n;
    assert_eq!(n.to_string(), "42");