Solidity Cheatsheet

Events

Use this Solidity reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

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.