Events
A Special Kind of On-Chain Data
Up until this point of the textbook, all on-chain data has revolved around the smart contract; that is, any information that we associated as living on the blockchain was always tied to an associated contract (i.e. via a state variable). In this section, we will go over events, a different form of on-chain data that allows us to permanently store data without having it be stored within a smart contract.
Motivation: Dummy ERC-20 Token
To understand events, we will introduce the example of a "dummy" ERC-20 token called... Dummy
.
Like all ERC-20 tokens, Dummy
has the logic necessary to transfer tokens from one address to another, via the transfer
function. However, rather than being able to just transfer tokens between addresses, we would also like to keep track of every transfer. Whether you are the IRS or your average on-chain data scrapper, there is good reason to track transfer events.
To start, one might track transfer events via the following principle:
Create a state variable which stores all transfer events (as a list). Furthermore, for each transfer event, append the event to the transfer event list
An updated implementation of the Dummy
token with this new principle in mind might look like as follows:
Examining each segment of this new Dummy
contract in depth:
TransferEvent
: struct which represents a transfer event. This struct holds the from address, to address, and value transfered.allTransfers
: a dynamic array which holds all transfer eventstransfer
: function which executes a transfer. At the end of this function, the transfer event is appended toallTransfers
.
Our new implementation of the Dummy
token is now able to keep track of every transfer event! However, the biggest issue with this is that this new implementation is egregiously inefficient. Storing all transfer events into a state variable is extremely costly when considering the transfer
function might be called thousands of times per day. Are we doomed?
Events
As the last section made it evident, we want a way to store transfer events without having to use any contract data. For this, we can leverage events. The syntax for events is as follows:
Inside the parentheses is where we can list the arguments of our event (i.e. the data that we want logged). In the case of our transfer event, we can define the following:
Finally, now that we have our event defined, we can emit our event as follows:
Tying all these ideas, the following code is an updated version of our Dummy
token which utilizes events to record transfers:
Many might be wondering where events are actually stored? As mentioned previously, events are not stored as state variables; rather, events are stored as part of the transaction receipt. By storing data in this format, users are able to store on-chain data without having to utilize state variables.
Indexing
Although we showed how to store data in events, we have not shown how to prioritize certain types of data within an event. To understand why we might want to prioritize certain data fields in our events, consider again the Transfer
event:
Although we will get more into how events are represented when we discuss the EVM, the general structure of events is as follows:
Event ID
Event Data
With our current implementation of the Transfer
event, our event data field is as follows (utilizing arbitrary values for the from, to, and value fields):
Although it is possible to extract the arguments from event data field, most can agree that the data field, as it stands, is a mess. Ideally, we want a way to structure our event data so that we can access the arguments of an event directly without having to do any sort of decoding; that is, we want to prioritize certain arguments. For this, we can mark the arguments we want to prioritize with the indexed
label; the following code is an updated of our Transfer
event that utilizes indexed arguments:
Using indexed arguments, the new structure of our event is as follows:
Event ID
from
Argument:0x000000000000000000000000Fb2b43852f444DE82643ec58BDc125Ee4EBeDad6
to
Argument:0x000000000000000000000000549889FA3522717E7F1665647BA89f58DE8D277d
Event Data:
0x0000000000000000000000000000000000000000000000000000000000000001
Using indexed arguments, we can now clearly access the arguments of our event. Note that the structure of an event is dependent on the ordering of the event arguments.
Index Everything?
Although it seems that indexing all arguments is ideal, consider the following:
We can only index at most 3 arguments in an event
The amount of gas used in emitting an event is dependent on the number of arguments indexed. Therefore, in the case of our
transfer
event, although we could have indexed all three arguments, it is better from a gas perspective to index only two arguments since we know that the data field contains just the value transferred.
Resources
Events in Go Ethereum: https://goethereumbook.org/events/
Last updated