Skip to content

feat: Universal Resolver migration with full ENSIP-15 normalization - #113

Open
koko1123 wants to merge 13 commits into
mainfrom
koko/ens-universal-resolver-ensip15
Open

feat: Universal Resolver migration with full ENSIP-15 normalization#113
koko1123 wants to merge 13 commits into
mainfrom
koko/ens-universal-resolver-ensip15

Conversation

@koko1123

@koko1123 koko1123 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Full in-tree ENSIP-15 name normalization, ported from adraffy/go-ens-normalize (MIT, pinned 165fa80): tokenization, NFC, script groups, whole-script confusables, emoji sequences. Validated against the complete official conformance vectors (ens-normalize 1.11.1, Unicode 17: 38,613 ENSIP-15 + 40,068 NF cases) in CI via zig build vector-test.
  • All ENS entry points now normalize first, then derive namehash AND the ENSIP-10 DNS encoding from the same normalized string.
  • Forward resolution (resolve, getText, new getContentHash) migrated to the Universal Resolver (0xeEeEEEeE14D718C2B47D9923Deab1335E144EeEe, mainnet + Sepolia) via resolve(bytes,bytes), with UR custom-error reverts mapped to null / typed errors (selectors verified with cast sig).
  • Reverse resolution migrated to UR reverse(bytes,uint256) with on-chain forward verification; returned primary names are normalized before being handed to callers.
  • EIP-1577 contenthash decoding (ipfs/ipns/swarm). Note: dag-pb/sha2-256 contenthashes render as CIDv0 (Qm...) per EIP-1577's canonical example; see the docs for the cross-library string-compat caveat.
  • Provider lastError() now captures JSON-RPC revert data so contract custom errors can be decoded.
  • Removes legacy ens_registry chain metadata; zero new external dependencies (spec tables are 36 KB of vendored MIT-licensed data).

Testing

  • Official conformance suites wired into CI on both OSes (zig build vector-test).
  • Unit coverage for dnsEncode, UR calldata layout, revert-selector mapping, contenthash vectors (EIP-1577 + @ensdomains/content-hash sources), reverse tuple decode, and case-folding equivalence.
  • Mainnet integration tests gated on ETH_RPC_URL (skip when unset); fixtures independently verified against mainnet via cast.

Not included (follow-ups)

  • EIP-3668 CCIP-read: offchain-gateway names surface error.OffchainLookupRequired. Follow-up issue to be filed on merge.
  • ENSIP-9/11 multicoin addr, beautify(), comptime table decode, tokenize() introspection.

Closes #110. Supersedes #109 -- thanks @yashgo0018 for the report and the initial PR.

Summary by CodeRabbit

  • New Features

    • Added ENSIP-15 name normalization and DNS encoding.
    • Added Universal Resolver support for forward, reverse, text, and content-hash lookups.
    • Added IPFS, IPNS, and Swarm content-hash decoding.
    • Added CCIP-Read support and clearer ENS resolution errors.
  • Documentation

    • Expanded ENS guides with normalization, resolution, reverse lookup, content hashes, and error handling examples.
    • Updated wallet initialization examples.
  • Tests

    • Added ENS and Unicode conformance vectors.
    • Added integration coverage for ENS resolution and CCIP-Read scenarios.

koko1123 added 12 commits July 22, 2026 10:01
From adraffy/go-ens-normalize @ 165fa80 (MIT). spec.bin/nf.bin are the
compressed ENSIP-15 and Unicode NF tables (36 KB total, shipped in the
package); the JSON vectors live under tests/ and are not packaged.
Port of go-ens-normalize util/ (decoder.go, runeset.go) at 165fa80.

Both hand-computed test buffers in the brief (0x09, 0x83) were verified
bit-by-bit against decoder.go's LSB-first readBit/readUnary/readBinary
and are correct as written; no test corrections were needed.

