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
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.