04 Understanding Blockchain with `Ethers.js`: 4 Tasks of Smart Contract Events
Tags: blockchain, web3, dapp, ethersjs
Published: 2022-02-04 16:14:00

Events are very, very important for smart contracts and blockchain. `Ethers.js` also provide some features to deal with events as well as logs. There are three types of interactions we can do with a smart contract: 

- **read-only**: call non-state-change functions
- **write**: call state-change functions (sometimes we call it "send transaction")
- **listen to events**: listen to events emitted by smart contract.


### Understanding Smart contract events

Events explained in plain English. When we call a state-change function of a smart contract, there are three steps: 

- STEP 1: Off-chain call. We call a state-change function of a smart contract using JavaScript off-chain. 

- STEP 2: On-chain confirmation. State-change transactions need to be confirmed by consensus algorithms in several blocks on-chain. So we can't get the result immediately.

- STEP 3: Emit events. Once the transaction is confirmed, an event is emitted. You can listen to events to get the results off-chain.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2n5zms5jkp7owo38c0fq.png)
 
Usually we listen to events in our DAPP. But we can also try to play with events with `Ethers.js`. Here are 4 tasks for you.

---

## Prerequisites

You will interact with an ERC20 smart contract deployed to Hardhat Network (local testnet). Before you start the 4 tasks in this article, you need to to meet some prerequisites which you can found in [the third tutorial in this series](https://dev.to/yakult/03-understanding-blockchain-with-ethersjs-4-tasks-of-interacting-with-smart-contract-3ef6):

1. ERC20 smart contract "ClassToken" with deploy and test scripts.

2. Run Hardhat Network (local testnet) in another terminal by running `yarn hardhat node`

3. Compile, test and deploy ClassToken: 

```bash
yarn hardhat compile
yarn hardhat test
yarn hardhat run scripts/deploy_classtoken.ts --network localhost
//ClassToken deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
```

Enter Hardhat console:
```bash
yarn hardhat console --network localhost
```

In the following tasks, we will run in interactive console.

---

## Task 1. Analyze response of state-change transaction

### Task 1.1 Get contract instance

Get a contract instance and add some constants and helpers:
```js
const address = '0x5FbDB2315678afecb367f032d93F642f64180aa3';
const token = await ethers.getContractAt("ClassToken", address);

account0='0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'
account1='0x70997970c51812dc3a010c7d01b50e0d17dc79c8'

parseEther=ethers.utils.parseEther
formatEther=ethers.utils.formatEther
```

Test whether the smart contract is working well:
```js
await token.symbol()
//'CLT'
await token.balanceOf(account0).then(r=>formatEther(r))
//'10000.0'
token.signer.address
//'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
```

It seems everything is OK. Let's go through the tasks.

### Task 1.2 Transfer CLT and analyze response

You can refer to `Ethers.js` docs on [this](https://docs.ethers.io/v5/api/contract/contract/#contract-functionsSend).

```js
response = await token.transfer(account1,parseEther('100.0'))
```

The response is:
```json
{
  hash: '0x1c7eab681e9e4b23485110d0d563eea9e4ae79e8f3114f4aaa86b56c7f423dd0',
...
  blockNumber: 2,
  transactionIndex: 0,
  confirmations: 1,
  from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
...
  to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  value: BigNumber { value: "0" },
  nonce: 1,
...
  chainId: 31337,
  wait: [Function (anonymous)]
}
```

There is a wait() function in the response object. By calling this function, we can get transaction `receipt` as in the `web3.js`.

### Task 1.3 Get transaction receipt

```js
receipt = await response.wait()

//check the events
receipt.events
```

There is only one event in it now, we can call functions of event to learn more about it.

```js
e= receipt.events[0]
await e.getBlock()
await e.getTransaction() 
await e.getTransactionReceipt()
```

### 1.4 smart contract method analysis

Ethers provide functions of analysis for smart contract methods, which means "analyze properties and results of a write method without actually executing it." 

Take "call static" for example: "ask a node to pretend that a call is not state-changing and return the result."

Smart contract object is "Meta-Class" in ethers: 

> A Meta-Class is a Class which has any of its properties determined at run-time. (via [Ethers.js Meta-Class](https://docs.ethers.io/v5/api/contract/contract/#Contract--metaclass))

```js
//estimate gas 
await token.estimateGas.transfer(account1,parseEther('100.0'))
//BigNumber { value: "35292" }

//call Static
await token.callStatic.transfer(account1,parseEther('100.0'))
//true
```

---

## Task 2 Understanding Smart contract events

### 2.1 ERC20 Events - Transfer

There are two events in ERC20 smart contract: 

> Transfer(from, to, value)
> Approval(owner, spender, value)

'Transfer' event is "Emitted when value tokens are moved from one account (from) to another (to)." (via [link](https://docs.openzeppelin.com/contracts/4.x/api/token/erc20#IERC20-Transfer-address-address-uint256-))

### 2.2 Do more transfer and retrieve all the events

Now, let's do 4 more transfer and get all 5 "Transfer" events in console

```js
await token.transfer(account1,parseEther('100.0'))
await token.transfer(account1,parseEther('100.0'))
await token.transfer(account1,parseEther('100.0'))
await token.transfer(account1,parseEther('100.0'))

//check balance
await token.balanceOf(account0).then(r=>formatEther(r))
//'9500.0'
await token.balanceOf(account1).then(r=>formatEther(r))
//'500.0'
```

There are [Events of ERC20 smart contract](https://docs.ethers.io/v5/api/contract/example/#erc20-events) examples in `Ethers.js` docs.

```js
//create event filter: transfer from account0
filterFrom = token.filters.Transfer(account0);

//query transfer from events in the last 10 blocks
events = await token.queryFilter(filterFrom, -10, "latest");

events.length
//5

```

### 2.3 Get Transfer event args 

Let's try to get all the Transfer to account0.

```js
//create event transfer: transfer to account0
filterTo = token.filters.Transfer(null,account0);
events.length
//1
```

There is only one Transfer event which transfer CLT token to account0. When ClassToken was deployed to the chain, 1000.0 CLT was minted and transferred from 0x0 to account0.

We can get the details: 

```js
txargs = events[0].args
//[
//  '0x0000000000000000000000000000000000000000',
//  '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
//  BigNumber { value: "10000000000000000000000" },
//  from: '0x0000000000000000000000000000000000000000',
//  to: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
//  value: BigNumber { value: "10000000000000000000000" }
//]
```

---

## Task 3: Listen to events

### Task 3.1 Set up a listener

In the current terminal where the Hardhat console is running, set up a listener to events.

```js
token.on(filterFrom, (from, to, amount, event) => {  console.log(event)});
```

The latest event is logged on the screen.

### Task 3.2 Run transfer in another Hardhat console 

```js
response = await token.transfer(account1,parseEther('100.0'))
```

### Task 3.3 Check the result in the first terminal

When you go back to the first terminal, you will see that the new event is logged on the screen.

---

## Task 4: Play more with Event listening

### Task 4.1 Check listeners and remove them all 

```js
token.listenerCount()
//1
token.removeAllListeners(filterFrom)
token.listenerCount()
//0
```

### Task 4.2 Set up once listener

```js
token.once(filterFrom,(from, to, amount, event) => {console.log(from,to,amount,event.blockNumber)})

token.listenerCount()
//1
```

### Task 4.3 Run transfer in another terminal and see events

Run transfer in another terminal
```js
response = await token.transfer(account1,parseEther('100.0'))
```

In the listener terminal, we will get: 
```js
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
0x70997970C51812dc3A010C7d01b50e0d17dc79C8 
BigNumber { value: "100000000000000000000" } 
16
```

Now the listenerCount is back to 0:
```js
token.listenerCount()
//0
```

`Web3.js` can also deal with events but it is quite different. You can also refer to [web3.js contract events related documents](https://web3js.readthedocs.io/en/v1.2.11/web3-eth-contract.html#contract-events).

Related links:

- Ethers.js docs on contract Events: https://docs.ethers.io/v5/api/contract/example/#erc20-events

- Node.js docs on Events: https://nodejs.org/api/events.html

---

Do you need more tasks about `Ethers.js`, leave a message. Maybe I can prepare it for you.

If you find this tutorial helpful, you can find me at Twitter [@fjun99](https://twitter.com/fjun99/)

---