Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome to the fifth lesson in our "Advanced Solidity & Contract Logic" module.
In our last lesson, we covered arrays, giving us a way to store ordered collections of data. We saw how to manage lists of investors or whitelisted addresses. However, a crucial piece is missing: how does an off-chain application, like a web frontend or a data analytics script, know when these on-chain lists change? Constantly polling the contract for changes is inefficient and slow.
This is where today's topic, Solidity events, comes in. Your goal for this lesson is to learn how to implement events to log important contract activity for off-chain applications.
For a backend developer like yourself, you can think of events as being conceptually similar to a structured logging system (like Python's logging or Winston for Node.js). You emit structured data from your application (the smart contract), which can then be consumed by other services (the off-chain world). The key difference is that these logs are immutably stored on the blockchain itself.
What are Events and Why are They Essential?
Events are a specialized, gas-efficient logging facility built into the Ethereum Virtual Machine (EVM). They serve one primary purpose: to provide a way for smart contracts to communicate with the outside world.
Key characteristics:
- Gas-Efficient: Storing data in an event log is significantly cheaper than writing to a
storagevariable. - Inaccessible to Contracts: A contract can emit events, but it cannot read event logs (not even its own). This limitation is what makes them cheap.
- The Bridge to Off-Chain: They are the standard way for user interfaces, scripts, and backend services to listen for and react to specific occurrences within a contract.
This first video from Chainlink provides an excellent overview of what events are and the crucial role they play in the blockchain ecosystem.
Events and Logging in Solidity
Watch this introductory segment to understand the fundamental purpose of events and how they enable off-chain applications to work.
Watch from the beginning to 02:56. Focus on the core problem events solve: allowing off-chain applications to efficiently listen for on-chain activity without the high cost of using storage variables.
To deepen that understanding, the following article from RareSkills explains precisely why querying events is far superior to trying to parse historical transaction data.
Solidity Events | By RareSkills
This article, 'Solidity Events' by RareSkills, further clarifies the 'why' behind events by comparing the API options for retrieving transactions versus events.
Read the section titled 'Retrieving transactions faster'. This will give you a clear picture of the technical limitations that make events necessary for efficient historical data retrieval.
The Anatomy of a Solidity Event
Implementing an event involves two steps: declaring its structure and then emitting it from a function.
-
Declaring the Event: You define an event using the
eventkeyword, giving it a name and a set of parameters, much like a function signature. -
The
indexedKeyword: You can add theindexedkeyword to up to three parameters in an event's definition.- Indexed parameters (Topics): These are stored in a special way that allows you to efficiently search for them. Think of them as database indexes. You can filter logs to find all events where an indexed
addressparameter was, for example, your own address. - Non-indexed parameters: These are cheaper to store but cannot be searched for directly. Off-chain services must fetch all events and then filter these values client-side.
- Indexed parameters (Topics): These are stored in a special way that allows you to efficiently search for them. Think of them as database indexes. You can filter logs to find all events where an indexed
-
Emitting the Event: Inside a function, you use the
emitkeyword, followed by the event's name and the values you want to log.
Let's continue with the Chainlink video to see the exact syntax for this.
Events and Logging in Solidity
This next segment demonstrates how to declare an event, use the indexed keyword, and emit the event from within a function.
Watch from 02:56 to 05:01. Pay close attention to the syntax event StoredNumber(...) and emit StoredNumber(...) and the explanation of indexed vs. non-indexed parameters.
Visualizing Events and Listening Off-Chain
When a transaction that emits an event is mined, the event data is stored in the transaction's receipt. Block explorers like Etherscan can parse and display these logs.

While block explorers are great for inspection, your applications will need to consume this data programmatically. This is typically done using a library like ethers.js or web3.js.
The RareSkills article provides clear ethers.js examples for both subscribing to live events and querying past events. This pattern will be fundamental when you build a dApp frontend in Module 9.
Solidity Events | By RareSkills
Now, let's look at the code for listening to these events. This section of the RareSkills article provides JavaScript snippets that are directly applicable to building dApps.
Read the section 'Listening to Events'. Focus on the two examples: contract.on() for real-time listening and tokenContract.queryFilter() for retrieving historical events that match specific criteria (like a transfer from a particular address).
Practical Implementation with Hardhat
Now it's time to put it all together. The following video segment walks through a complete example within a Hardhat project. You will see how to:
- Define a contract with an event.
- Write a Hardhat script to deploy the contract.
- Call the function that emits the event.
- Retrieve and log the event data from the transaction receipt.
This process of accessing event arguments from a transaction receipt is incredibly useful for testing, as it allows you to assert that the correct event was emitted with the correct data.
Events and Logging in Solidity
This detailed walkthrough shows the full development cycle: writing the contract, deploying it with a script, and then accessing the event data programmatically.
Watch from 06:16 to 18:32. Follow along as the video demonstrates how to access the event arguments via transactionReceipt.events. This is a core skill for both scripting and testing.
Test your understanding!
You have a simple contract to track high scores.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract HighScore {
uint256 public highScore;
function setHighScore(uint256 _newScore) public {
if (_newScore > highScore) {
highScore = _newScore;
// Your code goes here
}
}
}
Modify the contract to emit an event named NewHighScore whenever the highScore is updated. The event should log the address of the player who set the new score (player) and the new score itself (score). The player's address should be searchable.
Show answer
You need to add an event declaration and an emit statement. The player address should be indexed.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
contract HighScore {
uint256 public highScore;
// 1. Declare the event with an indexed player address
event NewHighScore(address indexed player, uint256 score);
function setHighScore(uint256 _newScore) public {
if (_newScore > highScore) {
highScore = _newScore;
// 2. Emit the event with the correct parameters
emit NewHighScore(msg.sender, _newScore);
}
}
}
Events in Practice: The ERC-20 Token Standard
Events are not just a "nice-to-have"; they are a required part of many standards, including the ERC-20 token standard you'll be building. The standard mandates that Transfer and Approval events are emitted for key functions. This is what allows wallets, exchanges, and block explorers to track every single token movement across the network.
To see this in action, the following short clips show exactly where the emit statements appear in the core functions of an ERC-20 contract.
These clips from Smart Contract Programmer's ERC-20 tutorial highlight how events are a non-negotiable part of the token standard.
Watch the following short segments: Transfer event: 03:52 - 04:30 Approval event: 05:00 - 05:15 Minting (Transfer from address(0)): 07:43 - 08:15 Notice how each state-changing action is accompanied by an emit statement.
Conclusion
You've now learned how to use events, the essential mechanism for communicating from your smart contract to the outside world. This is a fundamental skill for creating contracts that can be integrated into larger applications.
Key Takeaways:
- Events are for logging: They provide a cheap way to record data on-chain for off-chain consumption.
indexedparameters are for searching: Use theindexedkeyword on parameters you expect clients will need to filter by, like user addresses.emitis the trigger: Use theemitkeyword inside your functions after a significant state change has occurred.- Off-chain listeners: Applications use libraries like
ethers.jsto subscribe to events and react to them, creating responsive user experiences.
Preview of the Next Lesson:
We've covered structs, mappings, arrays, and now events—all powerful tools for managing and reporting on contract data. However, robust contracts also need to control who can change that data. In our next lesson, we will create and apply function modifiers for reusable validation and access control. You'll learn how to write clean, declarative checks (like onlyOwner) to secure your contract's functions.