#ethereum #sqlx #sql #database #u256

ethereum-mysql

Ethereum types (Address, U256) wrapper for seamless SQL database integration with SQLx

11 stable releases (3 major)

3.1.1 Sep 25, 2025
3.0.2 Jul 8, 2025
3.0.0 Jun 30, 2025
2.1.0 Jun 28, 2025
0.1.0 Jun 7, 2025

#6 in #u256

Download history 152/week @ 2025-06-20 379/week @ 2025-06-27 264/week @ 2025-07-04 47/week @ 2025-07-11 135/week @ 2025-09-19 141/week @ 2025-09-26 14/week @ 2025-10-03

290 downloads per month
Used in typed_sqlx_client

MIT/Apache

100KB
2K SLoC

ethereum-mysql

Crates.io Documentation License

Design Purpose:

ethereum-mysql is a pragmatic Rust library that provides type-safe, ergonomic wrappers for Ethereum types (Address, U256, FixedBytes, Bytes) for seamless integration with SQLx and relational databases (MySQL, PostgreSQL, SQLite). The crate is designed for real-world Web3/data engineering scenarios, focusing on:

  • String-based storage: All types are stored as human-readable hex strings (e.g. 0x...) in the database, ensuring maximum compatibility and easy inspection.
  • Type safety: Compile-time and runtime validation for all Ethereum types, eliminating manual string parsing and validation in business logic.
  • API ergonomics: Direct arithmetic, comparison, and conversion with Rust primitives for U256, and compile-time address macros for zero-cost, safe usage.
  • No binary mode: Binary column support is intentionally omitted; for binary storage, use the official alloy types directly or wait for native support in your stack.
  • Minimal, focused features: Only the most practical and widely-used Ethereum types and operations are supported, with optional serde integration.

Features

  • Multi-database support: MySQL, PostgreSQL, SQLite via SQLx
  • Zero-cost abstractions: Wraps alloy::primitives types (Address, U256, FixedBytes, Bytes)
  • Type-safe wrappers: SqlAddress, SqlU256, SqlFixedBytes, SqlBytes for direct DB integration
  • Arithmetic operations: Full arithmetic and comparison for SqlU256 (U256), including with Rust primitives
  • Type conversions: Seamless conversion between wrappers and Rust integer types
  • Compile-time macros: Create addresses and hashes at compile time with sqladdress!, sqlhash!
  • Constants: Pre-defined constants like SqlAddress::ZERO, SqlU256::ZERO, SqlU256::ETHER
  • Decimal utilities: Parse and format Ethereum amounts with parse_sether/format_sether
  • Serde support: Optional JSON serialization with serde
  • SQLx native: Implements sqlx_core::Type, sqlx_core::Encode, and sqlx_core::Decode for all wrappers
  • Pure Rust: No C dependencies, async-first, production ready
  • Common 32-byte types SqlHash and SqlTopicHash are exported as type aliases for SqlFixedBytes<32>

Database Column Types

All types are stored as strings (hex with 0x prefix) for maximum compatibility:

Type Recommended Column Type
SqlAddress VARCHAR(42)
SqlU256 VARCHAR(66)
SqlFixedBytes VARCHAR(2+2*N)
SqlBytes TEXT
  • For PostgreSQL, use TEXT for all string types.
  • For MySQL/SQLite, use VARCHAR as above.

U256 String Encoding

  • All U256/FixedBytes types are written as lowercase hex strings with 0x prefix (e.g. 0x1234...).
  • Reading supports both 0x-prefixed hex and decimal strings (but hex is recommended for consistency).
  • For predictable sorting/comparison, always store as hex.

Key Advantages

🛡️ Type Safety & Validation

All wrappers provide compile-time and runtime validation. No more manual string parsing or error-prone conversions in your business logic.

⚡ Zero Conversion Overhead

Directly bind and retrieve types from SQLx queries—no intermediate conversions or manual parsing required.

🔥 Intuitive Arithmetic

