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");