Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listening for events #492

Closed
ochikov opened this issue Apr 16, 2019 · 12 comments
Closed

Listening for events #492

ochikov opened this issue Apr 16, 2019 · 12 comments

Comments

@ochikov
Copy link

ochikov commented Apr 16, 2019

Hello,
Does anybody know how to listen for events with ethers.js.
The following code is implemented using web3:

await this.contract.getPastEvents("BlockCreated", { fromBlock, toBlock });
await this.token.getPastEvents("Transfer", { filter: { to: contractAddress }, fromBlock, toBlock });

but I need to do it with ethers.js.

Thank you.

@mrwillis
Copy link

Hey there. You can do something like this (for events in the future)

contract.on("Transfer", (to, amount, from) => {
    console.log(to, amount, from);
});

To read past events, you can do something like

      const filter = {
        address: contractAddress,
        fromBlock: 0,
        toBlock: 10000,
        topics: [contract.interface.events.MyEvent.topic]
      };
      const logs = await provider.getLogs(filter);

Get the topic from the ethers.js Contract wrapper. Above, contract is the wrapper and there is an event called MyEvent on that contract.

@reuptaken
Copy link

@mrwillis Could you elaborate a bit how does the first example works internally? How is the loop created, how to control it?

I'm using similar code plus provider.resetEventsBlock(someBlock) to read past events and then it keeps listening to new events, but my node gets overloaded. Is there a way to control this? Throttle perhaps?

@ricmoo
Copy link
Member

ricmoo commented Apr 22, 2019

When you call resetEventsBlock, it does optimize log events, and fetch them all at once using a filter with the from block tag.

If you need to pagination it more, you should use the provider.getLogs, with fromBlock and toBlock, spanning the block size you want to process as a batch.

Make sense?

@ochikov
Copy link
Author

ochikov commented Apr 22, 2019

Hello @ricmoo @mrwillis
I have the following code written with web3:

this.tokenFilter = this.token.events.Transfer({ filter: { to: contractAddress } })
        this.tokenFilter.on("data", event => {
            this.state.lastBlockNumber = +event.blockNumber
            replayEvent(this.plasma, event).catch(this.error)
            return this.store.saveState(this.state).catch(this.error)
        })
        this.tokenFilter.on("changed", event => { this.error("Event removed in re-org!", event) })
        this.tokenFilter.on("error", this.error)

And the returned structure is like this:

 logIndex: 0,
  transactionIndex: 0,
  transactionHash: '0xb09d02d588476f1a31e4afb23248a9693235f8da67517f9b73a5bbc85396e784',
  blockHash: '0xab0e5eb8197d264dbb95e89643c3c3f4cc765b78b3fd17244a33ea238a36d680',
  blockNumber: 3,
  address: '0xbAA81A0179015bE47Ad439566374F2Bae098686F',
  type: 'mined',
  id: 'log_746cf57b',
  returnValues: 
   Result {
     '0': '0xa3d1F77ACfF0060F7213D7BF3c7fEC78df847De1',
     '1': '0xEAA002f7Dc60178B6103f8617Be45a9D3df659B6',
     '2': '10000000000000000000',
     from: '0xa3d1F77ACfF0060F7213D7BF3c7fEC78df847De1',
     to: '0xEAA002f7Dc60178B6103f8617Be45a9D3df659B6',
     value: '10000000000000000000' },
  event: 'Transfer',
  signature: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
  raw: 
   { data: '0x0000000000000000000000000000000000000000000000008ac7230489e80000',
     topics: 
      [ '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
        '0x000000000000000000000000a3d1f77acff0060f7213d7bf3c7fec78df847de1',
        '0x000000000000000000000000eaa002f7dc60178b6103f8617be45a9d3df659b6' ] } }

I tried to use the provided examples above for listening the future events, but all I am able to catch is the to, amount, from.
Could you help me a little bit, how to do it with ethers.js ?
Thank you.

@reuptaken
Copy link

Make sense?

Thanks. I have yet to digest how to work with provider.getLogs.

Can you explain how does contract.on works? What makes the script waiting for upcoming events as opposed to just finish execution when contract.on is defined?

@reuptaken
Copy link

I've tried it, but I don't understand how one could process events from the received logs. I used contract.on before.

@reuptaken
Copy link

So I managed to do this using something like:

let provider = new ethers.providers.JsonRpcProvider();
let iface = new ethers.utils.Interface(My.abi);

provider.getLogs({
    fromBlock: 7613948,
    toBlock: 7614777,
    address: exchangeAddress
}).then((result) => {
    let events = result.map((log) => iface.parseLog(log));
    events.forEach((e) => console.log(e));
}).catch((err) => {
    console.log(err)
});

But I have more broad question to @ricmoo (or other person who could perhaps answer it).

Previously I used provider.resetEventsBlock(someBlock) and then added event listeners. It looked much more elegant to me, but I had to switch to .getLogs() because of performance problems with a node. So the question is: are you planning some solution to those problems (throttling? upper block number limit?) in future version? If so, I'd happily wait for a new version.

@mhoangvslev
Copy link

@reuptaken Possible answer at #505 ?

@ricmoo
Copy link
Member

ricmoo commented May 11, 2019

So, internally all it does is query from the last queried block to the current block number (on every block). This works in general, but can certainly cause issues with resetBlockNumber, since you are asking for a much larger range.

There isn’t really a good way to break up the block ranges in the library. Any max block span could still suffer from the same problem, which is too many events in within that span (e.g. ICO airdrop results in MANY events, even a block span of a week could be too many). And any max span would be far too small for most events, since most events are sparse. A span of a week would likely take hours to days to fetch all the events, when a single shot would have returns the 10 results in about 4 seconds.

If you produce a system that need historic access to events, it is probably best to build a caching service. Also, I will be looking into the Alchemy event API more, which may have something that could help in these cases.

I had not thought of this, but in talks regarding Eth2.0, I will bring up pagination for lots in the kernels.

I do have a new idea that is kinda interesting, and I might experiment with it, even if just to write a blog... have an eventBlock event, which triggers every 200 events (the contract would track this and emit the additional event). Then anything interested in the events, can query this sparser event to know what ranges to target. I’m not actually suggesting this, as it is only a little-c solution, just something you made me think about. :)

@ricmoo
Copy link
Member

ricmoo commented Jun 3, 2020

I think this issue is mostly resolved and stale, so I'm going to close it. If you have further issues though, please feel free to re-open it.

As a quick note, the v5 API makes this much simpler. With a Contract, you can use the contract.queryFilter(filter, fromBlock, toBlock), which will properly parse everything for you. :)

There still isn't a built-in useful way to paginate against the JSON-RPC, but that's the best we can do for now.

Thanks! :)

@hyndsite
Copy link

hyndsite commented Apr 23, 2021

@ricmoo but if we want to subscribe to events, and only get for future events, there is no way to do that with contract.on() correct? queryFilter is only going to allow for a polling approach as opposed to being notified.

The only way I have found for this to work, is to get the current block number getBlockNumber and check on every fired event that I am subscribed to (contract.on(filter, callback)) and react to only block number above the current block number.

@mrwillis previous example of getting only future events, does produce events from the past as well.

@ricmoo
Copy link
Member

ricmoo commented Apr 28, 2021

@hyndsite please check out this post and let me know if that helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants