Web3 Builders
Event Logs Explained for Web Developers
How Ethereum event logs work, what topics and data contain, how to filter logs efficiently, and how web developers use them to track transfers and app activity.

Smart contracts cannot send notifications to your web server. What they can do is emit events. Each event creates a log entry stored with the transaction receipt. Web developers use these logs to track token transfers, NFT mints, votes, sales and almost any activity a contract exposes.
What an event is
In Solidity, a contract defines an event and emits it when something happens:
event Transfer(address indexed from, address indexed to, uint256 value);
When a token transfer occurs, the contract emits Transfer with the sender, recipient and amount. The event does not change contract state. It creates a cheap, permanent record that off-chain systems can read.
Anatomy of a log
A log entry contains several fields:
| Field | Meaning |
|---|---|
| address | The contract that emitted the event |
| topics | Up to four indexed values, starting with the event signature |
| data | Non-indexed values, ABI encoded |
| blockNumber | The block containing the transaction |
| transactionHash | The transaction that produced the log |
| logIndex | Position of the log within the block |
| removed | True if the log was removed by a chain reorganisation |
The first topic is the keccak hash of the event signature. For the standard Transfer event, it is the same across all ERC-20 tokens, which makes transfers easy to find.
Indexed and non-indexed values
Parameters marked indexed go into topics, and each is padded to 32 bytes. Addresses appear as 64 hex characters with leading zeros. Non-indexed parameters go into the data field.
For the Transfer event:
- Topic 0 is the event signature hash
- Topic 1 is the sender address, padded
- Topic 2 is the recipient address, padded
- Data holds the amount as a 32-byte number
Because topics are indexed, nodes can filter on them efficiently.

Filtering logs
The eth_getLogs method accepts a filter:
{
"fromBlock": "0x10d4f00",
"toBlock": "0x10d54ff",
"address": "0xTokenContract",
"topics": ["0xddf252ad...", null, "0x000...recipient"]
}
A null in the topics array means any value. This example finds transfers of one token to one recipient from any sender within a block range.
Good filters make queries fast:
- Always set the contract address when you can
- Filter on indexed topics rather than scanning data
- Keep block ranges within your provider's limits
- Track the last block you processed so you never scan twice
Common uses
Developers use logs for many tasks:
- Detecting incoming payments to an address
- Showing a user's activity history in an app
- Building leaderboards from game or trading events
- Triggering notifications when a contract state changes
- Indexing NFT mints and sales
For large histories, dedicated indexing services store logs in databases you can query quickly. For small apps, direct eth_getLogs queries often suffice.
Reorgs and the removed flag
Recent blocks can be replaced during a chain reorganisation. Logs from replaced blocks may be marked removed or disappear from later queries. Handle this by waiting for confirmations before acting on important events, and by ignoring logs flagged as removed.
Beware of fake events
Any contract can emit an event with the Transfer signature. Scam tokens emit fake transfers to make wallets show misleading activity. Always check the address field of a log against the real contract you care about. Matching the event signature alone is not enough.
A practical example
Payment detection is one of the clearest uses of logs. To confirm a USDC payment, filter by the USDC contract, the Transfer topic and your receiving address, then match the amount in the data field. Proud Globe detects every tile payment this way, with confirmations and contract checks before a pin goes live.
Educational content only. Nothing here is financial, legal or tax advice. Crypto assets carry risk, so check the details for your own situation.