Skip to content

fix: decode anonymous event logs when requested - #5375

Merged
jxom merged 10 commits into
mainfrom
fix/anonymous-event-opt-in
Nov 2, 2026
Merged

jxom merged 10 commits into
mainfrom
fix/anonymous-event-opt-in

Conversation

@jxom

@jxom jxom commented Nov 2, 2026

Copy link
Copy Markdown
Member

Supersedes #5374 and keeps @mortezashojaei's two commits, with one follow-up commit on top.

encodeEventTopics already leaves out the signature topic for anonymous events. decodeEventLog and parseEventLogs still read topics[0] as the signature, so getLogs and watchEvent dropped anonymous logs. #5374 fixed that.

It also let an anonymous event claim any log whose signature topic is unknown and whose topic count matches. A log carries nothing that identifies an anonymous event. Decoding is a weak check: address and uint topics decode from any 32 bytes, and extra data is ignored. So an ABI that already holds an anonymous event, such as DSNote's LogNote in Maker ABIs, would quietly mislabel unrelated logs from other contracts in a receipt.

This PR matches anonymous events only when they are requested:

  • eventName names the event, or
  • every event in abi is anonymous. This covers getLogs and watchEvent with a single anonymous event, so they need no changes.
const abi = [anonLogNote, transferEvent]

parseEventLogs({ abi, logs: receipt.logs })
// only Transfer logs; LogNote is not considered

parseEventLogs({ abi, eventName: ['LogNote', 'Transfer'], logs: receipt.logs })
// both

decodeEventLog({ abi, topics: [unknownSelector, addr], data })
// throws AbiEventSignatureNotFoundError again, as before #5374

The rest of #5374 is unchanged: a signature match wins, the topic count must match, events with the same number of indexed inputs are never guessed (AbiEventAmbiguityError or the log is dropped), and the non-strict fallback never invents an anonymous event. The follow-up also removes an unreachable if (!abiItem) branch and replaces a nested ternary in parseEventLogs.

Not covered: getLogs({ events: [...] }) with a mix of anonymous and non-anonymous events still can't fetch the anonymous logs. The topic filter only ORs the selectors. That predates this PR, and the changeset now states the single-event scope.

Validation: decodeEventLog, parseEventLogs, encodeEventTopics, and index tests pass (97), except the 3 existing parseEventLogs tests that need anvil, which weren't available locally. tsc -p src/tsconfig.json and Biome are clean.


Generated by Claude Code

mortezashojaei and others added 3 commits November 1, 2026 23:24
`decodeEventLog` and `parseEventLogs` matched an anonymous event to any log
with an unknown signature topic and the same topic count. Since a log carries
nothing that identifies an anonymous event, an ABI that holds one (such as
DSNote's `LogNote`) claimed unrelated logs from other contracts in a receipt.

Anonymous events are now only matched when `eventName` names them, or when
every event in `abi` is anonymous (as with `getLogs` and `watchEvent` given a
single anonymous `event`). A mixed ABI without `eventName` throws
`AbiEventSignatureNotFoundError` for an unknown signature again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3nmWga38V3yjkJET3UZeh
@vercel

vercel Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
viem Ready Ready Preview Nov 2, 2026 1:42am UTC

Request Review

@jxom jxom mentioned this pull request Nov 2, 2026
10 tasks done
@changeset-bot

changeset-bot Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6ceab0e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
viem Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-02T01:44:45.689664Z 6ceab0e New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76e0600643

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/errors/abi.ts Resolved
Comment thread .changeset/anonymous-event-logs.md Resolved
Comment thread src/utils/abi/decodeEventLog.test.ts Resolved
Comment thread src/errors/abi.ts Resolved
Move `AbiEventAmbiguityError` from `errors/abi.ts` to the bottom of
`decodeEventLog.ts` and export it from there. For overloads that share a
name, the error now recommends an `abi` narrowed to one event, since
`eventName` cannot choose between them.

Also shorten the changeset to one sentence, split the anonymous-event
paragraphs in the `decodeEventLog` and `parseEventLogs` docs, and assert
the anonymous-event decoder results with inline snapshots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3nmWga38V3yjkJET3UZeh
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Published viem@0.0.0-pr-5375-6481114 under the pr-5375 dist-tag, which follows this pull request until it closes.

pnpm add viem@pr-5375
pnpm add viem@0.0.0-pr-5375-6481114

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d6a15de8d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/utils/abi/parseEventLogs.ts Resolved
Comment thread src/utils/abi/decodeEventLog.ts Resolved
Comment on lines 188 to +190
if (!event && !strict) {
abiItem = abiItems[0]
const fallback = abiItems[0]
if (!fallback || fallback.abi.anonymous) return null

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retry unique anonymous candidates in non-strict mode

When strict: false is used with a uniquely selected anonymous event whose topics have the expected arity but whose data is missing or nonconforming, the initial strict probe fails and this return drops the log outright. parseEventLogs documents non-strict mode as partially decoding nonconforming logs, and the candidate is already uniquely identified by eventName or an all-anonymous ABI, so it should receive the same strict: false retry as a signature match.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Leaving this as is for now. A non-anonymous log is identified by its signature topic before non-strict decoding runs. An anonymous log has nothing identifying it, so a strict decode is the only check left that it came from the event. getLogs defaults to strict: false, so a non-strict retry here would label any same-arity log from the watched address as the anonymous event, with partial args. This is the false positive this PR removes. I've asked the maintainer to make the call.


Generated by Claude Code

Comment thread src/utils/abi/decodeEventLog.test.ts Resolved
Comment thread src/utils/abi/decodeEventLog.ts Resolved
Comment thread src/utils/abi/decodeEventLog.ts Resolved
TypeScript 7 rejects `encodeEventTopics` parameters without `eventName`
for this ABI literal, which failed `Verify / Types (latest)`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3nmWga38V3yjkJET3UZeh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6a40dbe156

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +167 to +168
return requested.filter(
(item) => item.anonymous && indexedCount(item) === topicCount,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve partial decoding for selected anonymous events

When strict: false is used with a uniquely selected anonymous event whose indexed topics are missing, this exact arity filter rejects the event before decodeMatchedEventLog can apply its existing non-strict handling for missing indexed inputs. For example, an explicit eventName plus one topic for an anonymous event with two indexed parameters now throws AbiEventSignatureNotFoundError instead of returning the partially decoded argument set, unlike the same mismatch on a non-anonymous event. Allow the uniquely identified candidate to reach the non-strict decoder.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Same reasoning as #5375 (comment). For an anonymous event the topic count is the only structural check left, since there's no signature topic. If a log with fewer topics could reach the non-strict decoder, any shorter log from the address would decode as the event, with the missing inputs read from data. A non-anonymous mismatch differs because its signature topic already identifies the event. I've asked the maintainer to make the call.


Generated by Claude Code

`getFilterChanges` and `getFilterLogs` now pass `filter.eventName` to
`parseEventLogs`, and the `watchContractEvent` subscription passes
`eventName` to `decodeEventLog`, so an anonymous event selected from a
mixed ABI decodes. In strict mode, `watchEvent` and `watchContractEvent`
subscriptions skip logs that match no requested event, which a topic filter
cannot exclude for anonymous events.

Also add TSDoc to `AbiEventAmbiguityError`, import public helpers from
`viem` in the `decodeEventLog` tests, and raise the `viem` (esm and cjs) and
`viem/actions` size limits by 1 kB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3nmWga38V3yjkJET3UZeh
Property tests over random ABIs (named and anonymous events, overloads,
integer, bytes, string, tuple, and array inputs, named and unnamed) and
random logs:

- `decodeEventLog` decodes the arguments a log was encoded from.
- `decodeEventLog` matches ox for named non-anonymous events.
- A signature match decodes the same way whatever else the ABI holds.
- `decodeEventLog` returns or throws a Viem error for any log.
- `parseEventLogs` matches a model of its documented matching rules, with
  and without `eventName`, keeps a log whose own indexed values are passed
  as `args`, and does not throw in non-strict mode.

`FUZZ_RUNS` and `FUZZ_SEED` control the runs, as for the Tempo fuzz tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3nmWga38V3yjkJET3UZeh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6481114dba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/actions/public/watchEvent.ts Resolved
Move the `decodeEventLog` properties to `decodeEventLog.fuzz.test.ts` and the
`parseEventLogs` properties to `parseEventLogs.fuzz.test.ts`, beside their
modules. The shared ABI and log generators move to `test/src/abi/eventLogs.ts`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3nmWga38V3yjkJET3UZeh
`watchEvent` (with `event` or `events`) and `watchContractEvent` (with
`eventName`) subscriptions now skip logs that match no requested event in
non-strict mode too, as their polling paths do. A topic filter cannot select
an anonymous event, so the subscription can deliver other logs from the same
address. Watches that name no event still emit every log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3nmWga38V3yjkJET3UZeh
`abitype` does not resolve from `test/src` in CI, which failed
`Verify / Types (latest)`. `src/index.ts` re-exports `AbiEvent` and
`AbiParameter`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3nmWga38V3yjkJET3UZeh
@jxom
jxom merged commit bb12b94 into main Nov 2, 2026
41 checks passed
@jxom
jxom deleted the fix/anonymous-event-opt-in branch November 2, 2026 01:43

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6ceab0ea0a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


return decodeMatchedEventLog({
abiItem,
argTopics: abiItem.anonymous ? topics : topics.slice(1),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Account for anonymous topics in return-type inference

For an anonymous event, this branch correctly treats every topic as indexed data at runtime, but DecodeEventLogParameters still models the first element as a signature and infers the topics generic from only the remaining elements. Consequently, an anonymous event with one indexed argument infers topics as [], making the return type report args?: undefined (or optional when data is supplied) even though this call returns the decoded argument object; consumers therefore cannot safely access the primary result of the new feature. Update the parameter/return inference for anonymous events and cover it in decodeEventLog.test-d.ts.

AGENTS.md reference: AGENTS.md:L70-L76

Useful? React with 👍 / 👎.

const decoded = decodeEventLog({
abi: abi,
data: log.data,
eventName,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Filter signature matches from anonymous subscriptions

When a non-polling watchContractEvent selects an anonymous event from a mixed ABI with no selective indexed arguments, its subscription has no signature topic and therefore also receives ordinary named events. Passing eventName here does not scope signature matches because decodeEventLog deliberately checks non-anonymous selectors first, so a received named event is successfully decoded and emitted instead of being skipped; the polling path filters that same result through parseEventLogs. Verify that decoded.eventName matches the requested name before emitting, or decode against only the selected ABI event.

Useful? React with 👍 / 👎.


A log matches an anonymous event when its topic count equals the event's indexed argument count, and its first topic is not another event's signature.

A log that fits several anonymous events is left out. Pass `eventName` to choose one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Document narrowing for same-name anonymous overloads

When several anonymous overloads share both a name and indexed-topic count, passing that eventName still leaves multiple candidates, so parseEventLogs continues to omit the log even after the reader follows this instruction. As the adjacent decodeEventLog documentation already explains, this case requires narrowing abi to the desired overload; include that qualification here so the documented remedy actually resolves every stated ambiguity.

Useful? React with 👍 / 👎.

This branch was successfully deployed

1 active deployment
Preview — 6ceab0ea Deployed Nov 2, 2026 by vercel[bot]
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.

3 participants