Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 27 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,42 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Zig](https://img.shields.io/badge/Zig-%E2%89%A5%200.15.2-orange)](https://ziglang.org/)

**The fastest Ethereum library.** Beats Rust's alloy.rs on 20 out of 26 benchmarks.
**The fastest Ethereum library.** Beats Rust's alloy.rs on 23 out of 26 benchmarks.

A complete Ethereum client library written in Zig -- ABI encoding, RLP serialization, secp256k1 signing, Keccak-256 hashing, HD wallets, ERC-20/721 tokens, JSON-RPC, ENS, and more. Just `zig build`.
A complete Ethereum client library written in Zig -- ABI encoding, RLP serialization, secp256k1 signing, Keccak-256 hashing, HD wallets, ERC-20/721 tokens, JSON-RPC, ENS, and more.

**[Read the docs at ethzig.org](https://ethzig.org)**

## Why eth.zig?

**Fastest Ethereum library** -- eth.zig [beats alloy.rs](bench/RESULTS.md) (Rust's leading Ethereum library, backed by Paradigm) on **20 out of 26 benchmarks**. ABI decoding up to 7.94x faster, Keccak hashing up to 1.34x, u256 division 4x, UniswapV2 getAmountOut 1.30x, transaction hashing 1.27x. See the [full results](bench/RESULTS.md).
**Fastest Ethereum library** -- eth.zig [beats alloy.rs](bench/RESULTS.md) (Rust's leading Ethereum library, backed by Paradigm) on **23 out of 26 benchmarks**. ECDSA signing 2.09x faster, ABI decoding up to 7.65x, secp256k1 recovery 3.23x, mulDiv 1.76x, u256 division 5.57x, Keccak hashing up to 1.26x. See the [full results](bench/RESULTS.md).

**Comptime-first** -- Function selectors and event topics are computed at compile time with zero runtime cost. The compiler does the hashing so your program doesn't have to.

**Complete** -- ABI, RLP, secp256k1, Keccak-256, BIP-32/39/44 HD wallets, EIP-712, JSON-RPC, WebSocket, ENS, ERC-20/721 -- everything you need for Ethereum in one package.

## Performance vs alloy.rs

eth.zig wins **20/26 benchmarks** against [alloy.rs](https://alloy.rs). Measured on Apple Silicon, `ReleaseFast` (Zig) vs `--release` (Rust). Criterion-style harness with 0.5s warmup and 2s measurement.
eth.zig wins **23/26 benchmarks** against [alloy.rs](https://alloy.rs). Measured on Apple Silicon, `ReleaseFast` (Zig) vs `--release` (Rust). Criterion-style harness with 0.5s warmup and 2s measurement.

| Operation | eth.zig | alloy.rs | Winner |
|-----------|---------|----------|--------|
| Keccak-256 (32B) | 135 ns | 179 ns | **zig 1.33x** |
| Keccak-256 (4KB) | 4,097 ns | 4,826 ns | **zig 1.18x** |
| ABI encode (static) | 13 ns | 51 ns | **zig 3.92x** |
| ABI encode (dynamic) | 91 ns | 171 ns | **zig 1.88x** |
| ABI decode (uint256) | 8 ns | 26 ns | **zig 3.25x** |
| ABI decode (dynamic) | 17 ns | 135 ns | **zig 7.94x** |
| Address derivation | 136 ns | 190 ns | **zig 1.40x** |
| Checksum address | 161 ns | 201 ns | **zig 1.25x** |
| u256 multiply | 2 ns | 5 ns | **zig 2.50x** |
| u256 division | 3 ns | 12 ns | **zig 4.00x** |
| UniswapV2 getAmountOut | 10 ns | 13 ns | **zig 1.30x** |
| UniswapV4 swap | 22 ns | 24 ns | **zig 1.09x** |
| Hex encode (32B) | 11 ns | 12 ns | **zig 1.09x** |
| Hex decode (32B) | 12 ns | 14 ns | **zig 1.17x** |
| TX hash (EIP-1559) | 170 ns | 216 ns | **zig 1.27x** |

alloy.rs wins on secp256k1 signing (3.09x -- large precomputed EC tables), address hex parsing (1.33x -- SIMD), and u256 mulDiv (1.20x). See [full results](bench/RESULTS.md).
| secp256k1 sign | 24,481 ns | 51,286 ns | **zig 2.09x** |
| secp256k1 sign+recover | 67,816 ns | 218,720 ns | **zig 3.23x** |
| Keccak-256 (32B) | 268 ns | 337 ns | **zig 1.26x** |
| Keccak-256 (4KB) | 7,886 ns | 9,238 ns | **zig 1.17x** |
| ABI encode (static) | 24 ns | 100 ns | **zig 4.17x** |
| ABI encode (dynamic) | 176 ns | 325 ns | **zig 1.85x** |
| ABI decode (uint256) | 16 ns | 51 ns | **zig 3.19x** |
| ABI decode (dynamic) | 34 ns | 260 ns | **zig 7.65x** |
| u256 mulDiv (512-bit) | 17 ns | 30 ns | **zig 1.76x** |
| u256 division | 7 ns | 39 ns | **zig 5.57x** |
| u256 multiply | 4 ns | 9 ns | **zig 2.25x** |
| UniswapV2 getAmountOut | 21 ns | 27 ns | **zig 1.29x** |
| UniswapV4 swap | 42 ns | 47 ns | **zig 1.12x** |
| TX hash (EIP-1559) | 333 ns | 407 ns | **zig 1.22x** |

alloy.rs wins on address hex parsing (1.36x -- SIMD) and RLP u256 decoding (1.40x). See [full results](bench/RESULTS.md).

## Quick Start

Expand Down Expand Up @@ -221,19 +220,19 @@ cd examples && zig build && ./zig-out/bin/01_derive_address

| Category | eth.zig | alloy.rs |
|----------|---------|----------|
| Benchmarks won | **20/26** | 4/26 |
| ABI encoding/decoding | Faster (2.23-7.94x) | -- |
| Hashing (Keccak) | Faster (1.18-1.34x) | -- |
| u256 arithmetic | Faster on add/mul/div/V2/V4 | Faster on mulDiv (1.20x) |
| Hex operations | Faster (1.09-1.17x) | -- |
| secp256k1 signing | -- | Faster (3.09x, larger precomputed tables) |
| Benchmarks won | **23/26** | 2/26 |
| secp256k1 signing | Faster (2.09-3.23x) | -- |
| ABI encoding/decoding | Faster (1.85-7.65x) | -- |
| Hashing (Keccak) | Faster (1.17-1.26x) | -- |
| u256 arithmetic | Faster on all ops (1.12-5.57x) | -- |
| Hex operations | Faster (1.05-1.22x) | Faster on hex parsing (1.36x, SIMD) |

### Features vs Zabi (Zig)

| Feature | eth.zig | Zabi |
|---------|---------|------|
| Comptime selectors | Yes | No |
| Pure Zig secp256k1 | Yes | No (C binding) |
| secp256k1 ECDSA | Yes (libsecp256k1 + Zig fallback) | Yes (C binding) |
| ABI encode/decode | Yes | Yes |
| HD wallets (BIP-32/39/44) | Yes | Yes |
| ERC-20/721 wrappers | Yes | No |
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ We will acknowledge receipt within 48 hours and aim to provide a fix or mitigati

## Scope

eth.zig includes cryptographic primitives (secp256k1 ECDSA, Keccak-256, BIP-32/39/44) implemented in pure Zig. Security issues in these components are treated as critical.
eth.zig includes cryptographic primitives (secp256k1 ECDSA, Keccak-256, BIP-32/39/44). Security issues in these components are treated as critical.

Areas of particular concern:

Expand Down
79 changes: 40 additions & 39 deletions bench/RESULTS.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,70 @@
# eth.zig vs alloy.rs: Ethereum Library Benchmark Comparison

Pure Zig vs Rust -- a head-to-head performance comparison of [eth.zig](https://github.com/StrobeLabs/eth.zig) and [alloy.rs](https://alloy.rs) across 26 core Ethereum operations: Keccak-256 hashing, ABI encoding/decoding, RLP serialization, secp256k1 ECDSA signing, u256 arithmetic (including UniswapV4 mulDiv with true 512-bit intermediate), hex operations, address derivation, and EIP-1559 transaction hashing.
Zig vs Rust -- a head-to-head performance comparison of [eth.zig](https://github.com/StrobeLabs/eth.zig) and [alloy.rs](https://alloy.rs) across 26 core Ethereum operations: Keccak-256 hashing, ABI encoding/decoding, RLP serialization, secp256k1 ECDSA signing, u256 arithmetic (including UniswapV4 mulDiv with true 512-bit intermediate), hex operations, address derivation, and EIP-1559 transaction hashing.

**Score: eth.zig wins 20/26 | alloy.rs wins 4/26 | tied 2/26**
**Score: eth.zig wins 23/26 | alloy.rs wins 2/26 | tied 1/26**

Benchmarks run on Apple Silicon with `ReleaseFast` (Zig) vs `--release` (Cargo). Custom criterion-style harness with 0.5s warmup, calibrated batch sizes, and 2s measurement window. Both mulDiv benchmarks use true 512-bit intermediate arithmetic (eth.zig's `mulDiv`, alloy's `U512` from ruint).

## Full Comparison

| Benchmark | eth-zig | alloy.rs | Winner |
|---|---|---|---|
| keccak256_empty | 131 ns | 175 ns | **zig 1.34x** |
| keccak256_32b | 135 ns | 179 ns | **zig 1.33x** |
| keccak256_256b | 271 ns | 333 ns | **zig 1.23x** |
| keccak256_1kb | 1,069 ns | 1,263 ns | **zig 1.18x** |
| keccak256_4kb | 4,097 ns | 4,826 ns | **zig 1.18x** |
| secp256k1_sign | 83,448 ns | 27,000 ns | rs 3.09x |
| secp256k1_sign_recover | 233,841 ns | 114,170 ns | rs 2.05x |
| address_derivation | 136 ns | 190 ns | **zig 1.40x** |
| address_from_hex | 8 ns | 6 ns | rs 1.33x |
| checksum_address | 161 ns | 201 ns | **zig 1.25x** |
| abi_encode_transfer | 13 ns | 29 ns | **zig 2.23x** |
| abi_encode_static | 13 ns | 51 ns | **zig 3.92x** |
| abi_encode_dynamic | 91 ns | 171 ns | **zig 1.88x** |
| abi_decode_uint256 | 8 ns | 26 ns | **zig 3.25x** |
| abi_decode_dynamic | 17 ns | 135 ns | **zig 7.94x** |
| rlp_encode_eip1559_tx | 34 ns | 37 ns | **zig 1.09x** |
| rlp_decode_u256 | 5 ns | 5 ns | tie |
| u256_add | 2 ns | 2 ns | tie |
| u256_mul | 2 ns | 5 ns | **zig 2.50x** |
| u256_div | 3 ns | 12 ns | **zig 4.00x** |
| u256_uniswapv2_amount_out | 10 ns | 13 ns | **zig 1.30x** |
| u256_mulDiv | 18 ns | 15 ns | rs 1.20x |
| u256_uniswapv4_swap | 22 ns | 24 ns | **zig 1.09x** |
| hex_encode_32b | 11 ns | 12 ns | **zig 1.09x** |
| hex_decode_32b | 12 ns | 14 ns | **zig 1.17x** |
| tx_hash_eip1559 | 170 ns | 216 ns | **zig 1.27x** |
| keccak256_empty | 275 ns | 333 ns | **zig 1.21x** |
| keccak256_32b | 268 ns | 337 ns | **zig 1.26x** |
| keccak256_256b | 536 ns | 640 ns | **zig 1.19x** |
| keccak256_1kb | 2,052 ns | 2,432 ns | **zig 1.19x** |
| keccak256_4kb | 7,886 ns | 9,238 ns | **zig 1.17x** |
| secp256k1_sign | 24,481 ns | 51,286 ns | **zig 2.09x** |
| secp256k1_sign_recover | 67,816 ns | 218,720 ns | **zig 3.23x** |
| address_derivation | 262 ns | 370 ns | **zig 1.41x** |
| address_from_hex | 15 ns | 11 ns | rs 1.36x |
| checksum_address | 312 ns | 450 ns | **zig 1.44x** |
| abi_encode_transfer | 25 ns | 61 ns | **zig 2.44x** |
| abi_encode_static | 24 ns | 100 ns | **zig 4.17x** |
| abi_encode_dynamic | 176 ns | 325 ns | **zig 1.85x** |
| abi_decode_uint256 | 16 ns | 51 ns | **zig 3.19x** |
| abi_decode_dynamic | 34 ns | 260 ns | **zig 7.65x** |
| rlp_encode_eip1559_tx | 57 ns | 72 ns | **zig 1.26x** |
| rlp_decode_u256 | 14 ns | 10 ns | rs 1.40x |
| u256_add | 4 ns | 4 ns | tie |
| u256_mul | 4 ns | 9 ns | **zig 2.25x** |
| u256_div | 7 ns | 39 ns | **zig 5.57x** |
| u256_uniswapv2_amount_out | 21 ns | 27 ns | **zig 1.29x** |
| u256_mulDiv | 17 ns | 30 ns | **zig 1.76x** |
| u256_uniswapv4_swap | 42 ns | 47 ns | **zig 1.12x** |
| hex_encode_32b | 22 ns | 23 ns | **zig 1.05x** |
| hex_decode_32b | 23 ns | 28 ns | **zig 1.22x** |
| tx_hash_eip1559 | 333 ns | 407 ns | **zig 1.22x** |

## Score Summary

| | Count |
|---|---|
| eth-zig wins | 20 |
| alloy.rs wins | 4 |
| Tied | 2 |
| eth-zig wins | 23 |
| alloy.rs wins | 2 |
| Tied | 1 |

## Key Optimizations

| Optimization | Impact |
|---|---|
| Lane-complementing Keccak-f[1600] (XKCP opt64) | keccak256_4kb: 1.18x faster than alloy |
| U256Limb limb-native arithmetic | uniswapv2: beats alloy 1.30x (was 3.58x loss) |
| Half-word division (`div128by64`) | u256_div: 3ns, 4.00x faster than alloy |
| bitcoin-core/secp256k1 C backend (vendored) | secp256k1_sign: 2.09x faster than alloy; sign_recover: 3.23x faster |
| Lane-complementing Keccak-f[1600] (XKCP opt64) | keccak256_4kb: 1.17x faster than alloy |
| Streamlined mulDiv with native u128 division | mulDiv: 1.76x faster than alloy (17ns vs 30ns) |
| U256Limb limb-native arithmetic | uniswapv2: beats alloy 1.29x |
| Half-word division (`div128by64`) | u256_div: 7ns, 5.57x faster than alloy |
| mulWide + divWide (Knuth D for 512-bit) | Replaces 256-iteration binary long division with ~4-iteration Knuth D |
| FixedBufferAllocator in benchmarks | Eliminates allocator overhead for ABI/RLP/TX benchmarks |
| GLV endomorphism for secp256k1 signing | Constant-time, 1.4x faster than v0.2 |
| Vendored bitcoin-core/secp256k1 + XKCP keccak | Best-in-class C backends compiled by `zig build` |
| Custom criterion-style harness | Accurate timing in the sub-25ns regime; zbench had ~25ns floor on macOS |

## Where alloy.rs Wins

| Benchmark | Gap | Root Cause |
|---|---|---|
| secp256k1_sign | 3.09x | k256-rs uses large precomputed base point tables (hundreds of points); eth.zig uses 16-point GLV tables. Both constant-time for signing. |
| secp256k1_sign_recover | 2.05x | k256-rs uses variable-time Shamir's trick for recovery (safe -- no secrets involved); eth.zig uses conservative constant-time path |
| address_from_hex | 1.33x | alloy uses SIMD hex parsing; eth.zig uses scalar loop |
| u256_mulDiv | 1.20x | ruint's reciprocal-based division vs eth.zig's Knuth Algorithm D |
| address_from_hex | 1.36x | alloy uses SIMD hex parsing; eth.zig uses scalar loop |
| rlp_decode_u256 | 1.40x | alloy's ruint has optimized small-integer RLP decoding |

## Reproducing

Expand Down
34 changes: 34 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub fn build(b: *std.Build) void {
.link_libc = true,
});
addXkcp(b, eth_module, target);
addSecp256k1(b, eth_module);

// Unit tests
const unit_tests = b.addTest(.{
Expand Down Expand Up @@ -53,6 +54,7 @@ pub fn build(b: *std.Build) void {
.link_libc = true,
});
addXkcp(b, bench_module, target);
addSecp256k1(b, bench_module);

const bench_exe = b.addExecutable(.{
.name = "bench",
Expand Down Expand Up @@ -179,3 +181,35 @@ fn addXkcp(b: *std.Build, module: *std.Build.Module, target: std.Build.ResolvedT
.flags = c_flags,
});
}

/// Add libsecp256k1 (Bitcoin Core) C sources for high-performance EC operations.
fn addSecp256k1(b: *std.Build, module: *std.Build.Module) void {
const c_flags = &.{
"-DENABLE_MODULE_RECOVERY=1", // ecrecover -- essential for Ethereum
"-DENABLE_MODULE_ECDH=1",
"-DENABLE_MODULE_EXTRAKEYS=1",
"-DENABLE_MODULE_SCHNORRSIG=1",
"-DENABLE_MODULE_ELLSWIFT=1",
"-DECMULT_WINDOW_SIZE=15",
"-DCOMB_BLOCKS=43",
"-DCOMB_TEETH=6",
"-O2",
"-Wno-unused-function",
};

module.addIncludePath(b.path("src/crypto/secp256k1/include"));
module.addIncludePath(b.path("src/crypto/secp256k1/src"));

// Unity build: 3 C files
const src_files = [_][]const u8{
"src/crypto/secp256k1/src/secp256k1.c",
"src/crypto/secp256k1/src/precomputed_ecmult.c",
"src/crypto/secp256k1/src/precomputed_ecmult_gen.c",
};
for (src_files) |src| {
module.addCSourceFile(.{
.file = b.path(src),
.flags = c_flags,
});
}
}
82 changes: 41 additions & 41 deletions docs/content/docs/benchmarks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,70 @@ title: Benchmarks
description: eth.zig vs alloy.rs -- head-to-head performance comparison across 26 Ethereum operations.
---

Pure Zig vs Rust -- a head-to-head performance comparison of eth.zig and [alloy.rs](https://alloy.rs) across 26 core Ethereum operations.
eth.zig vs Rust -- a head-to-head performance comparison of eth.zig and [alloy.rs](https://alloy.rs) across 26 core Ethereum operations.

**Score: eth.zig wins 19/26 | alloy.rs wins 5/26 | tied 2/26**
**Score: eth.zig wins 23/26 | alloy.rs wins 1/26 | tied 2/26**

Benchmarks run on Apple Silicon with `ReleaseFast` (Zig) vs `--release` (Cargo). Both mulDiv benchmarks use true 512-bit intermediate arithmetic.
Benchmarks run on Apple Silicon with `ReleaseFast` (Zig) vs `--release` (Cargo). Custom criterion-style harness with 0.5s warmup, calibrated batch sizes, and 2s measurement window. Both mulDiv benchmarks use true 512-bit intermediate arithmetic.

## Full Results

| Benchmark | eth.zig | alloy.rs | Winner |
|-----------|---------|----------|--------|
| keccak256_empty | 119 ns | 173 ns | **zig 1.45x** |
| keccak256_32b | 128 ns | 175 ns | **zig 1.37x** |
| keccak256_256b | 258 ns | 334 ns | **zig 1.29x** |
| keccak256_1kb | 1,028 ns | 1,262 ns | **zig 1.23x** |
| keccak256_4kb | 4,008 ns | 4,772 ns | **zig 1.19x** |
| secp256k1_sign | 112,061 ns | 27,372 ns | rs 4.09x |
| secp256k1_sign_recover | 254,525 ns | 119,700 ns | rs 2.13x |
| address_derivation | 135 ns | 190 ns | **zig 1.41x** |
| address_from_hex | 8 ns | 13 ns | **zig 1.62x** |
| checksum_address | 159 ns | 201 ns | **zig 1.26x** |
| abi_encode_transfer | 33 ns | 30 ns | rs 1.10x |
| abi_encode_static | 29 ns | 50 ns | **zig 1.72x** |
| abi_encode_dynamic | 114 ns | 175 ns | **zig 1.54x** |
| abi_decode_uint256 | 22 ns | 26 ns | **zig 1.18x** |
| abi_decode_dynamic | 75 ns | 133 ns | **zig 1.77x** |
| rlp_encode_eip1559_tx | 43 ns | 38 ns | rs 1.13x |
| rlp_decode_u256 | 3 ns | 6 ns | **zig 2.00x** |
| u256_add | 2 ns | 2 ns | tie |
| u256_mul | 2 ns | 5 ns | **zig 2.50x** |
| u256_div | 3 ns | 12 ns | **zig 4.00x** |
| u256_uniswapv2_amount_out | 43 ns | 13 ns | rs 3.31x |
| u256_mulDiv | 12 ns | 17 ns | **zig 1.42x** |
| u256_uniswapv4_swap | 21 ns | 24 ns | **zig 1.14x** |
| hex_encode_32b | 11 ns | 11 ns | tie |
| hex_decode_32b | 12 ns | 24 ns | **zig 2.00x** |
| tx_hash_eip1559 | 184 ns | 210 ns | **zig 1.14x** |
| keccak256_empty | 257 ns | 334 ns | **zig 1.30x** |
| keccak256_32b | 259 ns | 336 ns | **zig 1.30x** |
| keccak256_256b | 519 ns | 639 ns | **zig 1.23x** |
| keccak256_1kb | 2,000 ns | 2,494 ns | **zig 1.25x** |
| keccak256_4kb | 7,673 ns | 9,292 ns | **zig 1.21x** |
| secp256k1_sign | 24,609 ns | 51,738 ns | **zig 2.10x** |
| secp256k1_sign_recover | 54,221 ns | 218,790 ns | **zig 4.04x** |
| address_derivation | 262 ns | 363 ns | **zig 1.39x** |
| address_from_hex | 15 ns | 11 ns | rs 1.36x |
| checksum_address | 307 ns | 387 ns | **zig 1.26x** |
| abi_encode_transfer | 25 ns | 55 ns | **zig 2.20x** |
| abi_encode_static | 24 ns | 97 ns | **zig 4.04x** |
| abi_encode_dynamic | 171 ns | 324 ns | **zig 1.89x** |
| abi_decode_uint256 | 16 ns | 50 ns | **zig 3.12x** |
| abi_decode_dynamic | 32 ns | 256 ns | **zig 8.00x** |
| rlp_encode_eip1559_tx | 55 ns | 72 ns | **zig 1.31x** |
| rlp_decode_u256 | 8 ns | 9 ns | **zig 1.12x** |
| u256_add | 4 ns | 4 ns | tie |
| u256_mul | 4 ns | 10 ns | **zig 2.50x** |
| u256_div | 7 ns | 24 ns | **zig 3.43x** |
| u256_uniswapv2_amount_out | 21 ns | 24 ns | **zig 1.14x** |
| u256_mulDiv | 17 ns | 29 ns | **zig 1.71x** |
| u256_uniswapv4_swap | 41 ns | 45 ns | **zig 1.10x** |
| hex_encode_32b | 21 ns | 21 ns | tie |
| hex_decode_32b | 23 ns | 28 ns | **zig 1.22x** |
| tx_hash_eip1559 | 328 ns | 402 ns | **zig 1.23x** |

## Score Summary

| | Count |
|---|---|
| eth.zig wins | 19 |
| alloy.rs wins | 5 |
| eth.zig wins | 23 |
| alloy.rs wins | 1 |
| Tied | 2 |

## Key Optimizations

| Optimization | Impact |
|---|---|
| Knuth Algorithm D u64-limb division | mulDiv: 281 ns -> 12 ns (23x), beats alloy's 17 ns |
| secp256k1 `mulDoubleBasePublic` recovery | sign_recover: 837 us -> 255 us (3.3x) |
| Stack-buffer RLP encoding (single pass) | rlp_encode: 89 ns -> 43 ns (2.1x) |
| ABI static-only fast path | abi_encode_transfer: 71 ns -> 33 ns (2.2x) |
| `fastMul` u128 fast path | u256 compound ops: 2x faster |
| bitcoin-core/secp256k1 C backend (vendored) | secp256k1_sign: 2.10x faster than alloy; sign_recover: 4.04x faster |
| Lane-complementing Keccak-f[1600] (XKCP opt64) | keccak256_4kb: 1.21x faster than alloy |
| Streamlined mulDiv with native u128 division | mulDiv: 1.71x faster than alloy (17ns vs 29ns) |
| mulWide + divWide (Knuth D for 512-bit) | Replaces 256-iteration binary long division with ~4-iteration Knuth D |
| U256Limb limb-native arithmetic | uniswapv2: beats alloy 1.14x |
| Half-word division (`div128by64`) | u256_div: 7ns, 3.43x faster than alloy |
| FixedBufferAllocator in benchmarks | Eliminates allocator overhead for ABI/RLP/TX benchmarks |
| Vendored bitcoin-core/secp256k1 + XKCP keccak | Best-in-class C backends compiled by `zig build` |
| Custom criterion-style harness | Accurate timing in the sub-25ns regime; zbench had ~25ns floor on macOS |

## Where alloy.rs Wins

| Benchmark | Gap | Root Cause |
|---|---|---|
| secp256k1_sign | 4.09x | alloy uses k256-rs precomputed EC tables with variable-time ops; eth.zig uses constant-time stdlib |
| secp256k1_sign_recover | 2.13x | Same as above (improved 3.3x in v0.3.0 via `mulDoubleBasePublic`) |
| u256_uniswapv2_amount_out | 3.31x | alloy's `ruint` uses hand-optimized 4x u64 limb arithmetic |
| abi_encode_transfer | 1.10x | alloy's `sol!` macro generates specialized encode code at compile time |
| rlp_encode_eip1559_tx | 1.13x | alloy derive macros produce single-purpose encode code |
| address_from_hex | 1.36x | alloy uses SIMD hex parsing; eth.zig uses scalar loop |

## Reproducing

Expand Down
Loading