Add comptime contract bindings: eth.bind / abigen (closes #68)#97
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a comptime ABI binding generator ( ChangesCompile-time ABI Binding System
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
The flagship Zig differentiator: a JSON ABI becomes a fully-typed
contract struct AT COMPILE TIME, with selectors and event topics
precomputed, typed read methods, and typed event decoders -- no runtime
ABI parsing and no codegen step. alloy needs a proc-macro + build step
to do this; Zig does it in-language.
const Weth = eth.bind(@embedfile("weth.json"));
const w = Weth.at(weth_address);
const bal = try w.balanceOf(&provider, holder); // bal: u256
const t = try Weth.decodeTransfer(log); // { from, to, value }
- abigen.Bind(comptime abi_json) returns a struct type with at(address),
selectorOf/topicOf, ArgsOf/ReturnOf, a typed read method per function
(ABI-encodes selector ++ args, eth_call via *Provider, decodes the
mapped return type), and decodeEvent for typed event decoding.
- Comptime JSON tokenizer (std.json needs a runtime allocator and can't
run at comptime); builds comptime-known structures by scanning the
ABI string.
- ABI->Zig type mapping: uintN->smallest fitting uint (uint8->u8,
uint256->u256), intN->iN, address->[20]u8, bool->bool, bytesN->[N]u8,
bytes/string->[]const u8, arrays->slices/arrays, multi-output->named
struct. Documented in the module.
- Scope: read functions + event decoders this release; state-changing
writes (via *Wallet) to follow.
Selectors asserted against canonical values (balanceOf 0x70a08231,
transfer 0xa9059cbb, totalSupply 0x18160ddd, allowance 0xdd62ed3e) and
the Transfer topic0; calldata encoding hand-verified. 826/826 tests
pass on 0.16.0. New docs page.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/abigen.zig`:
- Around line 253-256: The decoder currently calls readWord which fabricates
zero-padded words when log.data is too short, causing silent corruption; change
decodeEvent to validate that log.data contains the full expected bytes for each
word before calling readWord (or change readWord to return an error/optional on
out-of-range), and return a decode error instead of using zero-filled values.
Locate the uses at the blocks referencing readWord/eventFieldName/wordToValue
(the snippet around the assignment to `@field`(result, eventFieldName(input.name,
i)) and the similar block at lines ~392-399) and add a length check
(expected_offset + 32 <= log.data.len) for each word (or propagate readWord's
error) so truncated data causes decodeEvent to fail.
- Around line 180-182: selectorOf and topicOf currently call findFunc/findEvent
which `@compileError` on non-generatable shapes; change them to lookup
parsed.funcs / parsed.events directly (avoid findFunc/findEvent) and only error
if the name is missing so callers for "parsed but not callable" entries do not
fail. Preserve ABI tuple component information by modifying parseParam to retain
components (or expose raw param components) and update signatureOf to serialize
tuple types recursively into the canonical component form (and handle arrays of
tuples) before passing to keccak.selector/topic hashing. Ensure changes touch
selectorOf, topicOf, parseParam, signatureOf and remove reliance on
findFunc/findEvent so unsupported-but-parsed entries produce deterministic
selector/topic values rather than compile errors.
- Around line 153-261: The returned contract struct currently only exposes
generic call/decodeEvent APIs (call, decodeEvent, ArgsOf, ReturnOf, EventOf,
selectorOf, topicOf) but does not generate the per-function/per-event
convenience methods advertised in examples; either generate those concrete
members or reduce the public contract surface. Fix by adding comptime loops over
parsed.funcs and parsed.events inside the returned struct to emit pub fn
<sanitizedFuncName>(self, provider:*, comptime name:[], args: ArgsOf(name)) ->
ReturnOf(name) wrappers that forward to call/selectors and pub fn
decode<SanitizedEventName>(comptime name:[], log: Log) -> EventOf(name) wrappers
that forward to decodeEvent/topicOf; ensure you sanitize ABI names to valid Zig
identifiers and reuse existing helpers (ArgsOf, ReturnOf, selectorOf, topicOf,
decodeEvent, EventOf) when generating those methods.
- Around line 245-250: The code currently rejects indexed dynamic types because
eventStruct, isEventGeneratable, and decodeEvent always treat ABI types directly
and pass indexed topics through wordToValue; update the logic so that when an
input is indexed and its ABI type is dynamic (string/bytes) you map it to a
fixed 32-byte topic representation (e.g., TopicHash or [u8;32]) instead of the
original dynamic type: change mapType usage in eventStruct generation and the
checks in isEventGeneratable to accept dynamic indexed inputs by mapping them to
the 32-byte topic type, and alter decodeEvent/path that handles indexed inputs
(where wordToValue is called) to return the raw 32-byte topic for dynamic types
(or call a helper like topicToBytes) while keeping existing behavior for static
indexed types; update references: mapType, eventFieldName, wordToValue,
decodeEvent, isEventGeneratable, and eventStruct to reflect this mapping change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a9613286-acb0-48bb-9579-9a04a56f6fe4
📒 Files selected for processing (5)
CHANGELOG.mddocs/content/docs/abigen.mdxdocs/content/docs/meta.jsonsrc/abigen.zigsrc/root.zig
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/content/docs/abigen.mdx`:
- Line 101: Update the broken link text "`[`abi_json`](/docs/modules)`" to point
to the correct route by changing the href from "/docs/modules" to "/modules"
(i.e., replace the `/docs/modules` URL in the `[`abi_json`](/docs/modules)` link
in abigen.mdx so it becomes `[`abi_json`](/modules)`), ensuring consistency with
other links like `href="/modules"` and "Browse [Modules](/modules)".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 33a27cf2-a42a-4a86-9e25-490127046063
📒 Files selected for processing (5)
CHANGELOG.mddocs/content/docs/abigen.mdxdocs/content/docs/meta.jsonsrc/abigen.zigsrc/root.zig
✅ Files skipped from review due to trivial changes (1)
- docs/content/docs/meta.json
🚧 Files skipped from review as they are similar to previous changes (2)
- src/root.zig
- src/abigen.zig
…uncated event data
- Module doc: correct the usage example to the real generic call API
(weth.call(&provider, "balanceOf", .{holder}) / decodeEvent("Transfer",
log)) and explain why Zig can't mint named methods from a comptime
string. The advertised weth.balanceOf(...)/decodeTransfer(...) members
never existed.
- Indexed reference-type event params (string/bytes/array/tuple) now
decode to their raw 32-byte topic (keccak256(value)) instead of
compile-erroring; isEventGeneratable only excludes NON-indexed
dynamic params now. Added a test.
- decodeEvent rejects truncated log.data (error.TruncatedData) instead
of fabricating a zero-padded word from an under-length RPC payload.
Added a test.
- Fixed the /modules docs link.
828/828 tests pass on 0.16.0.
The flagship Zig feature
A JSON ABI becomes a fully-typed contract struct at compile time -- selectors and event topics precomputed, typed read methods, typed event decoders, zero runtime ABI parsing, no codegen step. This is the thing Zig can do that Rust can't: alloy needs a proc-macro + build step; eth.zig does it in-language at comptime.
API
abigen.Bind(comptime abi_json) type(aliasedeth.bind) returns a struct with:at(address),selectorOf/topicOf,ArgsOf/ReturnOf, a typed read method per ABI function (ABI-encodesselector ++ args,eth_callvia*Provider, decodes into the mapped return type), anddecodeEventfor typed event decoding.How it parses at comptime
std.jsonneeds a runtime allocator and can't run at comptime, so this uses a comptime JSON tokenizer that scans the@embedFilestring and builds comptime-known structures. (Verified the approach before building.)ABI -> Zig type mapping
uintN->smallest fitting uint (uint8->u8,uint256->u256),intN->iN,address->[20]u8,bool->bool,bytesN->[N]u8,bytes/string->[]const u8, arrays->slices/arrays, multi-output->a named struct. Documented in the module.Scope
Read functions + event decoders this release; state-changing writes (via
*Wallet) are the documented follow-up, per the issue's decomposition.Verification
Selectors asserted against canonical values (balanceOf
0x70a08231, transfer0xa9059cbb, totalSupply0x18160ddd, allowance0xdd62ed3e) and the Transfertopic0; calldata encoding hand-verified (selector + padded address). 826/826 tests pass on 0.16.0, fmt clean, docs build. Newabigen.mdxdocs page.Closes #68
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bindand exportedbindalias; public event decoding errors exposed.Documentation
Tests