Ethers.js: Watch for and parse events across multiple instances of a contract

Created on 14 Oct 2020  路  6Comments  路  Source: ethers-io/ethers.js

I hope all is well. I have a specific use case that I'm working through right now that essentially follows a factory pattern:

I deploy a Factory contract that deploys new instances of another contract that users interact with. My challenge is that because there are potentially hundreds of these spawned contracts, trying to react to events that are emitted from them is not feasible using the Contract API as normal. It would require an ever growing list of open connections for each contract instance.

I wanted to see if I can use the Event / Topics functionality to do this, where I can catch ANY event that matches the Topic regardless of contract address? Basically, I need a global event handler that can run in a process and respond to various events across all the instances of the user contract.

For example, let's say I emit these events from the contracts users use:

event HelloWorld(string greeting)
event CreateElement(string elementId, address sender)

In a Node.js API for example, could I use Ethers to watch the logs for any events that match these criteria and pull the values out of those event emissions?

Notionally, I tried to use the provider.on() syntax which was able to react to the event, but I didn't see any of the data points to extract from the event.

let filter = {
    topics: [
        ethers.utils.id("HelloWorld(string)"),
        ethers.utils.id("CreateElement(string,address)")
    ]
}

provider.on(filter, (log, event) => {
    // Emitted whenever the above topic(s) occur
    console.log("results of event logging", log, event)
})

If this is the proper syntax above, how can I extract the contents of the event in the callback? Thanks for the help in advance :)

Most helpful comment

Oh yeah, I just noticed that you had (log, event) => { there. I assume event is the parsed version of the log. I don't think provider can give you a parsed log, as provider does not know how to parse it (it needs an interface) so it cannot technically parse it and give you. You will only get a log and then you need an interface to parse it:

provider.on(filter, (log) => {
    // Emitted whenever the above topic(s) occur
    const event = interface.parseLog(log);
    console.log("results of event logging", log, event)
})

Edit: Just curious on where did you see (log, event) => {? Or was it something you were trying?

All 6 comments

You can add options in topic filter by using array of arrays:

let filter = {
    topics: [
        [ethers.utils.id("HelloWorld(string)"), ethers.utils.id("CreateElement(string,address)")]
    ]
}

Both HelloWorld and CreateElement should match to this filter. But can you try if this works in the provider.on? Just in case if it didn't work, provider.getLogs should.

You can add options in topic filter by using array of arrays:

let filter = {
    topics: [
        [ethers.utils.id("HelloWorld(string)"), ethers.utils.id("CreateElement(string,address)")]
    ]
}

Can you try if this works in the provider.on?

It will catch these events, yes, but what is captured in the callback (log, event) is just the transaction object and event is undefined. My issue now is figuring out how to get the values out of these events when they are indeed caught.

Oh yeah, I just noticed that you had (log, event) => { there. I assume event is the parsed version of the log. I don't think provider can give you a parsed log, as provider does not know how to parse it (it needs an interface) so it cannot technically parse it and give you. You will only get a log and then you need an interface to parse it:

provider.on(filter, (log) => {
    // Emitted whenever the above topic(s) occur
    const event = interface.parseLog(log);
    console.log("results of event logging", log, event)
})

Edit: Just curious on where did you see (log, event) => {? Or was it something you were trying?

Makes perfect sense, that's what I was looking for I must have missed it in the documentation. This should work. I will report back ASAP

thank you!

this works, thanks so much for the help. I missed the interface fragment stuff in docs.

Oh yeah, I just noticed that you had (log, event) => { there. I assume event is the parsed version of the log. I don't think provider can give you a parsed log, as provider does not know how to parse it (it needs an interface) so it cannot technically parse it and give you. You will only get a log and then you need an interface to parse it:

provider.on(filter, (log) => {
    // Emitted whenever the above topic(s) occur
    const event = interface.parseLog(log);
    console.log("results of event logging", log, event)
})

Edit: Just curious on where did you see (log, event) => {? Or was it something you were trying?

BTW, (log,event) are args in the new v5 docs for provider event handling. Example ---> https://docs.ethers.io/v5/api/providers/provider/#Provider--event-methods

excerpt from that section:

// This filter could also be generated with the Contract or
// Interface API. If address is not specified, any address
// matches and if topics is not specified, any log matches
filter = {
    address: "dai.tokens.ethers.eth",
    topics: [
        utils.id("Transfer(address,address,uint256)")
    ]
}
provider.on(filter, (log, event) => {
    // Emitted whenever a DAI token transfer occurs
})
Was this page helpful?
0 / 5 - 0 ratings

Related issues

lastmjs picture lastmjs  路  31Comments

ruzpuz picture ruzpuz  路  26Comments

Levino picture Levino  路  35Comments

282931 picture 282931  路  42Comments

pkieltyka picture pkieltyka  路  36Comments