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
indexedtopics: 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
| Indexed | Non-Indexed | |
|---|---|---|
| Storage location | EVM topic (log header) | ABI-encoded data field |
| Filterable by clients | yes | no |
| Max per event | 3 (4 topics total, first = sig) | unlimited |
Dynamic types (string, bytes, array) | stored as keccak256 hash | stored in full |
| Gas cost | slightly higher | slightly 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
| Operation | Approximate gas |
|---|---|
| Base log cost | 375 |
| Per topic (indexed param) | 375 each |
| Per byte of data | 8 |
| 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
delegatecallemit logs attributed to the caller's address, not the implementation's. - Maximum 4 topics per log entry (topic 0 = signature + 3 indexed params).
anonymousevents cannot be searched by name — use only when you need the gas saving and manage indexing manually.