Skip to content

Commit a4f225f

Browse files
authored
Add documentation site for ethzig.org (#8)
* Add documentation site for ethzig.org Fumadocs-powered docs site (Next.js 15 + Tailwind v4) with 16 pages covering guides, API reference, benchmarks, and community resources. Also fixes README install version to v0.2.2 and adds docs badge. * Fix docs review feedback - ENS: replace placeholder text records section with real getText example - Transactions: fix SendTransactionOpts description (all fields are optional) - HD Wallets: clarify words reference in multiple accounts example - Introduction: add test key warning to quick start example - NotFound: use Tailwind classes instead of inline styles, increase redirect delay
1 parent 99a8961 commit a4f225f

34 files changed

Lines changed: 8350 additions & 2 deletions

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ zig-pkg/
44
.DS_Store
55
.env*
66
!.env.example
7+
8+
# Docs site build artifacts
9+
docs/node_modules/
10+
docs/.next/
11+
docs/.source/
12+
docs/out/

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# eth.zig
22

33
[![CI](https://github.com/strobelabs/eth.zig/actions/workflows/ci.yml/badge.svg)](https://github.com/strobelabs/eth.zig/actions/workflows/ci.yml)
4+
[![Docs](https://img.shields.io/badge/docs-ethzig.org-blue)](https://ethzig.org)
45
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
56
[![Zig](https://img.shields.io/badge/Zig-%E2%89%A5%200.15.2-orange)](https://ziglang.org/)
67

78
**The fastest Ethereum library. Pure Zig. Zero dependencies.**
89

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

12+
**[Read the docs at ethzig.org](https://ethzig.org)**
13+
1114
## Why eth.zig?
1215

1316
**Faster than Rust** -- eth.zig [beats alloy.rs](bench/RESULTS.md) (Rust's leading Ethereum library, backed by Paradigm) on **19 out of 26 benchmarks**, including UniswapV4 mulDiv. ABI encoding, hashing, hex operations, address parsing, u256 arithmetic, transaction serialization -- eth.zig is faster on the majority of operations.
@@ -122,15 +125,15 @@ const addr = key.toAddress();
122125
**One-liner:**
123126

124127
```bash
125-
zig fetch --save git+https://github.com/StrobeLabs/eth.zig.git#v0.2.1
128+
zig fetch --save git+https://github.com/StrobeLabs/eth.zig.git#v0.2.2
126129
```
127130

128131
**Or add manually** to your `build.zig.zon`:
129132

130133
```zig
131134
.dependencies = .{
132135
.eth = .{
133-
.url = "git+https://github.com/StrobeLabs/eth.zig.git#v0.2.1",
136+
.url = "git+https://github.com/StrobeLabs/eth.zig.git#v0.2.2",
134137
.hash = "...", // run `zig build` and it will tell you the expected hash
135138
},
136139
},

docs/.gitignore

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# deps
2+
/node_modules
3+
4+
# generated content
5+
.contentlayer
6+
.content-collections
7+
.source
8+
9+
# test & build
10+
/coverage
11+
/.next/
12+
/out/
13+
/build
14+
*.tsbuildinfo
15+
16+
# misc
17+
.DS_Store
18+
*.pem
19+
/.pnp
20+
.pnp.js
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*
24+
25+
# others
26+
.env*.local
27+
.vercel
28+
next-env.d.ts
29+
.env.local

docs/content/docs/benchmarks.mdx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
title: Benchmarks
3+
description: eth.zig vs alloy.rs -- head-to-head performance comparison across 26 Ethereum operations.
4+
---
5+
6+
Pure Zig vs Rust -- a head-to-head performance comparison of eth.zig and [alloy.rs](https://alloy.rs) across 26 core Ethereum operations.
7+
8+
**Score: eth.zig wins 19/26 | alloy.rs wins 5/26 | tied 2/26**
9+
10+
Benchmarks run on Apple Silicon with `ReleaseFast` (Zig) vs `--release` (Cargo). Both mulDiv benchmarks use true 512-bit intermediate arithmetic.
11+
12+
## Full Results
13+
14+
| Benchmark | eth.zig | alloy.rs | Winner |
15+
|-----------|---------|----------|--------|
16+
| keccak256_empty | 119 ns | 173 ns | **zig 1.45x** |
17+
| keccak256_32b | 128 ns | 175 ns | **zig 1.37x** |
18+
| keccak256_256b | 258 ns | 334 ns | **zig 1.29x** |
19+
| keccak256_1kb | 1,028 ns | 1,262 ns | **zig 1.23x** |
20+
| keccak256_4kb | 4,008 ns | 4,772 ns | **zig 1.19x** |
21+
| secp256k1_sign | 112,061 ns | 27,372 ns | rs 4.09x |
22+
| secp256k1_sign_recover | 254,525 ns | 119,700 ns | rs 2.13x |
23+
| address_derivation | 135 ns | 190 ns | **zig 1.41x** |
24+
| address_from_hex | 8 ns | 13 ns | **zig 1.62x** |
25+
| checksum_address | 159 ns | 201 ns | **zig 1.26x** |
26+
| abi_encode_transfer | 33 ns | 30 ns | rs 1.10x |
27+
| abi_encode_static | 29 ns | 50 ns | **zig 1.72x** |
28+
| abi_encode_dynamic | 114 ns | 175 ns | **zig 1.54x** |
29+
| abi_decode_uint256 | 22 ns | 26 ns | **zig 1.18x** |
30+
| abi_decode_dynamic | 75 ns | 133 ns | **zig 1.77x** |
31+
| rlp_encode_eip1559_tx | 43 ns | 38 ns | rs 1.13x |
32+
| rlp_decode_u256 | 3 ns | 6 ns | **zig 2.00x** |
33+
| u256_add | 2 ns | 2 ns | tie |
34+
| u256_mul | 2 ns | 5 ns | **zig 2.50x** |
35+
| u256_div | 3 ns | 12 ns | **zig 4.00x** |
36+
| u256_uniswapv2_amount_out | 43 ns | 13 ns | rs 3.31x |
37+
| u256_mulDiv | 12 ns | 17 ns | **zig 1.42x** |
38+
| u256_uniswapv4_swap | 21 ns | 24 ns | **zig 1.14x** |
39+
| hex_encode_32b | 11 ns | 11 ns | tie |
40+
| hex_decode_32b | 12 ns | 24 ns | **zig 2.00x** |
41+
| tx_hash_eip1559 | 184 ns | 210 ns | **zig 1.14x** |
42+
43+
## Score Summary
44+
45+
| | Count |
46+
|---|---|
47+
| eth.zig wins | 19 |
48+
| alloy.rs wins | 5 |
49+
| Tied | 2 |
50+
51+
## Key Optimizations
52+
53+
| Optimization | Impact |
54+
|---|---|
55+
| Knuth Algorithm D u64-limb division | mulDiv: 281 ns -> 12 ns (23x), beats alloy's 17 ns |
56+
| secp256k1 `mulDoubleBasePublic` recovery | sign_recover: 837 us -> 255 us (3.3x) |
57+
| Stack-buffer RLP encoding (single pass) | rlp_encode: 89 ns -> 43 ns (2.1x) |
58+
| ABI static-only fast path | abi_encode_transfer: 71 ns -> 33 ns (2.2x) |
59+
| `fastMul` u128 fast path | u256 compound ops: 2x faster |
60+
61+
## Where alloy.rs Wins
62+
63+
| Benchmark | Gap | Root Cause |
64+
|---|---|---|
65+
| secp256k1_sign | 4.09x | alloy uses k256-rs precomputed EC tables with variable-time ops; eth.zig uses constant-time stdlib |
66+
| secp256k1_sign_recover | 2.13x | Same as above (improved 3.3x in v0.3.0 via `mulDoubleBasePublic`) |
67+
| u256_uniswapv2_amount_out | 3.31x | alloy's `ruint` uses hand-optimized 4x u64 limb arithmetic |
68+
| abi_encode_transfer | 1.10x | alloy's `sol!` macro generates specialized encode code at compile time |
69+
| rlp_encode_eip1559_tx | 1.13x | alloy derive macros produce single-purpose encode code |
70+
71+
## Reproducing
72+
73+
```bash
74+
# Full comparison (requires Zig, Rust, Python 3)
75+
bash bench/compare.sh
76+
77+
# eth.zig benchmarks only
78+
zig build bench
79+
80+
# alloy benchmarks only
81+
(cd bench/alloy-bench && cargo bench --bench eth_comparison)
82+
```

docs/content/docs/comparison.mdx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
title: Comparison
3+
description: How eth.zig compares to alloy.rs, Zabi, and other Ethereum libraries.
4+
---
5+
6+
## Performance vs alloy.rs (Rust)
7+
8+
[alloy.rs](https://alloy.rs) is the leading Rust Ethereum library, backed by Paradigm. eth.zig outperforms it on the majority of benchmarks.
9+
10+
| Category | eth.zig | alloy.rs |
11+
|----------|---------|----------|
12+
| Benchmarks won | **19/26** | 5/26 |
13+
| ABI encoding | Faster (1.18-1.72x) | Faster on 1 specialized path |
14+
| Hashing (Keccak) | Faster (1.19-1.45x) | -- |
15+
| Hex operations | Faster (1.00-2.00x) | -- |
16+
| u256 arithmetic | Faster on div/mul/mulDiv | Faster on compound ops |
17+
| UniswapV4 mulDiv | Faster (1.42x) | -- |
18+
| secp256k1 signing | -- | Faster (precomputed tables) |
19+
20+
See [full benchmark results](/benchmarks) for details.
21+
22+
## Features vs Zabi (Zig)
23+
24+
[Zabi](https://www.zabi.sh/) is another Zig Ethereum library. Here's how they compare:
25+
26+
| Feature | eth.zig | Zabi |
27+
|---------|---------|------|
28+
| Dependencies | 0 | 0 |
29+
| Comptime selectors | Yes | No |
30+
| Pure Zig crypto (secp256k1) | Yes | No (C binding) |
31+
| ABI encode/decode | Yes | Yes |
32+
| HD wallets (BIP-32/39/44) | Yes | Yes |
33+
| ERC-20/721 wrappers | Yes | No |
34+
| JSON ABI parsing | Yes | Yes |
35+
| WebSocket transport | Yes | Yes |
36+
| ENS resolution | Yes | Yes |
37+
| EIP-712 typed data | Yes | Yes |
38+
| Multicall3 | Yes | No |
39+
40+
### Key Differences
41+
42+
**Pure Zig crypto**: eth.zig implements secp256k1 entirely in Zig. Zabi uses a C binding to libsecp256k1. This means eth.zig has zero C dependencies -- no cross-compilation issues, no linking headaches, and full auditability.
43+
44+
**Comptime selectors**: eth.zig computes function selectors and event topics at compile time. This is unique to eth.zig and eliminates runtime hashing overhead. See [Comptime Selectors](/comptime).
45+
46+
**Typed token wrappers**: eth.zig provides ERC-20 and ERC-721 structs that wrap the contract address and provider for a clean API (`token.balanceOf(addr)` instead of raw ABI encoding).
47+
48+
## vs ethers.js / viem (JavaScript)
49+
50+
| Aspect | eth.zig | ethers.js / viem |
51+
|--------|---------|-----------------|
52+
| Language | Zig | JavaScript/TypeScript |
53+
| Performance | Nanoseconds | Microseconds-milliseconds |
54+
| Dependencies | 0 | Many (npm packages) |
55+
| Use case | Backend, infra, bots | Frontend, scripts, dApps |
56+
| Binary size | Single static binary | Node.js runtime required |
57+
58+
eth.zig is not a replacement for JavaScript libraries in browser environments. It targets backend infrastructure, MEV bots, indexers, and latency-sensitive applications where performance matters.

docs/content/docs/comptime.mdx

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
title: Comptime Selectors
3+
description: Compile-time function selectors and event topics -- a key differentiator of eth.zig.
4+
---
5+
6+
One of eth.zig's most powerful features is **comptime-first design**. Function selectors and event topics are computed at compile time, eliminating runtime hashing entirely.
7+
8+
## Function Selectors
9+
10+
A Solidity function selector is the first 4 bytes of the Keccak-256 hash of the function signature. In eth.zig, this is done at compile time:
11+
12+
```zig
13+
const eth = @import("eth");
14+
15+
// Computed at compile time -- zero runtime cost
16+
const transfer_sel = eth.abi_comptime.comptimeSelector("transfer(address,uint256)");
17+
// transfer_sel == [4]u8{ 0xa9, 0x05, 0x9c, 0xbb }
18+
19+
const approve_sel = eth.abi_comptime.comptimeSelector("approve(address,uint256)");
20+
// approve_sel == [4]u8{ 0x09, 0x5e, 0xa7, 0xb3 }
21+
```
22+
23+
The Zig compiler evaluates `comptimeSelector` during compilation. The resulting binary contains only the precomputed 4-byte selectors -- no hashing at runtime.
24+
25+
## Event Topics
26+
27+
Event topics (used for log filtering) work the same way:
28+
29+
```zig
30+
const eth = @import("eth");
31+
32+
const transfer_topic = eth.abi_comptime.comptimeTopic("Transfer(address,address,uint256)");
33+
// transfer_topic == keccak256("Transfer(address,address,uint256)")
34+
35+
const approval_topic = eth.abi_comptime.comptimeTopic("Approval(address,address,uint256)");
36+
```
37+
38+
## Why This Matters
39+
40+
In other Ethereum libraries, function selectors are typically computed at runtime:
41+
42+
| Approach | When It Runs | Cost |
43+
|----------|-------------|------|
44+
| eth.zig `comptimeSelector` | Compile time | 0 ns at runtime |
45+
| Runtime `keccak256(signature)` | Every call | ~128 ns per hash |
46+
| Cached/lazy hash | First call | ~128 ns once, then lookup |
47+
48+
For hot paths (MEV bots, indexers, high-frequency DeFi), eliminating per-call hashing overhead adds up.
49+
50+
## ERC-20 Precomputed Selectors
51+
52+
The ERC-20 wrapper provides all standard selectors as comptime constants:
53+
54+
```zig
55+
const selectors = eth.erc20.selectors;
56+
57+
selectors.name; // name()
58+
selectors.symbol; // symbol()
59+
selectors.decimals; // decimals()
60+
selectors.totalSupply; // totalSupply()
61+
selectors.balanceOf; // balanceOf(address)
62+
selectors.transfer; // transfer(address,uint256)
63+
selectors.approve; // approve(address,uint256)
64+
selectors.allowance; // allowance(address,address)
65+
selectors.transferFrom; // transferFrom(address,address,uint256)
66+
```
67+
68+
## Custom Selectors
69+
70+
Define selectors for any contract function:
71+
72+
```zig
73+
const eth = @import("eth");
74+
75+
// UniswapV2 Router
76+
const swap_sel = eth.abi_comptime.comptimeSelector(
77+
"swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"
78+
);
79+
80+
// Aave V3 Pool
81+
const supply_sel = eth.abi_comptime.comptimeSelector(
82+
"supply(address,uint256,address,uint16)"
83+
);
84+
85+
// Any custom function
86+
const my_sel = eth.abi_comptime.comptimeSelector(
87+
"myFunction(uint256,bytes32,bool)"
88+
);
89+
```

docs/content/docs/contracts.mdx

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
title: Contract Interaction
3+
description: Reading and writing to Ethereum smart contracts with eth.zig.
4+
---
5+
6+
eth.zig provides two levels of contract interaction: low-level functions (`contractRead`/`contractWrite`) and the high-level `Contract` struct.
7+
8+
## Reading a Contract
9+
10+
Use `contractRead` to call a contract function and decode the result:
11+
12+
```zig
13+
const eth = @import("eth");
14+
15+
// Define the function selector (comptime -- zero runtime cost)
16+
const balanceOf_sel = eth.abi_comptime.comptimeSelector("balanceOf(address)");
17+
18+
// Read balanceOf for a given address
19+
const results = try eth.contract.contractRead(
20+
allocator,
21+
&provider,
22+
token_address,
23+
balanceOf_sel,
24+
&.{.{ .address = holder_address }},
25+
&.{.uint256},
26+
);
27+
defer eth.contract.freeReturnValues(results, allocator);
28+
29+
const balance = results[0].uint256;
30+
```
31+
32+
## Writing to a Contract
33+
34+
Use `contractWrite` to send a state-changing transaction:
35+
36+
```zig
37+
const eth = @import("eth");
38+
39+
const transfer_sel = eth.abi_comptime.comptimeSelector("transfer(address,uint256)");
40+
41+
const tx_hash = try eth.contract.contractWrite(
42+
allocator,
43+
&wallet,
44+
token_address,
45+
transfer_sel,
46+
&.{
47+
.{ .address = recipient },
48+
.{ .uint256 = amount },
49+
},
50+
);
51+
```
52+
53+
## Contract Struct
54+
55+
The `Contract` struct binds a provider and address for repeated interaction:
56+
57+
```zig
58+
const eth = @import("eth");
59+
60+
var contract = eth.contract.Contract.init(allocator, contract_address, &provider);
61+
62+
// Raw read (returns bytes)
63+
const result_bytes = try contract.call(calldata);
64+
defer allocator.free(result_bytes);
65+
66+
// Typed read with selector
67+
const result = try contract.readRaw(selector, &.{.{ .address = addr }});
68+
defer allocator.free(result);
69+
```
70+
71+
## Multicall3
72+
73+
Batch multiple read calls into a single RPC request using Multicall3:
74+
75+
```zig
76+
const eth = @import("eth");
77+
78+
var mc = eth.multicall.Multicall3.init(allocator, &provider);
79+
80+
// Queue calls
81+
try mc.addCall(token_addr, eth.erc20.selectors.balanceOf, .{holder_addr});
82+
try mc.addCall(token_addr, eth.erc20.selectors.totalSupply, .{});
83+
try mc.addCall(token_addr, eth.erc20.selectors.decimals, .{});
84+
85+
// Execute all in one RPC call
86+
const results = try mc.execute();
87+
```
88+
89+
This reduces RPC round-trips and is essential for building efficient indexers or dashboards.

0 commit comments

Comments
 (0)