Direct arithmetic and comparison between SqlU256 and Rust primitives, e.g. balance * 25 / 10000.

🚀 API-Ready

Request structs can use SqlAddress/SqlU256 directly for automatic validation and type safety in web APIs.


Quick Start

Add to your Cargo.toml:

ethereum-mysql = "3.1.1"

Example Usage

Address Creation

use ethereum_mysql::{SqlAddress, sqladdress};
use std::str::FromStr;

let zero = SqlAddress::ZERO;
let addr = sqladdress!("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d");
let addr2 = SqlAddress::from_str("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d").unwrap();
const ADMIN: SqlAddress = sqladdress!("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d");

U256 Usage & Ethereum Constants

use ethereum_mysql::{SqlU256, utils::{parse_sether, format_sether}};
use std::str::FromStr;

// Basic usage
let small = SqlU256::from(42u64);
let large = SqlU256::from(u128::MAX);
let zero = SqlU256::ZERO;
let from_decimal = SqlU256::from_str("123456789").unwrap();
let from_hex = SqlU256::from_str("0x75bcd15").unwrap();
assert_eq!(from_decimal, from_hex);

// Ethereum amount calculations
let one_ether = SqlU256::ETHER;  // 10^18 wei
let half_ether = SqlU256::ETHER / 2;
let gas_price = SqlU256::from(20_000_000_000u64); // 20 gwei

// Decimal string parsing and formatting
let amount = parse_sether("1.5").unwrap(); // Parse 1.5 ETH
assert_eq!(format_sether(amount).unwrap(), "1.500000000000000000");

// Arithmetic operations
let a = SqlU256::from(100u64);
let b = SqlU256::from(50u64);
let sum = a + b;
let product = a * b;
let doubled = a * 2;
let tripled = 3 * a;

Hash and FixedBytes Creation

use ethereum_mysql::{sqlhash, SqlFixedBytes};

// Create various sized hashes at compile time
const TX_HASH: SqlFixedBytes<32> = sqlhash!(32, "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef");
const FUNCTION_SELECTOR: SqlFixedBytes<4> = sqlhash!(4, "0xa9059cbb");
const TOPIC: SqlFixedBytes<32> = sqlhash!(32, "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef");

SQLx Integration (String Mode)

use ethereum_mysql::{SqlAddress, SqlU256, SqlHash};
use sqlx::MySqlPool;
use std::str::FromStr;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let pool = MySqlPool::connect("mysql://user:pass@localhost/db").await?;
    let user_address = SqlAddress::from_str("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d").unwrap();
    let balance = SqlU256::from_str("1000000000000000000").unwrap();
    let tx_hash = SqlHash::from_str("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap();
    sqlx::query(
        "CREATE TABLE IF NOT EXISTS users (
            id INT AUTO_INCREMENT PRIMARY KEY,
            wallet_address VARCHAR(42) NOT NULL,
            balance VARCHAR(66),
            tx_hash VARCHAR(66),
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )",
    )
    .execute(&pool)
    .await?;
    sqlx::query("INSERT INTO users (wallet_address, balance, tx_hash) VALUES (?, ?, ?)")
        .bind(&user_address)
        .bind(&balance)
        .bind(&tx_hash)
        .execute(&pool)
        .await?;
    Ok(())
}

JSON Serialization (with serde)

use ethereum_mysql::{SqlAddress, SqlU256, sqladdress};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct User {
    id: u64,
    wallet: SqlAddress,
    balance: SqlU256,
    staked_amount: Option<SqlU256>,
}

let user = User {
    id: 1,
    wallet: sqladdress!("0x742d35Cc6635C0532925a3b8D42cC72b5c2A9A1d"),
    balance: SqlU256::from_str("1500000000000000000").unwrap(),
    staked_amount: Some(SqlU256::from_str("1000000000000000000").unwrap()),
};
let json = serde_json::to_string(&user)?;

License

Licensed under either of:

at your option.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Dependencies

~4.5–9MB
~169K SLoC