print
or display
the data from within a Solidity program. So how do you keep track of the happenings within a Solidity smart contract? Well, the workaround for this comes in the form of events.
You see, Ethereum (or the Ethereum Virtual Machine (EVM), to be exact) supports the storage of specifically indexed data structures and this feature is conveniently called logs. The EVM logs allow for the recording and storage of events and activities on the Ethereum network. These logs can be accessed and analyzed by developers, allowing them to gain a deeper understanding of the activities taking place on the Ethereum network. This logging feature is leveraged by Solidity in order to implement events.
Just like the print
statements in other languages, events
in Solidity are a way for smart contracts to communicate with the outside world and to record important information on the Ethereum network. Events are useful for a variety of purposes, such as tracking the execution of transactions, monitoring the state of a contract, and providing notifications to other contracts or external applications. Understanding how to fetch and utilize these events will enable developers to better audit and monitor the behavior of smart contracts on the network and this article is essentially a guide that will help you do just that. So, LFG!!!
event
keyword, and they allow smart contracts to send asynchronous notifications to clients. Events can be used to notify users of changes in the contract state, and they can be filtered, searched, and archived for future reference.
An event can have parameters, which are typed variables that provide additional information about the event. To emit an event in a Solidity function, use the emit
keyword followed by the event name and its parameters. Event logs are stored in the blockchain, and they can be accessed through APIs or block explorers. The following code demonstrates the declaration and emission of events in Solidity:
Transfer
event :
from
, to
, and tokenId
—and all of them are preceded by the indexed
keyword.
Upon emitting this event, an event log will be created and within that log, the event signature will be generated by hashing (using Keccak-256) the name (Transfer
) and parameter types (address
, address
,uint256
) of the event. Below is the web3.js code for generating the event signature for the Transfer
event:
topic[0]
of every log.
The topics
array represents all the event parameters that are marked using the indexed
keyword: from
, to
, and tokenId
, in our case. These topics help query and identify particular event logs. The topics
array can include a maximum of 4 elements (topics) and the first element in the array will always be the event signature.
Each topic (parameter data) in the array can be represented using a maximum of 32 bytes of data and while representing the data, if the data falls short of 32 bytes, you can add filler data to cover the size specification.
Any topic carrying data exceeding the prescribed size limit will be hashed before storing. Due to the size limit, arrays or string type data are not given as indexed parameters in order to avoid them from being part of the topics
array.
To explain the topics
array better, let’s look at an example. Suppose a Bored Ape NFT with token ID: 8009
was transferred from address 0xdafce4acc2703a24f29d1321adaadf5768f54642
to the address 0xdbfd76af2157dc15ee4e57f3f942bb45ba84af24
. The topics
array of the generated Transfer
event log then will look like this:
topics
array have 64 characters (omit 0x
) instead of 32, know that they are in hex format and 2 hex digits make up one byte.indexed
keyword) are added to the data
section of the log. Unlike topics
, the data
section cannot be queried. In the case of our Transfer
event, since all the parameters are indexed, the data
section will be empty and it will simply contain 0x
as a placeholder.
All the other fields, like the contract address, block number, and transaction hash contain the respective information. As a whole, the log for the Transfer
event will look something like this:
eth_getLogs
RPC method. This method retrieves the logs that match certain filter criteria defined by the user. The filter criteria can include things like the address of the contract emitting the logs, the topics of the logs, and the range of blocks to search. Here is the sample curl request for fetching the Transfer
event logs from the BAYC contract:
params
of the request, we have provided the fromBlock
and toBlock
fields. They are used to represent the range of blocks to search. While providing the range, keep in mind that the larger the range, the higher the number of logs and thus the higher the response size.
latest
to refer to the latest block. You can also use pending
and earliest
for referring to transactions that are not yet mined.
The address
field in the request parameter helps filter the events based on the contract (represented using the address) from which it was emitted. Since a contract can emit multiple events, we can use the topics
field to provide additional filtering. Here, within the topics
array, we have provided the signature of the Transfer
event—topics[0]
. This will get us the logs of only the Transfer
event from the BAYC contract.
subscribe()
function. The function helps us subscribe to incoming logs, which we can filter and process using different options. The following script help subscribe to the Transfer
event logs from the BAYC contract:
fromBlock
,toBlock
). In the script, while providing the block number, you can directly use the decimal representation of the number.
Here, since we are expecting a stream of data, we are using the WSS endpoint of our Chainstack node. Just like our curl request, we have provided the contract address and the event signature within the topics
field as a means to filter the events.
To provide more filtering, you can specify more topics in the topics
array. To demonstrate this, here is a script that subscribes to the Transfer
event log of a particular BAYC NFT based on its token ID:
topics
array. This script will only fetch the Transfer
event logs that were generated while transferring that particular NFT. When providing topics to the topics
array, we can use the null
value to represent all the topics whose values we didn’t specify (the from
and to
address in this case). Also, before adding the token ID to the array, we used the padLeft()
function to add zeros to the hex value, in order to cover the 32-byte size specification.
And with that, we have covered all the major ways of fetching Ethereum event logs.