Fixed one Zig-specific memory-safety issue not present in the Go
source: a literal translation of readUnique would wrap the
errdefer-tracked sorted-ascending prefix in an ArrayList via
fromOwnedSlice and grow it in place, so an allocation failure mid-append
could free that buffer twice (once via the list's own errdefer, once
via the outer errdefer on the original slice). Reworked to build the
appended ranges in a separate buffer and copy once at the end; added a
regression test that exercises the append path end to end.
Port of go-ens-normalize nf/nf.go with embedded nf.bin tables.
Port of go-ens-normalize ensip15/ at 165fa80: tokenization, NFC,
script groups, whole-script confusables, emoji sequences, and the
label validation rules. Public API: ens_normalize.normalize.
zig build vector-test runs the full upstream suites (ens-normalize
1.11.1, Unicode 17) in CI.
An unconditional stderr summary made the Zig build runner print a
"failed command" trailer on successful vector-test runs. Print
diagnostics only when failures occur; the expectEqual assertion still
fails the build on any mismatch.
lastError() now exposes the error.data hex payload so callers can
decode contract custom errors (needed for Universal Resolver).
resolve/getText/getContentHash now normalize per ENSIP-15, then build
namehash and ENSIP-10 dnsEncode from the same normalized string and
call resolve(bytes,bytes) on the UR (same address on mainnet and
Sepolia). UR custom-error reverts map to null / OffchainLookupRequired.
Adds EIP-1577 contenthash decoding (ipfs/ipns/swarm).
reverse(bytes,uint256) with on-chain forward verification replaces the
namehash-of-addr.reverse registry flow. Removes the now-unused
ens_registry chain metadata.
Integration tests are gated on ETH_RPC_URL and skip when unset.
Final-review cleanup: NoResolver was declared but never produced (the
no-resolver case returns null) and its docs contradicted the null
contract; parseSelector existed verbatim in resolver.zig and
reverse.zig; provider revert-data buffer comment and the ens docs
normalization-timing claim were imprecise.
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
eth-zig Ready Ready Preview Aug 2, 2026 6:23pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds ENSIP-15 normalization, ENSIP-10 Universal Resolver support, DNS encoding, EIP-1577 contenthash decoding, reverse lookup verification, provider revert-data capture, conformance tests, integration tests, and updated ENS documentation.

Changes

ENS resolution support

Layer / File(s) Summary
ENSIP-15 normalization and conformance
src/ens/normalize/*, tests/ens_vectors_test.zig, tests/data/ens-normalize/*, build.zig, .github/workflows/ci.yml, Makefile
Adds Unicode normalization tables, ENSIP-15 validation, public normalization APIs, conformance fixtures, and a vector-test build step used by CI and Make.
ENS wire encoding and contenthash decoding
src/ens/namehash.zig, src/ens/contenthash.zig
Adds DNS-wire encoding and EIP-1577 decoding for IPFS, IPNS, and Swarm content hashes.
Universal Resolver and reverse lookup flow
src/ens/resolver.zig, src/ens/reverse.zig, src/provider.zig
Uses normalized names with Universal Resolver forward and reverse profiles. Maps resolver errors, supports OffchainLookupRequired, verifies reverse records, and preserves raw provider revert data.
Public exports, chain metadata, integration coverage, and documentation
src/root.zig, src/chains/*, tests/integration_tests.zig, README.md, docs/content/docs/*, examples/04_send_transaction.zig
Exports the new ENS modules, removes ENS registry chain metadata, updates wallet setup, adds live ENS integration tests, and documents the new resolution behavior and errors.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant ENSResolver
  participant Provider
  participant UniversalResolver
  Caller->>ENSResolver: normalize name
  ENSResolver->>ENSResolver: namehash and DNS-encode normalized name
  ENSResolver->>Provider: call Universal Resolver
  Provider->>UniversalResolver: resolve(bytes,bytes)
  UniversalResolver-->>Provider: response or revert data
  Provider-->>ENSResolver: result and ErrorInfo.data
  ENSResolver-->>Caller: record, null, or ResolveError
Loading

Possibly related PRs

  • StrobeLabs/eth.zig#109: Earlier Universal Resolver migration affecting the same resolver, chain metadata, DNS encoding, and reverse-resolution paths.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes unrelated contenthash, provider revert capture, chain metadata removal, and wallet-example changes beyond linked issue #110. Split unrelated changes into separate pull requests, or link issues that define their requirements.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the PR's main changes: Universal Resolver migration and ENSIP-15 normalization.
Linked Issues check ✅ Passed The PR satisfies issue #110 by normalizing names before hashing and DNS encoding, adding regression tests, and documenting invalid-name handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch koko/ens-universal-resolver-ensip15

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (5)
tests/ens_vectors_test.zig (1)

71-76: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Guard the case-array length before indexing.

v[0], v[1], and v[2] assume every case holds at least three elements. A refreshed nf-tests.json with a shorter row causes an index-out-of-bounds panic, and the panic does not identify the offending group or row. A length check turns that into a readable test failure.

♻️ Proposed guard
         for (entry.value_ptr.array.items) |case| {
             const v = case.array.items;
+            if (v.len < 3) {
+                fail += 1;
+                std.debug.print("malformed NF case in {s}: {d} fields\n", .{ entry.key_ptr.*, v.len });
+                continue;
+            }
             const input = try utf8ToCps(allocator, v[0].string);
🤖 Prompt for 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.

In `@tests/ens_vectors_test.zig` around lines 71 - 76, In the test loop over
cases, validate that case.array.items contains at least three elements before
assigning v[0], v[1], or v[2]. Turn insufficient rows into a readable test
failure that identifies the current group and row, then continue indexing only
after the check passes.
src/ens/normalize/nf.zig (1)

159-195: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Free the pending row when the map insertion fails.

one at Line 165 and pair at Line 185 are allocated before decomps.put. If put returns error.OutOfMemory, the slice is not yet a map value, so freeDecomps in the errdefer cannot free it. The result is a leaked row on the allocation-failure path. The same allocator is used for both, so an errdefer inside each loop body closes the gap.

🛡️ Proposed fix for the OOM path
             for (decomp1, 0..) |cp_raw, i| {
                 const one = try allocator.alloc(u21, 1);
+                errdefer allocator.free(one);
                 one[0] = `@intCast`(decomp1a[i]);
                 try decomps.put(allocator, `@intCast`(cp_raw), one);
             }
                 const pair = try allocator.alloc(u21, 2);
+                errdefer allocator.free(pair);
                 pair[0] = cp_b;
                 pair[1] = cp_a;
                 try decomps.put(allocator, cp, pair);
🤖 Prompt for 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.

In `@src/ens/normalize/nf.zig` around lines 159 - 195, Add an errdefer immediately
after allocating each pending decomposition row in the single-codepoint loop and
the two-codepoint loop, covering one and pair respectively, so failed
decomps.put calls free the allocation while successful insertion transfers
ownership to decomps.
src/ens/normalize/ensip15.zig (2)

886-897: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a negative-path unit test in this module.

The tests in src/ens/normalize.zig cover rejection cases, and tests/ens_vectors_test.zig covers the official vectors. This file has only one positive test. A single confusable or illegal-mixture case here keeps the core module self-checking when the public wrapper changes, and it exercises checkWhole and determineGroup, which no test in this module reaches today.

🤖 Prompt for 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.

In `@src/ens/normalize/ensip15.zig` around lines 886 - 897, Add a negative-path
unit test alongside the existing normalize test in this module, using a
confusable or illegal mixed-script name that normalize rejects. Assert the call
returns the expected NormalizeError, exercising the internal checkWhole and
determineGroup validation path while leaving the existing positive test
unchanged.

860-884: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Replace the busy spin with std.Io.Mutex.

Ensip15.init can take a long time. Use a module-level std.Io.Mutex with an atomic ready fast path. Re-check ready after lockUncancelable(runtime.blockingIo()), initialize under the lock, publish tables, and then release the lock. std.Thread.Futex is not required; a futex implementation would also need a 32-bit wait word instead of the current u8 atomic.

🤖 Prompt for 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.

In `@src/ens/normalize/ensip15.zig` around lines 860 - 884, Replace the
initializing-state CAS and spin loop in getTables with a module-level
std.Io.Mutex and an atomic ready fast path. When not ready, acquire the mutex
via lockUncancelable(runtime.blockingIo()), re-check readiness after locking,
initialize Ensip15 only if still needed, publish tables, mark ready with release
ordering, and unlock; preserve the existing page_allocator initialization and
catch behavior.
src/ens/normalize.zig (1)

23-25: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider limiting the public surface for the NF re-export.

testing_nf exposes the internal NF implementation through the published eth.ens_normalize API. Only the vector suite needs it. The name marks the intent, but consumers can still depend on it, and later table refactors then become breaking changes. An alternative is to import src/ens/normalize/nf.zig directly from tests/ens_vectors_test.zig through a dedicated build module, and keep normalize.zig limited to NormalizeError and normalize.

🤖 Prompt for 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.

In `@src/ens/normalize.zig` around lines 23 - 25, Remove the public testing_nf
re-export from normalize.zig and update the vector suite to import
normalize/nf.zig directly through its dedicated build module. Keep
normalize.zig’s public surface limited to NormalizeError and normalize, while
preserving the vector tests’ direct access to the NF implementation.
🤖 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/ens/contenthash.zig`:
- Around line 125-133: Update decodeIpns to validate the payload as a CID before
calling baseNEncode, matching the validation performed by the IPFS and Swarm
decoding paths; reject empty or otherwise invalid payloads instead of
constructing an unresolved ipns://k URI.

In `@src/ens/normalize/data/NOTICE`:
- Around line 1-7: Update src/ens/normalize/data/NOTICE to attribute
adraffy/go-ens-normalize as Copyright (c) 2024 Andrew Raffensperger, and add the
complete upstream MIT license text. Place the license text in an appropriate
repository license file since no aggregate third-party license file exists,
while preserving the existing provenance details.

In `@src/ens/normalize/decoder.zig`:
- Around line 109-140: Update the documentation for readUnsortedDeltas to remove
the claim that intermediate sums may be negative and state that decoded
accumulated values must remain non-negative for valid input. Keep the existing
i64 accumulator implementation in readArray unchanged.

In `@src/ens/resolver.zig`:
- Around line 170-172: Update the catch block around provider.call in the
resolver flow to inspect the caught error and call mapRevert(provider) only when
it is error.RpcError; propagate all other errors directly so allocation failures
cannot consume stale provider.lastError() data.

In `@src/ens/reverse.zig`:
- Around line 156-159: Update the encodeReverseTuple documentation and parameter
names to match the UR tuple order: primary name, forward resolver address, and
reverseResolver address. Rename the resolved parameter and its corresponding
AbiValue usage without changing tuple encoding behavior or lookupAddress.
- Around line 89-92: Update the primary-name handling after decoding in the
reverse-resolution function: normalize the decoded name only for comparison,
return null when the normalized form differs from the stored name, and return
the original name when they match. Update the test covering “Vitalik.ETH” to
expect null instead of the rewritten lowercase name.

In `@tests/integration_tests.zig`:
- Line 196: Update the private-key wallet initialization calls in README.md,
docs/content/docs/, and examples/04_send_transaction.zig to use
eth.wallet.Wallet.initLocal rather than Wallet.init; preserve Wallet.init for
signer_mod.Signer inputs, and leave the already-correct
tests/integration_tests.zig call unchanged.

---

Nitpick comments:
In `@src/ens/normalize.zig`:
- Around line 23-25: Remove the public testing_nf re-export from normalize.zig
and update the vector suite to import normalize/nf.zig directly through its
dedicated build module. Keep normalize.zig’s public surface limited to
NormalizeError and normalize, while preserving the vector tests’ direct access
to the NF implementation.

In `@src/ens/normalize/ensip15.zig`:
- Around line 886-897: Add a negative-path unit test alongside the existing
normalize test in this module, using a confusable or illegal mixed-script name
that normalize rejects. Assert the call returns the expected NormalizeError,
exercising the internal checkWhole and determineGroup validation path while
leaving the existing positive test unchanged.
- Around line 860-884: Replace the initializing-state CAS and spin loop in
getTables with a module-level std.Io.Mutex and an atomic ready fast path. When
not ready, acquire the mutex via lockUncancelable(runtime.blockingIo()),
re-check readiness after locking, initialize Ensip15 only if still needed,
publish tables, mark ready with release ordering, and unlock; preserve the
existing page_allocator initialization and catch behavior.

In `@src/ens/normalize/nf.zig`:
- Around line 159-195: Add an errdefer immediately after allocating each pending
decomposition row in the single-codepoint loop and the two-codepoint loop,
covering one and pair respectively, so failed decomps.put calls free the
allocation while successful insertion transfers ownership to decomps.

In `@tests/ens_vectors_test.zig`:
- Around line 71-76: In the test loop over cases, validate that case.array.items
contains at least three elements before assigning v[0], v[1], or v[2]. Turn
insufficient rows into a readable test failure that identifies the current group
and row, then continue indexing only after the check passes.
🪄 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 Plus

Run ID: 7cf36218-7bb2-4cb5-bda4-8458dd738df7

📥 Commits

Reviewing files that changed from the base of the PR and between 9da2547 and ca4d834.

⛔ Files ignored due to path filters (2)
  • src/ens/normalize/data/nf.bin is excluded by !**/*.bin
  • src/ens/normalize/data/spec.bin is excluded by !**/*.bin
📒 Files selected for processing (28)
  • .github/workflows/ci.yml
  • Makefile
  • README.md
  • build.zig
  • docs/content/docs/ens.mdx
  • docs/content/docs/modules.mdx
  • src/chains/arbitrum.zig
  • src/chains/base.zig
  • src/chains/chain.zig
  • src/chains/ethereum.zig
  • src/chains/optimism.zig
  • src/chains/polygon.zig
  • src/ens/contenthash.zig
  • src/ens/namehash.zig
  • src/ens/normalize.zig
  • src/ens/normalize/data/NOTICE
  • src/ens/normalize/decoder.zig
  • src/ens/normalize/ensip15.zig
  • src/ens/normalize/nf.zig
  • src/ens/normalize/rune_set.zig
  • src/ens/resolver.zig
  • src/ens/reverse.zig
  • src/provider.zig
  • src/root.zig
  • tests/data/ens-normalize/nf-tests.json
  • tests/data/ens-normalize/tests.json
  • tests/ens_vectors_test.zig
  • tests/integration_tests.zig
💤 Files with no reviewable changes (6)
  • src/chains/base.zig
  • src/chains/optimism.zig
  • src/chains/polygon.zig
  • src/chains/arbitrum.zig
  • src/chains/ethereum.zig
  • src/chains/chain.zig

Comment thread src/ens/contenthash.zig
Comment thread src/ens/normalize/data/NOTICE
Comment thread src/ens/normalize/decoder.zig
Comment thread src/ens/resolver.zig Outdated
Comment thread src/ens/reverse.zig Outdated
Comment thread src/ens/reverse.zig Outdated
Comment thread tests/integration_tests.zig
Strict-reject unnormalized primary names in reverse resolution (an
unnormalized stored primary is treated as no-primary-set rather than
rewritten to a name the UR never verified); consult lastError only for
RpcError reverts; validate ipns contenthash payloads; vendor the
upstream MIT license text with corrected attribution; fix decoder doc
precision and reverse test-helper naming; migrate remaining raw-key
Wallet.init callers in docs and examples to initLocal.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ens/resolver.zig (1)

198-207: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate custom-error payloads before mapping selectors.

mapRevert maps selector-only ResolverNotFound and ResolverError payloads to null, and maps 0x556f1830deadbeef to error.OffchainLookupRequired. These payloads omit required ABI arguments. Decode and validate every recognized error payload, return error.ProviderError on failure, and update the fixtures.

🤖 Prompt for 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.

In `@src/ens/resolver.zig` around lines 198 - 207, Update mapRevert to ABI-decode
and validate the payload for every recognized selector before returning its
mapped result, including required arguments for ResolverNotFound, ResolverError,
and OffchainLookup; return error.ProviderError when decoding or validation
fails. Preserve the existing mappings only for valid payloads, and update the
related fixtures to use complete valid error payloads plus invalid
selector-only/malformed cases.
🤖 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/ens/resolver.zig`:
- Around line 170-176: Update the error handling around provider.call in the
resolver flow so mapRevert(provider) is invoked only when e is error.RpcError
and provider.lastError().code equals 3. Return error.OutOfMemory unchanged, map
other RPC errors to error.ProviderError, and preserve the existing fallback for
non-RPC failures.

---

Outside diff comments:
In `@src/ens/resolver.zig`:
- Around line 198-207: Update mapRevert to ABI-decode and validate the payload
for every recognized selector before returning its mapped result, including
required arguments for ResolverNotFound, ResolverError, and OffchainLookup;
return error.ProviderError when decoding or validation fails. Preserve the
existing mappings only for valid payloads, and update the related fixtures to
use complete valid error payloads plus invalid selector-only/malformed cases.
🪄 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 Plus

Run ID: fe091f4f-07a7-4ba2-b257-c3687c9aeb93

📥 Commits

Reviewing files that changed from the base of the PR and between ca4d834 and 1161403.

📒 Files selected for processing (11)
  • README.md
  • docs/content/docs/examples.mdx
  • docs/content/docs/introduction.mdx
  • docs/content/docs/nonce-manager.mdx
  • docs/content/docs/transactions.mdx
  • examples/04_send_transaction.zig
  • src/ens/contenthash.zig
  • src/ens/normalize/data/NOTICE
  • src/ens/normalize/decoder.zig
  • src/ens/resolver.zig
  • src/ens/reverse.zig
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/ens/normalize/data/NOTICE
  • README.md
  • src/ens/contenthash.zig
  • src/ens/normalize/decoder.zig
  • src/ens/reverse.zig

Comment thread src/ens/resolver.zig
Comment on lines +170 to +176
const response = provider.call(UNIVERSAL_RESOLVER, outer) catch |e| {
// Only a revert carries diagnostics; any other failure (including an
// allocation failure) must not read a stale `lastError()` from an
// earlier call.
if (e == error.OutOfMemory) return error.OutOfMemory;
if (e == error.RpcError) return mapRevert(provider);
return error.ProviderError;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/sh
set -eu
rg -n -C 6 'pub const ErrorInfo|pub fn lastError|error\.RpcError|captureRpcError|code.*3|execution reverted' src/provider.zig

Repository: StrobeLabs/eth.zig

Length of output: 10346


🏁 Script executed:

#!/bin/sh
set -eu
printf '%s\n' '--- resolver symbols ---'
rg -n -C 12 'mapRevert|provider\.call|OffchainLookupRequired|lastError' src/ens/resolver.zig
printf '%s\n' '--- provider call and error capture ---'
rg -n -C 14 'pub fn call|fn call|last_error|captureRpcError|rpcCall|error\.RpcError' src/provider.zig

Repository: StrobeLabs/eth.zig

Length of output: 36076


Check lastError().code == 3 before calling mapRevert. error.RpcError covers every JSON-RPC error, not only execution reverts. Otherwise, mapRevert can interpret server-error data as resolver revert data.

🤖 Prompt for 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.

In `@src/ens/resolver.zig` around lines 170 - 176, Update the error handling
around provider.call in the resolver flow so mapRevert(provider) is invoked only
when e is error.RpcError and provider.lastError().code equals 3. Return
error.OutOfMemory unchanged, map other RPC errors to error.ProviderError, and
preserve the existing fallback for non-RPC failures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ENS: add ENSIP-15 name normalization before namehash / Universal Resolver calls

1 participant