Lesson 09 of 25
Events & Logging
Emit events, index parameters, and listen for contract activity off-chain.
IntermediateWhy Events?
Events are the EVM's logging mechanism. They are stored in transaction receipts (not contract storage), making them cheap to emit. Off-chain applications (dApps, indexers, bots) use events to track what happened on-chain without expensive storage reads.
Key uses: activity feeds, balance snapshots, dApp state sync, analytics.
Declaring and Emitting Events
Declare events at the contract level, then
emit them inside functions.
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract EventDemo {
event Transfer(
address indexed from,
address indexed to,
uint256 amount
);
event Deposit(address indexed user, uint256 amount);
mapping(address => uint256) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
function transfer(address to, uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
}
}
Indexed Parameters
Up to 3 parameters can be marked
indexed. Indexed parameters are stored as topics and can be efficiently filtered in off-chain queries. Non-indexed parameters are encoded in the event data and cannot be filtered.
Solidity
// Good: index fields you'll commonly filter by
event Approval(
address indexed owner, // filter by owner ✓
address indexed spender, // filter by spender ✓
uint256 value // not indexed — data only
);
// Off-chain filter example (ethers.js):
// contract.filters.Approval(myAddress, null)
// → returns all approvals FROM myAddress
Listening to Events (Front-end)
Use ethers.js or web3.js to listen for events in your dApp.
Solidity
// ethers.js v6 example
const contract = new ethers.Contract(address, abi, provider);
// Listen in real-time
contract.on("Transfer", (from, to, amount, event) => {
console.log(`${from} → ${to}: ${ethers.formatEther(amount)} ETH`);
});
// Query historical events
const filter = contract.filters.Transfer(null, myAddress);
const events = await contract.queryFilter(filter, 0, "latest");
events.forEach(e => console.log(e.args));