Your smart contract can store data, enforce rules, and move money. But it cannot send a notification. It cannot ping your frontend. It cannot tell an off-chain service that something just happened.
That is what events are for, and if you are building a product with a user interface, events are not optional.
What are events, in plain English
Think of events as a contract’s broadcast system. When something important happens inside a transaction, the contract can emit an event: a structured message that gets permanently recorded in the transaction receipt. External systems, like your web app, a mobile client, or an analytics pipeline, can listen for these messages and react to them in real time.
Here is a concrete example. Say you are building a token. Every time tokens move from one wallet to another, your frontend needs to update balances, show a notification, and log the transfer in a history feed. The contract itself does not call your frontend.
Instead, it emits a Transfer event with the sender, recipient, and amount. Your frontend subscribes to that event and updates the UI the moment the transaction confirms.
The key distinction: events are not stored in the contract’s state. They live in transaction logs, a separate part of the blockchain that is cheaper to write to and accessible to any external observer. Contracts themselves cannot read events after they are emitted. This is strictly a one-way broadcast from the blockchain to the outside world.
Why this matters for your project
If your product has a frontend, events are how it stays in sync with the blockchain. Without them, you would need to poll the contract’s state variables continuously, which is slow, expensive, and unreliable. Events give you a real-time feed of exactly what changed and when.
The ERC-20 token standard, used by every major token on Ethereum, requires two events: Transfer and Approval. These are not suggestions. They are part of the specification.
Wallets like MetaMask, block explorers like Etherscan, and portfolio trackers all depend on these events to display your token’s activity. If your token contract does not emit them, it will appear broken to every tool in the ecosystem.
Beyond compliance, events also serve as a cheap audit trail. Writing a value to contract storage costs at least 20,000 gas for a new slot. Emitting an event with the same data costs roughly 375 gas base plus 8 gas per byte. If you have data that only needs to be read off-chain (transaction history, activity logs, analytics), events save your users real money on every transaction.
How it works (the 30-second version)
Events in Solidity have two parts: the definition and the emission. You define the event once, then emit it wherever it is needed.
// Define the event structure
event Transfer(address indexed from, address indexed to, uint256 amount);
// Emit it inside a function
emit Transfer(msg.sender, recipient, amount); The indexed keyword on from and to makes those parameters searchable. Under the hood, indexed values are stored as “topics” in the EVM log, and external tools can filter events by topic. You want to find every transfer sent by a specific address? Filter on the first indexed parameter.
You can mark up to three parameters as indexed per event.
Non-indexed parameters (like amount above) are ABI-encoded into the log’s data section. They are included in the event but cannot be used as search filters. The trade-off is simple: indexed parameters are searchable but cost slightly more gas per topic (375 gas each). Non-indexed parameters are cheaper but require reading the full event to access.
The gas math: a typical event with two indexed parameters and 32 bytes of data costs around 1,750 gas total. Compare that to a single storage write at 20,000 gas. Events are roughly 10 to 15 times cheaper for recording the same information.
How Doodledapp makes this visual
Doodledapp splits the two parts of Solidity events into two distinct nodes, matching how events actually work in the language.
The Event Definition node is where you declare the event’s structure. You give it a name (like “Transfer”) and add parameters with their types. This is equivalent to the event Transfer(...) line in Solidity. It sits alongside your other definitions, like state variables and structs, and does not connect to any execution flow.
The Emit Event node is where you fire the event inside a function. It has execution handles (the triangles that connect to other work nodes), so it plugs directly into your function’s flow. You select which event to emit from a dropdown that lists all your Event Definition nodes, and the parameters are wired in through data connections.
| Solidity concept | Doodledapp equivalent |
|---|---|
event Transfer(...) | Event Definition node with name and parameters |
emit Transfer(...) | Emit Event node connected in execution flow |
indexed parameters | Parameter list with indexed option |
The visual separation makes a common mistake obvious: if you define an event but never connect an Emit Event node anywhere in your flow, you can see at a glance that the event is never actually used. In raw Solidity, an unused event definition compiles without warning.
Common mistakes to avoid
Forgetting to emit events that standards require. If you are building an ERC-20, ERC-721, or any contract that implements a standard interface, check which events the standard mandates. The ERC-20 spec requires Transfer to be emitted on every token movement, including mints and burns. Missing these events means wallets and explorers cannot track your token.
Indexing the wrong parameters. You get a maximum of three indexed parameters per event. Use them on the values your frontend or analytics system will filter by most often, typically addresses and IDs. Indexing a large string or bytes value does not store the actual value as a topic; it stores the keccak256 hash, which makes the original value unrecoverable from the log alone.
Using events as a substitute for storage. Events are cheap because contracts cannot read them. If your contract logic needs to reference a value later, it must be in storage. Events are for external consumption only. A function cannot check “was this event emitted earlier?” because the EVM provides no mechanism to read logs from within a contract.
Emitting too much data in a single event. Every byte of event data costs 8 gas. For small payloads this is negligible, but if you are emitting large structs or arrays in a loop, the gas adds up. Emit only the data that external systems actually need. If the rest is already in storage, consumers can query it separately.
The bottom line
Events are how your smart contract communicates with everything outside the blockchain: your frontend, your indexer, your analytics, and every wallet and explorer that interacts with your token. They cost a fraction of what storage writes cost, they are permanent, and for any contract that users interact with through a UI, they are essential. In Doodledapp, you can see this communication layer directly on the canvas, an Event Definition declaring the message format and an Emit Event node broadcasting it from inside your function flow.