Events & Logs
16.1 Declaring and emitting
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Events {
event Deposit(address indexed user, uint256 amount, uint256 timestamp);
event Transfer(address indexed from, address indexed to, uint256 value);
event Anon(uint256 x) anonymous; // no signature topic; up to 4 indexed params
mapping(address => uint256) public balances;
function deposit() external payable {
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value, block.timestamp);
}
}16.2 How logs work
keccak256("Deposit(address,uint256,uint256)") (the event selector; omitted for anonymous events) up to 3 indexed parameters (searchable/filterable) ABI-encoded non-indexed parameters
Indexed dynamic types (string, bytes, arrays, structs) are stored as their keccak256 hash, so the original value cannot be recovered from the topic.
Logs are not accessible to contracts — they are for off-chain consumers (frontends, indexers like The Graph, Ponder, Envio, Goldsky).
Logs are much cheaper than storage (≈375 gas + 375/topic + 8/byte).
Emit events for every important state change; indexers and users depend on them.
EventName.selector gives the topic-0 hash (0.8.15+).
16.3 Listening off-chain (ethers.js v6)
const contract = new ethers.Contract(address, abi, provider);
contract.on("Deposit", (user, amount, timestamp, event) => {
console.log(`${user} deposited ${ethers.formatEther(amount)} ETH`);
});
const logs = await contract.queryFilter(contract.filters.Deposit(userAddress), fromBlock, "latest");