Section 01 · Definition
What is an event in Solidity?
An event is a typed log entry that the EVM writes into a block alongside transaction execution. It is how a contract speaks to the off-chain world.
Quick answer
What is an event? An event is a structured log emitted by a contract during a transaction. It has a name, a topic hash (the keccak256 of the event signature), and a payload of typed data. Off-chain systems subscribe to events to know when something happened on chain — a token transferred, an order filled, a vote cast — without having to scan every storage slot.
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
event Paused(bool paused);
function transfer(address to, uint256 amount) external {
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount); // write the log
}Notice the indexed keyword on two parameters. Indexed parameters become "topics" in the log entry, which off-chain consumers can filter on directly. Up to three indexed parameters per event are allowed. Non-indexed parameters live in the data section and require parsing to extract. To compute the topic 0 hash of any event signature for off chain filters, paste it into the Keccak256 Hash Generator.
Section 02 · The pipeline
From emit to dashboard
An event leaves your contract, lands in a block, and ends up in a database — all within seconds.
A typical event reaches its consumers in three hops. First the contract calls emit, which the EVM compiles to a LOG opcode. Second the log is written into the transaction receipt and stored in the block's bloom filter. Third an off-chain consumer (a Graph subgraph, an ethers.js listener, an Etherscan watcher) fetches the receipt and decodes the log against the event ABI. The contract itself never sees the log again — it is one-way output.
Events vs storage cost
Storing 32 bytes of state costs ~20 000 gas. Emitting an event with the same 32 bytes costs ~1 500 gas plus 8 per byte. That is more than ten times cheaper. Emit liberally on every state change — the cost is negligible and downstream systems get a clean stream of changes to subscribe to.
Section 03 · indexed
What the indexed keyword actually does
indexed turns a parameter into a topic that off-chain filters can match exactly. Use it on values you will look up by — addresses, IDs, status enums.
event OrderPlaced(
uint256 indexed orderId, // topic 1
address indexed trader, // topic 2
address indexed asset, // topic 3
uint256 amount, // data
uint256 priceWei // data
);
emit OrderPlaced(123, msg.sender, USDC, 5_000, 1e18);A consumer can now ask the chain for "every OrderPlaced where asset == USDC and trader == 0xAlice" in one call, with no decoding work. Without indexed they would have to fetch every OrderPlaced and filter client-side, which is much slower.
The trade-off: indexed values longer than 32 bytes (strings, dynamic bytes) are stored as a hash, not the raw value. The actual content is unrecoverable from the log alone — you would also have to fetch the original transaction. For short values like address, uint256, and bytes32, this is a non-issue.
Section 04 · Standard events
The events ERC standards require
Wallets, marketplaces, and explorers all listen for the standard event signatures. Forget to emit one and your contract will not appear correctly in tooling.
// ERC20 — must emit on every transfer and approval
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
// ERC721 — must emit on every transfer, approval, and operator change
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
// ERC1155 — must emit on every transfer (single and batch)
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values);MetaMask, Etherscan, OpenSea, Uniswap, and the Graph subgraphs all watch for these exact signatures. If you write a transfer function but skip the emit Transfer, the balances will update on chain but no wallet will notice — the user appears to lose their tokens until they manually refresh and the wallet rescans the chain.
Section 05 · Real-world uses
Where events show up in production code
// 1. Token movements (every ERC standard)
emit Transfer(from, to, amount);
// 2. Admin changes — make every privileged action visible
event OwnerChanged(address indexed previousOwner, address indexed newOwner);
event FeeChanged(uint256 oldFee, uint256 newFee);
// 3. Lifecycle events — useful for off-chain analytics
event PositionOpened(address indexed user, uint256 collateral, uint256 debt);
event PositionLiquidated(address indexed user, address indexed liquidator, uint256 amount);
// 4. Error and warning events for monitoring
event SuspiciousActivity(address indexed actor, bytes32 reason);Section 07 · FAQ
Frequently asked questions
Can a contract read its own events back?
No. Events are written into the transaction log and are not part of the contract's state. From inside Solidity there is no way to read past events — that is the job of off-chain systems. If a value needs to be readable on chain, store it in state.
What does indexed do?
indexed turns the parameter into a separately stored 'topic' in the log entry, which off-chain filters can match exactly without decoding the rest of the data. You can mark up to three parameters as indexed per event. Indexed dynamic types (string, bytes) are stored as a keccak256 hash, not the raw value.
How much does it cost to emit an event?
Roughly 375 gas for the LOG opcode itself plus 375 per indexed topic plus 8 per byte of data. A simple Transfer event costs around 1 500 to 2 000 gas total — about ten times cheaper than the equivalent storage write.
Why does my wallet not show a token I just received?
Almost always because the contract did not emit a Transfer event with the standard ERC signature. Wallets watch the log stream, not contract storage. Without the event, the transfer is invisible to the wallet until the next manual rescan.
Can events be removed or modified after they are emitted?
No. Events are part of the transaction receipt and are immutable like every other piece of block data. Once a transaction is included, its events cannot be changed by anyone, including the contract that emitted them.