Skip to content

[FEATURE] improve getDecodedPublicEvents #20332

@wei3erHase

Description

@wei3erHase

Problem Statement

Problem

The current getDecodedPublicEvents only accepts (node, eventMetadata, fromBlock, limit), forcing callers to filter by block range alone. The underlying node.getPublicLogs already supports richer filtering via LogFilter (txHash, contractAddress, fromBlock, toBlock, afterLog), but none of those fields are passed through.

This means common patterns like "get Transfer events from this tx on this contract" require callers to drop down to raw log parsing, defeating the purpose of the helper.

On the private side, wallet.getPrivateEvents already accepts a rich filter with contractAddress, txHash, fromBlock, toBlock. This proposal brings public events to parity.

Proposed Solution

Proposed Signature

Overloaded to preserve backward compatibility with existing callers:

// New: accepts the full LogFilter
function getDecodedPublicEvents<T>(
  node: AztecNode,
  eventMetadata: EventMetadataDefinition,
  filter: LogFilter,
): Promise<T[]>

// Legacy (unchanged): convenience wrapper that builds { fromBlock, toBlock }
function getDecodedPublicEvents<T>(
  node: AztecNode,
  eventMetadata: EventMetadataDefinition,
  from: number,
  limit: number,
): Promise<T[]>

No new types -- LogFilter from @aztec/stdlib is reused directly, since the fields and semantics are identical.

Behaviour

  1. Pass the LogFilter to node.getPublicLogs(filter) as-is.
  2. For each returned log, match the event selector (last emitted field) against eventMetadata.eventSelector. Skip non-matching logs silently.
  3. For matching logs, assert log.fields.length === eventMetadata.fieldNames.length + 1. Throw on mismatch -- a matching selector with wrong payload length indicates ABI drift, not a benign skip.
  4. Decode via decodeFromAbi([eventMetadata.abiType], log.fields).
  5. Return the decoded array typed as T[].

Pagination

The function returns one page of results. When node.getPublicLogs signals maxLogsHit, the caller is responsible for paginating using afterLog set to the last returned log's LogId. This keeps the function simple and predictable -- callers that need exhaustive iteration can wrap it in a loop.

Example Use Case

Usage Examples

By transaction + contract (most common in tests):

const events = await getDecodedPublicEvents<Transfer>(
  node,
  TokenContract.events.Transfer,
  { txHash: receipt.txHash, contractAddress: token.address },
);

By block range (indexer-style):

const events = await getDecodedPublicEvents<Transfer>(
  node,
  TokenContract.events.Transfer,
  { fromBlock: 100, toBlock: 110 },
);

Legacy call sites (no changes needed):

const events = await getDecodedPublicEvents<Transfer>(
  node,
  TokenContract.events.Transfer,
  receipt.blockNumber!,
  1,
);

Alternative Solutions

No response

Additional Context

Current vs Proposed

Capability Current Proposed
Filter by block range yes yes
Filter by txHash no yes
Filter by contractAddress no yes
Pagination (afterLog) no yes
Silent skip on selector mismatch yes yes
Throw on field length mismatch yes yes
Backward compatible n/a yes (overload)

Scope

This spec covers the standalone getDecodedPublicEvents function only. A follow-up could add wallet.getPublicEvents(metadata, filter) for symmetry with wallet.getPrivateEvents, so callers don't need to reach for the raw node reference.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-feature-requestType: Adding a brand new feature (not to be confused with improving an existing feature).from-communityThis originated from the community :)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions