Skip to content

encodeEventTopics() (and watchLogs()) do not handle anonymous events #4461

@KartikSoneji

Description

@KartikSoneji

Check existing issues

Viem Version

2.47.10

Current Behavior

encodeEventTopics() does not handle anonymous events properly.
It incorrectly uses the signature as the first log topic.

Expected Behavior

To correctly encode the log topics.

Steps To Reproduce

const rawEventAbiItem = [
  {
    name: 'RawEvent',
    type: 'event',
    anonymous: true,
    inputs: [
      { name: 'topic0', type: 'bytes32', indexed: true },
      { name: 'topic1', type: 'bytes32', indexed: true },
      { name: 'topic2', type: 'bytes32', indexed: true },
      { name: 'topic3', type: 'bytes32', indexed: true },
    ],
  },
] as const

test('anonymous event: no args', () => {
  expect(encodeEventTopics({ abi: rawEventAbiItem })).toEqual([])
})

test('anonymous event: topic0', () => {
  expect(
    encodeEventTopics({
      abi: rawEventAbiItem,
      args: {
        topic0:
          '0x000000000000000000000000000000000000000000000000000000000000abcd',
      },
    }),
  ).toEqual([
    '0x000000000000000000000000000000000000000000000000000000000000abcd',
    null,
    null,
    null,
  ])
})

test('anonymous event: all topics', () => {
  expect(
    encodeEventTopics({
      abi: rawEventAbiItem,
      args: {
        topic0:
          '0x0000000000000000000000000000000000000000000000000000000000000000',
        topic1:
          '0x0000000000000000000000000000000000000000000000000000000000000001',
        topic2:
          '0x0000000000000000000000000000000000000000000000000000000000000002',
        topic3:
          '0x0000000000000000000000000000000000000000000000000000000000000003',
      },
    }),
  ).toEqual([
    '0x0000000000000000000000000000000000000000000000000000000000000000',
    '0x0000000000000000000000000000000000000000000000000000000000000001',
    '0x0000000000000000000000000000000000000000000000000000000000000002',
    '0x0000000000000000000000000000000000000000000000000000000000000003',
  ])
})

Link to Minimal Reproducible Example

No response

Anything else?

I came across this issue while trying to find a workaround to specify the log topics directly.

encodeEventTopics() does not check abiItem.anonymous before setting the sigature as topic0.

return [signature, ...topics]

The logic itself is trivial to fix:

diff --git a/src/utils/abi/encodeEventTopics.ts b/src/utils/abi/encodeEventTopics.ts
index 21415b5..c1c8a29 100644
--- a/src/utils/abi/encodeEventTopics.ts
+++ b/src/utils/abi/encodeEventTopics.ts
@@ -94,9 +94,6 @@ export function encodeEventTopics<
   if (abiItem.type !== 'event')
     throw new AbiEventNotFoundError(undefined, { docsPath })
 
-  const definition = formatAbiItem(abiItem)
-  const signature = toEventSelector(definition as EventDefinition)
-
   let topics: (Hex | Hex[] | null)[] = []
   if (args && 'inputs' in abiItem) {
     const indexedInputs = abiItem.inputs?.filter(
@@ -121,7 +118,15 @@ export function encodeEventTopics<
         }) ?? []
     }
   }
-  return [signature, ...topics]
+
+  // only prepend signature for non-anonymous events
+  if (!abiItem.anonymous) {
+    const definition = formatAbiItem(abiItem)
+    const signature = toEventSelector(definition as EventDefinition)
+    topics.unshift(signature)
+  }
+
+  return topics
 }
 
 export type EncodeArgErrorType =

But a bigger issue is the return type being [Hex, ...(Hex | Hex[] | null)[]], making an empty array fail the typecheck.
Not sure how that should be handled since changing the return type to [] can break existing code.
Maybe it can be inferred based on the type of event?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions