An Introduction To Events In Solidity

An Introduction To Events In Solidity

This article is an introduction to what events are in Solidity and how to execute them according to your use cases.

Events are important in Ethereum because they facilitate communication between smart contracts and their user interfaces. In Ethereum, when a transaction is mined, smart contracts emit events and write logs to the blockchain that the frontend can then process.

Events perform a major role in app development. It's because the activities we perform take some time and sometimes our next task relies on the result of that particular transaction.

So how we will know whether the transaction is completed or gets blocked or is still pending when the smart contract cannot communicate with you? That’s why we have to observe the blockchain for any change. We can do multiple reloads to check the transaction state but it is very cumbersome and infeasible. Here comes the role of events. Events are basically a separated data storage that can be read easily by nodes. We can subscribe to the events from smart contracts and update the transaction and the UI's state accordingly.

Consider you are using metamask for logging in to your app and your UI app just shows you the balance of your account and for the very first time, it shows you the balance of your current account and now you switch the current account to a different account. Now how will the app know you switched your account?

But before that let's create the provider:-

The provider is how web3 talks to the blockchain. Providers take JSON-RPC requests and return the response. This is normally done by submitting the request to an HTTP or IPC socket-based server. Web3.py supports one provider per instance. Run the following code to check this:

import { ethers } from "ethers";
   // you can use any software which interacts with ethereum blockchain. I am using metamask.
   // metamask automatically adds ethereum object in browser window property
const provider = new ethers.providers.Web3Provider(window.ethereum);
  // so by this we can get our provider using ether.js

Web3.js support 3 different providers: HttpProvider, WebsocketProvider, and IpcProvider. Detailed information about these providers is here

Events

An event needs to be declared in the smart contract. This is done similarly to declaring a variable. The emittance of an event is done with the keyword emit in the respective function. Run the following code:

contract events{
    event myFirstEvent(address indexed sender, uint256 indexed amount, string message);

    function executeEvent(string memory _message) public payable returns(string memory){
        emit myFirstEvent(msg.sender, msg.value, _message);
        return _message;
    }
}

Now back to listening to events!

So before listening to an event we should know the proper cycle of events. You can write your custom events in Solidity. You can know more about solidity by clicking here.

Declaring An Event:- Providing a sSolidity syntax for declaring an event can be done by running the following code:

event accountsChanged(address indexed from, bytes32 indexed to);

Emitting An Event:- You have to emit an event after declaration or you will not be able to listen to your event. You can do this by running the following code:

emit accountsChanged(from, to);
// whenever metamask change the account it emits the accountsChanged event which you can listen to.

You can listen to an event by running the following code:**

provider.on('accountsChanged', () => {
     // do something on account change
})

So whenever your account changes, your app will automatically read that the account is changed. Now if you will check your balance you can do so new account without any refresh action which is normally required.

For more information on listening events, you can refer to this youtube video and can read my github gist

Event Signature

An event signature is a way of referencing an event. For example:

Transfer(address1, address2, uint256)
     // address1 refers to senders address
     // address2 refers to receivers address
     // and the uint256 is the amount you want to transfer

This is one event from ERC20 Interface which is provided from OpenZeppelin

Event Emitters Different methods are already present in the providers and we can simply emit events using these methods. I am going to list and explain them but you can refer to the documentation here

on Method:- this method is called whenever an event is emitted in the contract which you have declared. It takes in an event name and a callback function as shown below:

this.provider.on(eventName, (result) => {
      // do something here
})

once Method:- This method is called once a particular event is going to be removed from the listeners. In simple words, you can use this event whenever you wanted to check for a transaction completion or an error. It simply executes its callback function whenever the transaction completes its final stage as shown below:

this.provider.once(eventName, (result) => {
      // do something here
})

emit Method:- Consider you have declared some listeners for an event and now you have to pass some arguments manually to all the listeners, then this method prove to be of a lot of use for you. But in most cases, this method is only used internally as executed below:

this.provider.emit(eventName, ...args)

off Method:- This method removes listeners from the event and if no listeners are provided, this method will remove all the listeners from that event using the following code snippet:

