Solidity Cheatsheet

Events

Use this Solidity reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Event Declaration

event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event Paused(address account);
event DataStored(bytes32 indexed key, bytes data); // non-indexed data in data field
  • indexed topics: up to 3 per event (excluding the event signature itself). Indexed parameters are stored as EVM "topics" — filterable by off-chain clients.
  • Non-indexed parameters go into the ABI-encoded "data" field — not filterable but cheaper and can hold dynamic types in full.

Emitting Events

contract Token {
    event Transfer(address indexed from, address indexed to, uint256 value);

    mapping(address => uint256) public balances;

    function transfer(address to, uint256 amount) external {
        balances[msg.sender] -= amount;
        balances[to] += amount;
        emit Transfer(msg.sender, to, amount);  // emit keyword required (0.4.21+)
    }
}

Indexed vs Non-Indexed

IndexedNon-Indexed
Storage locationEVM topic (log header)ABI-encoded data field
Filterable by clientsyesno
Max per event3 (4 topics total, first = sig)unlimited
Dynamic types (string, bytes, array)stored as keccak256 hashstored in full
Gas costslightly higherslightly lower
event LogMessage(
    address indexed sender,   // topic 1 — filterable
    uint256 indexed id,       // topic 2 — filterable
    string  message           // data field — full string, not filterable
);

// Indexed dynamic type — HASHED, not the raw value
event LogData(bytes indexed data); // off-chain receives keccak256(data), not data

When indexing a string, bytes, or array, the off-chain consumer sees the keccak256 hash. Store the raw value as a non-indexed parameter if you need to read it back.

Anonymous Events

event Ping() anonymous;
// No event signature topic (topic[0]). Saves ~375 gas.
// Cannot filter by event name — only by address or remaining topics.
emit Ping();

Event Signature Topic

Topic 0 (always set unless anonymous) = keccak256("EventName(type1,type2,...)").

// Transfer(address,address,uint256)
bytes32 constant TRANSFER_TOPIC = keccak256("Transfer(address,address,uint256)");
// = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

Events in Interfaces

interface IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract ERC20 is IERC20 {
    // Must emit these exact signatures to be compatible
    function transfer(address to, uint256 amount) external returns (bool) {
        // ...
        emit Transfer(msg.sender, to, amount);
        return true;
    }
}

Listening Off-Chain (ethers.js Reference)

// Filter by indexed topics
const filter = token.filters.Transfer(null, recipientAddress);
const logs   = await token.queryFilter(filter, fromBlock, toBlock);

// Subscribe
token.on("Transfer", (from, to, value, event) => {
    console.log(`${from} → ${to}: ${value}`);
});

Common Event Patterns

State Changes

event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event Upgraded(address indexed implementation);

Monetary Events

event Deposited(address indexed user, uint256 amount, uint256 newBalance);
event Withdrawn(address indexed user, uint256 amount);
event FeesCollected(address indexed collector, uint256 amount);

Lifecycle Events

event ContractDeployed(address indexed deployer, uint256 timestamp);
event Paused(address indexed account);
event Unpaused(address indexed account);

Audit Trail

// Include both old and new values for auditing
event ConfigUpdated(
    bytes32 indexed key,
    uint256 oldValue,
    uint256 newValue,
    address indexed updatedBy
);

Gas Costs

OperationApproximate gas
Base log cost375
Per topic (indexed param)375 each
Per byte of data8
Event with 3 indexed + 1 uint256 data~1 800

Events are much cheaper than storage. Use them for historical data you don't need to read on-chain.

revert and Events

Events are not emitted if the transaction reverts. All log entries from a reverted call are discarded.

function riskyOp() external {
    emit Started(msg.sender);   // this WILL NOT appear if revert() is called after
    require(condition, "fail"); // reverts entire tx including the emit above
    emit Finished(msg.sender);
}

Gotchas

  • You cannot read events from within Solidity — they are write-only from the contract's perspective.
  • Events persist on the blockchain but are not part of the state trie — they are in receipts.
  • Contracts that delegatecall emit logs attributed to the caller's address, not the implementation's.
  • Maximum 4 topics per log entry (topic 0 = signature + 3 indexed params).
  • anonymous events cannot be searched by name — use only when you need the gas saving and manage indexing manually.