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.
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:
ERC20 smart contract "ClassToken" with deploy and test scripts.
Run Hardhat Network (local testnet) in another terminal by running
yarn hardhat node
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 Hardhat console:
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:
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:
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.
response = await token.transfer(account1,parseEther('100.0'))
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)]
}
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
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()
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
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's 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'
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
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
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" }
//]
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.
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
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
token.listenerCount()
//1
token.removeAllListeners(filterFrom)
token.listenerCount()
//0
Task 4.2 Set up once listener
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
response = await token.transfer(account1,parseEther('100.0'))
In the listener terminal, we will get:
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
0x70997970C51812dc3A010C7d01b50e0d17dc79C8
BigNumber { value: "100000000000000000000" }
16
Now the listenerCount is back to 0:
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.
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
Top comments (0)