Events in Solidity
In this article, you’ll learn how events work in Solidity by reviewing some practical examples and common use cases of events.
This tutorial has been moved as part of a reorganization! It assumes you are using Hardhat. Everything in this lesson will work with minor adjustments if you are working in Foundry or Remix.
By the end of this lesson, you should be able to:
Understanding how Solidity events work is important in the world of smart contract development. Events provide a powerful way to create event-driven applications on the blockchain. They allow you to notify external parties, such as off-chain applications, user interfaces, and any entity that wants to listen for events of a particular contract.
In this tutorial, you’ll learn how to declare, trigger, and utilize events, gaining the knowledge necessary to enhance the functionality and user experience of your decentralized applications.
From the official solidity documentation, events are:
…an abstraction on top of the EVM’s logging functionality. Applications can subscribe and listen to these events through the RPC interface of an Ethereum client.
…when you call them, they cause the arguments to be stored in the transaction’s log – a special data structure in the blockchain. These logs are associated with the address of the contract that emitted them, are incorporated into the blockchain, and stay there as long as a block is accessible (forever as of now, but this might change in the future).
In other words, events are an abstraction that allow you to store a transaction’s log information in the blockchain.
Start by creating a first event in the Lock.sol
contract that’s included by default in Hardhat.
The event is called Created
and includes the address of the creator and the amount that was sent during the creation of the smart contract. Then, emit
the event in the constructor:
The contract is:
Events can be defined at the file level or as inheritable members of contracts (including interfaces). You can also define the event in an interface as:
You can test the event by simplifying the original test file with the following code:
Notice that the previous code is logging the sender address and the logs coming from the transaction receipt. You are also decoding the receipts.logs[0].data
field that contains the information emitted by the event but not in a human-readable way, since it is encoded. For that reason, you can use AbiCoder
to decode the raw data.
By running npx hardhat test
, you should be able to see the following:
Notice the value f39fd6e51aad88f6f4ce6ab8827279cfffb92266
is encoded in the property data, which is the address of the sender.
Another important feature is that events can be indexed by adding the indexed attribute to the event declaration.
For example, if you modify the interface with:
Then, if you run npx hardhat test
again, an error may occur because the decoding assumes that the data field contains an address
and a uint256
. But by adding the indexed attribute, you are instructing that the events will be added to a special data structure known as “topics”. Topics have some limitations, since the maximum indexed attributes can be up to three parameters and a topic can only hold a single word (32 bytes).
You then need to modify the decoding line in the test file with the following:
Then, you should be able to see the receipt as:
Notice the topics property, which now contains the address of the sender: f39fd6e51aad88f6f4ce6ab8827279cfffb92266
.
Solidity events have several common use cases, which are described in the following sections.
Events can be used to notify users or external systems about certain contract actions.
Events are primarily used to log significant changes within the contract, providing a transparent and verifiable history of what has occurred.
Events can be valuable for recreating the historical state of a contract. By capturing and analyzing emitted event logs, you can reconstruct past states, offering a transparent and auditable history of the contract’s actions and changes.
Events are essential for debugging and monitoring contract behavior, as they provide a way to observe what’s happening on the blockchain.
The ability to use events to recreate historical states provides an important auditing and transparency feature, allowing users and external parties to verify the contract’s history and actions. While not a common use, it’s a powerful capability that can be particularly useful in certain contexts.
Although it is possible to rely on events to fully recreate the state of a particular contract, there are a few other options to consider.
Existing services such as The Graph allow you to index and create GraphQL endpoints for your smart contracts and generate entities based on custom logic. However, you must pay for that service since you are adding an intermediate layer to your application. This has the following benefits, such as:
But storing all of the information within the smart contract and relying fully on it to access data can create more complexity, since not all of the data is directly query-able. The benefits of this approach include:
As a smart contract developer, you must evaluate which options work best for you.
In this lesson, you’ve learned the basics of Solidity events and their importance in Ethereum smart contract development. You now understand how to declare and trigger events, a few of their common use cases, and the difference between events and smart contract storage.
Now that you have a solid grasp of events and their versatile applications, you can leverage them to build more sophisticated and interactive smart contracts that meet your specific needs, all while being mindful of the cost considerations.