Why Events Exist
Events are Solidity's mechanism for writing to the Ethereum transaction log — a special data structure stored with each transaction receipt but outside the contract's own storage. You declare an event with the event keyword and fire it with emit. The primary purpose is to give off-chain applications (front-ends, indexers, wallets) an efficient, cheap way to be notified about on-chain activity, such as a token transfer or a governance vote. Crucially, contracts cannot read their own or others' logs — events are a one-way channel from the blockchain to the outside world.
Cricket analogy: An event is like the scoreboard operator posting each wicket to the big screen — spectators and broadcasters read it instantly, but the players on the pitch can't consult that screen to make decisions.
Indexed Parameters and Topics
An event can mark up to three parameters as indexed. Indexed parameters become 'topics' in the log, which lets off-chain clients filter logs efficiently — for example, querying all Transfer events where to equals a specific address. Non-indexed parameters are ABI-encoded together into the log's data field and cannot be filtered on directly. There is a subtlety: for reference types like string or bytes, marking them indexed stores the Keccak-256 hash of the value as the topic, not the value itself, so you can match on equality but cannot recover the original string from the log.
Cricket analogy: Indexed parameters are like tagging each scoreboard entry by batsman name and dismissal type, so a statistician can instantly filter 'all sixes by Rohit Sharma' without scanning every ball.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Token {
mapping(address => uint256) public balanceOf;
// Up to 3 indexed params become filterable topics
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// string indexed stores a hash as the topic, not the text
event Note(address indexed author, string indexed tagHash, string message);
function transfer(address to, uint256 value) external returns (bool) {
require(balanceOf[msg.sender] >= value, "insufficient");
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
emit Transfer(msg.sender, to, value); // fire the log
return true;
}
function annotate(string calldata tag, string calldata message) external {
emit Note(msg.sender, tag, message);
}
}The standard ERC-20 Transfer(address indexed from, address indexed to, uint256 value) event is what block explorers and wallets use to build your token balance history. Emitting the correct standard events is what makes a token 'show up' properly in tools like Etherscan and MetaMask.
Cost, anonymous Events, and Limits
Emitting an event is far cheaper than writing to storage: a log costs a base of 375 gas plus 375 per topic and 8 gas per byte of data, whereas a single cold storage write can cost 20000 gas. This makes events the right tool for recording historical activity that only off-chain consumers need. However, you cannot use events to store data your contract will later read — the contract has no opcode to access logs. An event may be declared anonymous, which omits the event signature from the first topic; this saves a little gas and allows a fourth indexed parameter, but off-chain filtering by event type becomes harder.
Cricket analogy: Emitting an event is like jotting a quick note in the scorer's book versus engraving it on a trophy — the note is cheap and permanent enough for records, but you can't play the next ball off it.
Never rely on events for critical state your contract logic needs, and never emit sensitive secrets in events — the transaction log is public and permanent. Also remember that events do not fire if a transaction reverts; the log is only committed when the transaction succeeds.
In practice, well-designed contracts emit an event for every meaningful state change — deposits, withdrawals, ownership transfers, configuration updates. This gives off-chain indexers (like The Graph) and auditors a complete, gas-efficient history to reconstruct the contract's timeline without replaying every transaction. Many security and monitoring tools watch for specific events in real time to detect anomalies, and standards like ERC-20 and ERC-721 mandate specific events precisely so the broader ecosystem of wallets and explorers can interoperate without custom integration.
Cricket analogy: Emitting an event per state change is like recording every ball in the scorebook, so a broadcaster can reconstruct the whole innings later without re-watching the match footage.
- Events write to the transaction log using
emit; they notify off-chain apps cheaply and are declared withevent. - Contracts cannot read logs — events are a one-way channel from chain to the outside world.
- Up to three parameters can be
indexed, becoming filterable topics; non-indexed params go into the data field. - Indexing a string or bytes stores its Keccak-256 hash as the topic, not the recoverable value.
- Events are far cheaper than storage (roughly 375 gas + 375 per topic + 8 gas/byte vs 20000 for a storage write).
- Standard events like ERC-20 Transfer power wallets and explorers; emit them for interoperability.
- Events do not fire on reverted transactions and must never carry secrets, since logs are public and permanent.
Practice what you learned
1. Can a smart contract read back events it has previously emitted?
2. How many parameters can be marked indexed in a normal (non-anonymous) event?
3. What is stored as the topic when a `string` parameter is marked indexed?
4. Why are events much cheaper than storing the same data in contract storage?
5. What happens to emitted events if the transaction reverts?
Was this page helpful?
You May Also Like
Functions and Visibility Modifiers
Understand how Solidity functions are declared and the four visibility levels — public, external, internal, and private — plus state mutability keywords like view and pure.
Error Handling with require, revert, assert
Solidity handles errors by reverting state changes. Learn when to use require for input validation, revert with custom errors for gas-efficient failures, and assert for invariants that must never fail.
Function Modifiers in Solidity
Function modifiers are reusable pieces of code that wrap function bodies to enforce preconditions like access control, using the special _ placeholder to mark where the function runs.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics