DEV Community

fangjun
fangjun

Posted on • Updated on

04 Understanding Blockchain with `ethers.js`: 4 Tasks of Smart Contract Events

Events are very, very important of smart contract and blockchain. Ethers.js also provide some features to deal with this. There are actually 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. We can listen to events to get the results off-chain.

Image description

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.


Prerequsites

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 serie:

  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:

yarn hardhat compile
yarn hardhat test
yarn hardhat run scripts/deploy_classtoken.ts --network localhost
//ClassToken deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Enter fullscreen mode Exit fullscreen mode

Enter Hardhat console:

yarn hardhat console --network localhost
Enter fullscreen mode Exit fullscreen mode

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 contants and helpers:

const address = '0x5FbDB2315678afecb367f032d93F642f64180aa3';
const token = await ethers.getContractAt("ClassToken", address);

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

parseEther=ethers.utils.parseEther
formatEther=ethers.utils.formatEther
Enter fullscreen mode Exit fullscreen mode

Test whether the smart contract is working well:

await token.symbol()
//'CLT'
await token.balanceOf(account0).then(r=>formatEther(r))
//'10000.0'
token.signer.address
//'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
Enter fullscreen mode Exit fullscreen mode

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.

response = await token.transfer(account1,parseEther('100.0'))
Enter fullscreen mode Exit fullscreen mode

The response is:

{
  hash: '0x1c7eab681e9e4b23485110d0d563eea9e4ae79e8f3114f4aaa86b56c7f423dd0',
...
  blockNumber: 2,
  transactionIndex: 0,
  confirmations: 1,
  from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
...
  to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
  value: BigNumber { value: "0" },
  nonce: 1,
...
  chainId: 31337,
  wait: [Function (anonymous)]
}
Enter fullscreen mode Exit fullscreen mode

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

receipt = await response.wait()

//check the events
receipt.events
Enter fullscreen mode Exit fullscreen mode

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

e= receipt.events[0]
await e.getBlock()
await e.getTransaction() 
await e.getTransactionReceipt()
Enter fullscreen mode Exit fullscreen mode

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)

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

//call Static
await token.callStatic.transfer(account1,parseEther('100.0'))
//true
Enter fullscreen mode Exit fullscreen mode

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)

2.2 Do more transfer and retrieve all the events

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

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'
Enter fullscreen mode Exit fullscreen mode

There are Events of ERC20 smart contract examples in Ethers.js docs.

//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

Enter fullscreen mode Exit fullscreen mode

2.3 Get Transfer event args

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

//create event transfer: transfer to account0
filterTo = token.filters.Transfer(null,account0);
events.length
//1
Enter fullscreen mode Exit fullscreen mode

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:

txargs = events[0].args
//[
//  '0x0000000000000000000000000000000000000000',
//  '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
//  BigNumber { value: "10000000000000000000000" },
//  from: '0x0000000000000000000000000000000000000000',
//  to: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
//  value: BigNumber { value: "10000000000000000000000" }
//]
Enter fullscreen mode Exit fullscreen mode

Task 3: Listen to events

Task 3.1 Set up a listener

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

token.on(filterFrom, (from, to, amount, event) => {  console.log(event)});
Enter fullscreen mode Exit fullscreen mode

The latest event is logged on the screen.

Task 3.2 Run transfer in another Hardhard console

response = await token.transfer(account1,parseEther('100.0'))
Enter fullscreen mode Exit fullscreen mode

Task 3.3 Check the result in the first terminal

When we go back to the first terminal, we 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

token.listenerCount()
//1
token.removeAllListeners(filterFrom)
token.listenerCount()
//0
Enter fullscreen mode Exit fullscreen mode

Task 4.2 Set up once listener

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

token.listenerCount()
//1
Enter fullscreen mode Exit fullscreen mode

Task 4.3 Run transfer in another terminal and see events

Run transfer in another terminal

response = await token.transfer(account1,parseEther('100.0'))
Enter fullscreen mode Exit fullscreen mode

In the listener terminal, we will get:

0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
0x70997970C51812dc3A010C7d01b50e0d17dc79C8 
BigNumber { value: "100000000000000000000" } 
16
Enter fullscreen mode Exit fullscreen mode

Now the listenerCount is back to 0:

token.listenerCount()
//0
Enter fullscreen mode Exit fullscreen mode

Web3.js can also deal with events but it is quite different. You can also refer to web3.js contract events related documents.

Related links:


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


Discussion (0)