Track 6 · dApps & frontend · lesson 6

Listening to events

10 min


Track 4 taught you to emit events. Track 1 said history lives in logs, not storage. This lesson is the other end of that: reading those events from a frontend to keep a UI honest.

A dApp has two jobs with events: show what already happened (history), and react to what happens while the user watches (live updates). viem does both, and both read from the logs your contracts emit.

Past events

const logs = await client.getLogs({
  address: token,
  event: parseAbiItem(
    "event Transfer(address indexed from, address indexed to, uint256 value)",
  ),
  args: { to: userAddress },   // only transfers TO this user
  fromBlock: "earliest",
});

args filters on the indexed parameters — the reason to was marked indexed back in track 4. Filtering on a non-indexed field is not possible at the node level; you would have to fetch everything and filter in JavaScript.

Live events

const unwatch = client.watchContractEvent({
  address: token,
  abi: erc20Abi,
  eventName: "Transfer",
  onLogs: (logs) => {
    for (const log of logs) refreshRow(log.args);
  },
});
// call unwatch() when the component unmounts

This is how a UI updates the instant something happens on-chain, without the user reloading.

Predict

You want to show a user only the NFTs sent to them, efficiently. What made that query cheap?

Choose one answer

When events are not enough

For anything beyond a simple query — totals, joins across contracts, ordering by computed fields — teams run an indexer (The Graph, Ponder, or a custom one) that ingests events into a database the frontend queries with GraphQL or SQL.

The rule of thumb: direct getLogs for simple, recent history; an indexer when you need to ask real questions of a lot of data.

Check

Why can't you filter `getLogs` on a non-indexed event parameter at the node?

Choose one answer

Worth remembering

  • A dApp reads events for history (getLogs) and live updates (watchContractEvent).
  • You can filter cheaply only on indexed parameters — the topics from track 4.
  • Non-indexed values live in the data blob and can't be filtered at the node.
  • Which parameters you index determines what the frontend can query efficiently.
  • For complex queries over lots of data, run an indexer rather than raw getLogs.