You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Compiling to WebAssembly is the thing Zig does that Rust does adequately and TypeScript cannot do at all, and it is the clearest technical differentiator eth.zig has over alloy.rs for application developers. A browser dApp that wants real signing today either ships a large JS bundle or a Rust/wasm-bindgen blob with a JS glue layer; a wasm32-freestanding eth.zig is a small, dependency-free .wasm with a plain C-shaped import surface.
The same work unlocks two other targets we get for free: wasm32-wasi for edge runtimes (Cloudflare Workers, Fastly, Deno Deploy), and bare-metal freestanding for hardware-wallet and secure-enclave firmware -- an audience that cannot use any existing Ethereum library because they all assume an allocator, a heap, and libc.
The actual blockers
This is not a "flip the target flag" change. Concretely, today:
Vendored C is unconditional.build.zig calls addXkcp on every module and adds C source files for libsecp256k1, XKCP, blst, and c-kzg with no way to opt out. link_libc = true is set unconditionally.
There is no backend selection.src/secp256k1.zig delegates sign, recover, and derivePublicKey straight to secp256k1_c (lines 140, 195, 256); src/keccak.zig imports keccak_xkcp.zig directly. Pure-Zig paths exist -- keccak_optimized.zig was fixed and vector-verified in keccak_optimized.zig: pure-Zig fallback produces wrong digests #80, and secp256k1.zig already carries the GLV endomorphism scaffolding -- but nothing selects them.
Transports assume a hosted OS.http_transport, ws_transport, and sse_transport need std.Io, sockets, and TLS. On wasm32-freestanding there is no socket; the host provides fetch.
Scope
Build options: -Dcrypto-backend=c|zig (default c on hosted targets, forced zig on freestanding) and -Dtransports=true|false. Gate addXkcp and the C source additions behind the backend option, and set link_libc accordingly.
Backend indirection: a comptime switch in secp256k1.zig and keccak.zig choosing the C or pure-Zig implementation. No behavior change on the default path -- the existing benchmarks must be byte-identical and speed-identical after this refactor, which is the main review criterion.
Freestanding module set: root.zig should compile with transports excluded, exposing primitives, hex, RLP, ABI, keccak, secp256k1, transaction signing, EIP-712, HD wallets, and keystore. KZG (blst) stays out of the freestanding build -- it is C-only and blob transactions are not a browser use case.
Allocator discipline: audit for hidden std.heap.page_allocator / libc-allocator assumptions on the freestanding paths; the caller supplies the allocator, and a fixed-buffer allocator must work.
CI: build legs for wasm32-freestanding, wasm32-wasi, and one bare aarch64-freestanding to keep the no-libc path honest. Run the unit suite under wasm32-wasi where the test runner supports it.
Demo: examples/wasm/ -- an HTML page that loads the .wasm and signs a transaction in-browser with no JS crypto dependency. This is the artifact that makes the case; ship it with the issue.
The C ABI issue and this one converge: the export fn surface from src/c_api.zig is exactly the right wasm export surface, and both need the same caller-allocates discipline. Land the backend selection here first, since the C ABI work can otherwise proceed independently.
Pointers
build.zig (addXkcp at line 14 and the addCSourceFile calls from line 201), src/secp256k1.zig, src/keccak.zig, src/keccak_optimized.zig (the verified pure-Zig Keccak from #80). Zig's std.crypto.ecc.Secp256k1 is already imported in secp256k1.zig and is a correct-but-slower fallback -- document the expected performance delta of the pure-Zig backend in bench/RESULTS.md rather than hiding it.
Motivation
Compiling to WebAssembly is the thing Zig does that Rust does adequately and TypeScript cannot do at all, and it is the clearest technical differentiator eth.zig has over alloy.rs for application developers. A browser dApp that wants real signing today either ships a large JS bundle or a Rust/wasm-bindgen blob with a JS glue layer; a
wasm32-freestandingeth.zig is a small, dependency-free.wasmwith a plain C-shaped import surface.The same work unlocks two other targets we get for free:
wasm32-wasifor edge runtimes (Cloudflare Workers, Fastly, Deno Deploy), and bare-metalfreestandingfor hardware-wallet and secure-enclave firmware -- an audience that cannot use any existing Ethereum library because they all assume an allocator, a heap, and libc.The actual blockers
This is not a "flip the target flag" change. Concretely, today:
build.zigcallsaddXkcpon every module and adds C source files for libsecp256k1, XKCP, blst, and c-kzg with no way to opt out.link_libc = trueis set unconditionally.src/secp256k1.zigdelegatessign,recover, andderivePublicKeystraight tosecp256k1_c(lines 140, 195, 256);src/keccak.zigimportskeccak_xkcp.zigdirectly. Pure-Zig paths exist --keccak_optimized.zigwas fixed and vector-verified in keccak_optimized.zig: pure-Zig fallback produces wrong digests #80, andsecp256k1.zigalready carries the GLV endomorphism scaffolding -- but nothing selects them.http_transport,ws_transport, andsse_transportneedstd.Io, sockets, and TLS. Onwasm32-freestandingthere is no socket; the host provides fetch.Scope
-Dcrypto-backend=c|zig(defaultcon hosted targets, forcedzigon freestanding) and-Dtransports=true|false. GateaddXkcpand the C source additions behind the backend option, and setlink_libcaccordingly.secp256k1.zigandkeccak.zigchoosing the C or pure-Zig implementation. No behavior change on the default path -- the existing benchmarks must be byte-identical and speed-identical after this refactor, which is the main review criterion.root.zigshould compile with transports excluded, exposing primitives, hex, RLP, ABI, keccak, secp256k1, transaction signing, EIP-712, HD wallets, and keystore. KZG (blst) stays out of the freestanding build -- it is C-only and blob transactions are not a browser use case.std.heap.page_allocator/ libc-allocator assumptions on the freestanding paths; the caller supplies the allocator, and a fixed-buffer allocator must work.wasm32-freestanding,wasm32-wasi, and one bareaarch64-freestandingto keep the no-libc path honest. Run the unit suite underwasm32-wasiwhere the test runner supports it.examples/wasm/-- an HTML page that loads the.wasmand signs a transaction in-browser with no JS crypto dependency. This is the artifact that makes the case; ship it with the issue.Relationship to #118
The C ABI issue and this one converge: the
export fnsurface fromsrc/c_api.zigis exactly the right wasm export surface, and both need the same caller-allocates discipline. Land the backend selection here first, since the C ABI work can otherwise proceed independently.Pointers
build.zig(addXkcpat line 14 and theaddCSourceFilecalls from line 201),src/secp256k1.zig,src/keccak.zig,src/keccak_optimized.zig(the verified pure-Zig Keccak from #80). Zig'sstd.crypto.ecc.Secp256k1is already imported insecp256k1.zigand is a correct-but-slower fallback -- document the expected performance delta of the pure-Zig backend inbench/RESULTS.mdrather than hiding it.