Posted By : Amit
On a blockchain, smart contracts are self-executing contracts. Without the use of middlemen, they enable transparent and safe transactions. The capacity to emit events when specific conditions are met is one of the essential characteristics of smart contracts. External apps can listen to and respond to these events, giving developers the ability to create decentralized, real-time applications. In this tutorial, we will explore smart contract listeners in Web3.js.
Here are all the subscription functions provided by Web3.js to listen to events emitted by a smart contract.
a) myContract.once(event[, options], callback) - to fetch an event only once returns undefined
b) myContract.events.MyEvent([options][, callback]) - to fetch events named "MyEvent", returns an eventEmitter
c) myContract.events.allEvents([options][, callback]) - to fetch all events continuously, also returns an EventEmitter
d) myContract.getPastEvents(event[, options][, callback]) - to fetch all past events available in the block range, returns a list of event objects
To use all of these subscription functions, we need to first create a contract instance using web3 like done here:-
Now suppose the contract we are interacting with is an ERC20 token and has a Transfer event. All of these functions have
parameters very similar to the following syntax options object with the following properties, which may or may not be optional depending on the functions
a) filter - Object (optional): Lets you filter events by indexed parameters, for example,
b) fromBlock - Number (optional): The block number from which to get events on
c) toBlock - Number (optional): The block number to get events up to (Defaults to "latest").
d) topics - Array (optional): This allows manually setting the topics for the event filter
//Transfer event emitted on an ERC20 token consisting "0x8aB7e3f2A693268348CcBf9111dEEd5dd3640c11" as the indexed from parameters
{filter:{from:"0x8aB7e3f2A693268348CcBf9111dEEd5dd3640c11"}}
//required to specify the block number from which to start fetching events.
fromBlock : 100000
//similar to fromBlock , the upper limit to the block number range
toBlock : 200000
//complete object to pass to the listener function
let options = {filter,fromBlock,toBlock}
The event object has the following details -
a) event - event name
b) address - address that generated the event
c) transactionHash - transaction hash of the transaction that resulted in the emission of this event
d) blockHash - hash of the block in which the transaction got mined, null if a transaction is pending
e) returnValues - an object consisting of all parameters in the event signature
for example for a transfer event in an erc20 token, they would depict from, to, and value named parameters
f) blockNumber
g) logIndex - index differentiating all the events emitted from this transaction, and others
An example depicting subscription of the events and reading the event object is the following:-
let contractSubObject = contractInstance.events.allEvents({
fromBlock: '2300000',
})
contractSubObject.on('connected', function (subId) {
logger.debug("New contract subscription active for %s with sub id %s", candidateAddress , subId);
contractSubscriptionDetail.subscriptionObject = contractSubObject
})
contractSubObject.on('data',function (event) {
saveEventTransaction(event,candidateAddress)
})
function saveEventTransaction(event, candidateAddress) {
let eventName = event.event.toLowerCase()
let transaction = new db.Transaction({
hash: event.transactionHash,
blockNumber: event.blockNumber,
logIndex: event.logIndex,
event: eventName,
candidate: candidateAddress,
})
if(eventName.includes("stake")){
transaction.staker = event.returnValues._staker;
transaction.capacity = event.returnValues._amount;
}else{
transaction.staker = event.returnValues._owner;
transaction.capacity = event.returnValues._cap;
transaction.blockNumber = event.returnValues._blockNumber;
}
web3HttpInstance.eth.getBlock(event.blockNumber,function(err,data){
let createdAt = moment.unix(data.timestamp).utc()
transaction.createdAt = createdAt;
if(!err){
transaction.save(function (ex, tx) {
if (ex) {
logger.error("Error saving event %s with hash %s on candidate address %s , error %s", eventName, event.transactionHash,
candidateAddress, ex)
} else {
logger.debug("saved event %s with hash %s on candidate address %s", eventName, event.transactionHash, candidateAddress);
}
})
}else{
logger.error("GetBlock Related Error saving event %s with hash %s on candidate address %s , error %s", eventName, event.transactionHash,
candidateAddress, ex)
}
})
}
For the myEvent and allEvents subscribers, we can also resubscribe this after a certain interval of time, so that we
guarantee fetching events in case of our process Event function has failed.
For this, we need access to the subscriptionObject that you saw in the above code.
You may also like | A Developer Guide to Smart Contract ABI
function resubscribeAllListeners(){
for(let cslItem of contractSubscriptionList){
updateSubscription(cslItem)
}
}
function updateSubscription(subItem) {
logger.debug("Resubscribing allEventListener sub for candidate %s subObject %o", subItem.address, subItem.subscriptionObject)
let olderSubscription = subItem.subscriptionObject;
olderSubscription.unsubscribe(function(err,data){
if(err){
logger.error("Error unsubscribing allEventListener with id %s , and candidate %s", olderSubscription.id,subItem.address)
}else{
if(data === true){
logger.debug(`unsubscribed allEventListener with id ${olderSubscription.id} for candidate ${subItem.address}`);
let contractInstance = new web3WsInstance.eth.Contract(candidateABI,subItem.address);
let contractSubObject = contractInstance.events.allEvents({fromBlock: 'genesis',})
contractSubObject.on('connected', function (subId) {
logger.debug("Resubscription now active with sub id %s for candidate %s ", subId, subItem.address)
subItem.subscriptionObject = contractSubObject
})
//let the events now be processed using this function
contractSubObject.on('data', function (event) {
saveEventTransaction(event,subItem.address)
})
}
}
})
}
For further details look into the contract topic on web3.js documentation at - web3-contract
Subscription of events will require a Web3 instance using a WebSocket provider, for that look into the web3-provider options.
For more information related to Ethereum smart contract development, you may connect with our blockchain developers.
November 21, 2024 at 11:17 am
Your comment is awaiting moderation.