this.provider.off(eventName, array of listeners)

removeAllListeners Method:- As stated by the method name this method removes all the listeners from the provided event name. If no event name is provided, this method removes all the listeners declared using the following snippet:

this.provider.removeAllListeners(array of eventNames)

listenerCount Method:- This method returns the count of the declared listeners of a particular event or an event array and if no event name is given this method returns the count of all the listeners using the following snippet:

this.provider.listenerCount(array of eventNames)

listeners Method:- This method returns an array of listeners of a particular event. If no event name is provided, it returns all the listeners in the contract using the following snippet:

this.provider.listeners(array of eventNames)

So what is this eventName?

Some predefined names are already given to the events and these names are clearly defined in the documentation which you can refer here

*`EventFilter`* :- Logs and filtering allow for efficient queries of indexed data and provide lower-cost data storage when the data is not required to be accessed on-chain. Use the following code to execute this event:

filter = {
    address: THE_ADDRESS_OF_YOUR_CONTRACT,
    topics: [
        // the name of the event, parnetheses containing the data type of each event, no spaces
        utils.id("Transfer(address,address,uint256)")
    ]
}
provider.on(filter, () => {
    // do whatever you want here
    // I'm pretty sure this returns a promise, so don't forget to resolve it
})

Explore more on Event Filters concept here

Can we emit one-to-one events?

For the requirement of having an event raised for a particular person (seller), the way to do this would be either:

One event list: The event data structure contains a destination address property. You can set the destination to a unique value (e.g. 0x000000) that means everyone, or you can set the "destination" to one person (e.g. the address of the seller).

Unique event list for every seller: Your event processing will need to have an outer loop to iterate over all the event lists. You can also have a global event list which means every seller is sent the event.

Or, you can use the best solution Events filter for a particular account address as shown below:

// Short example of manually creating filters for an ERC-20
// Transfer event.
//
// Most users should generally use the Contract API to
// compute filters, as it is much simpler, but this is
// provided as an illustration for those curious. See
// below for examples of the equivalent Contract API.

// ERC-20:
//   Transfer(address indexed src, address indexed dst, uint val)
//
// -------------------^
// ----------------------------------------^
//
// Notice that only *src* and *dst* are *indexed*, so ONLY they
// qualify for filtering.
//
// Also, note that in Solidity an Event uses the first topic to
// identify the Event name; for Transfer this will be:
//   id("Transfer(address,address,uint256)")
//
// Other Notes:
//  - A topic must be 32 bytes; so shorter types must be padded

// List all token transfers  *from*  myAddress
filter = {
    address: tokenAddress,
    topics: [
        utils.id("Transfer(address,address,uint256)"),
        hexZeroPad(myAddress, 32)
    ]
};

// List all token transfers  *to*  myAddress:
filter = {
    address: tokenAddress,
    topics: [
        utils.id("Transfer(address,address,uint256)"),
        null,
        hexZeroPad(myAddress, 32)
    ]
};

// List all token transfers  *to*  myAddress or myOtherAddress:
filter = {
    address: tokenAddress,
    topics: [
        utils.id("Transfer(address,address,uint256)"),
        null,
        [
            hexZeroPad(myAddress, 32),
            hexZeroPad(myOtherAddress, 32),
        ]
    ]
};
provider.on(filter, () => {
    // do whatever you want here
    // I'm pretty sure this returns a promise, so don't forget to resolve it
})

Conclusion

As discussed in this article, there are two types of Solidity event parameters: indexed and not indexed. Events are used for return values from the transaction and as a cheap data storage. This article dealt with how the Blockchain keeps event parameters in transactions where logsEvents can be filtered by name and by contract address.

I hope this article has given you an insight into what events are in Solidity and how to run them for your use-cases.

References

This article is part of Research & Development work being done by Pushkar Kumar, Suresh Konakanchi, and Ruchika Gupta. We will be covering a series of articles along with open source projects around blockchain, smart contracts, and web3 in general. Here is the list of all the articles that you can follow to start with Blockchain and write your first contract: