Web3.js – How to Asynchronously Wait for Contract.events.MyEvent Data in Web3 1.0

web3jsweb3js-v1.x

The documentation is sparse, and the examples all mix async, arrow callbacks, and promises. One problem I've run into is while subscribing to an event, I can't seem use asynchronous syntax within or outside the method. There is also odd behavior in the way it returns data that could be affecting this. The method not only is used to lookup events, but also to listen to them in the future.

The documented way to subscribe to an event in Web3 1.0 (https://web3js.readthedocs.io/en/v1.2.0/web3-eth-contract.html#events):

myContract.events.MyEvent({
    filter: {myIndexedParam: [20,23], myOtherIndexedParam: '0x123456789...'}, // Using an array means OR: e.g. 20 or 23
    fromBlock: 0
}, function(error, event){ console.log(event); })
.on('data', function(event){
    console.log(event); // same results as the optional callback above
})
.on('changed', function(event){
    // remove event from local database
})
.on('error', console.error);

It works, but for production most people will probably want to do other things first. I need to listen to the current block to pass it into the event subscription so I can get the last ten blocks. After the data for the initial event query comes back, I need to fetch the current nonce of my wallet, and then iterate over the returned addresses to then sign them. After that, it becomes a passive listener, and so I set scriptHasBeenLoaded to true so that new events coming in take a different logical path.

Here is my current code:

getBlockNumber()

function getBlockNumber() {
    web3.eth.getBlockNumber().then((result) => {
        startingBlock = result
        console.log('*** Starting Block is', startingBlock, '***')
        startListening()
    })
}

function startListening() {
contract.events.Approval({
            fromBlock: startingBlock - 10,
            toBlock: 'latest'
        },
        (error, events) => {
            if (error) { console.log("*** Error Retrieving Addresses ***") }
            subscriberAddresses.push(events.returnValues.src)
            if (scriptHasBeenLoaded === true) {
                retrieveApprovals()
            }
        })
        getNonce()
}

function getNonce() {
    web3.eth.getTransactionCount(account, 'pending').then((result) => {
        console.log('*** Initial Nonce is', result, '***')
        nonceCounter = result
        console.log('*** Listening for Approvals ***')
        ...theBusinessEndFunctions()
    })
}

How can I wait for the events to come back, and then fire getNonce()?

Everything I do seems to bork the script. Currently, getNonce() is just in line. The only work around I have is setTimeout, which works, but it is the anti-solution. 🙁

Edit: getPastEvents is not a solution or workaround after further testing.

Best Answer

You could create an async function, put all you code inside and await for the individual requests results:

async function myLogic() {

    var startingBlock;
    await web3.eth.getBlockNumber()
        .then((number)=>{
            startingBlock = number;            
        });


    await  contract.getPastEvents('Approval',{
            fromBlock: startingBlock - 10,
            toBlock: 'latest'
            },
        (error, events) => {...})


    await  web3.eth.getTransactionCount(account, 'pending').then((result) => {...})
    // some other logic
}

myLogic()

Hope this helps

Related Topic