How I can get past events using ethers.js ?
Something like https://ethereum.stackexchange.com/questions/16313/how-can-i-view-event-logs-for-an-ethereum-contract?answertab=votes#tab-top
Heya!
I do need to add this to the documentation...
You can use the following to achieve this:
provider.resetEventsBlock(0)
which will begin processing all events from block 0 forward. Keep in mind, that INFURA nodes may not provider the entire history, and even a local node may have issues with this. There isn't currently a great solution to the performance issue, but the Geth team is working on improving this.
You can specify any block number, so if you know the block you are interested in, that might improve its reliability as well. Certainly, there is no need to look at contract events before the contract's deployment, for example. And if you keep a local cache, you have an idea where to start from.
Hey @ricmoo!
If were to use provider.resetEventsBlock(fromBlock) to run Contract event callbacks starting at fromBlock, would calling provider.getBlockNumber() in the event callback return the correct block numbers?
The provider.getBlockNumber() will hit the Ethereum node for the current block, so yes the correct block number will be returned; it doesn't use the internally cached lastBlockNumber.
It will regenerate all events though, including block, address (balance changed) and transaction (mined) events. For transaction events, you should generally use provider.once instead of provider.on, since they are usually only mined once.
I do need to figure out a cleaner interface. If you need more fine grained control, you can use the Interface from your Contract. If you , for example, had a function called someEvent(address), your contract.interface.events.someevent is an object that can parse the results from provider.getLogs. This is more advanced, so let me know if you need more specifics for this method.
I will mull over other options (and feel free to suggest some) for how they API could handle this.
Hey @ricmoo,
If I pass BigNumber into provider.resetEventsBlock(fromBlock), when the function doPoll() happened, it will not add the BigNumber correct lastBlockNumber + 1 inside the for loop. The result will be treat it as string to append "1".
Block numbers should be specified as normal JavaScript numbers.
They support going to the year 4,284,246,220... Give or take. :)
But I should add more checks to make sure variable types are passed in correctly. This will be coming with the TypeScript migration; much tighter variable type checking.
Hey @ricmoo,
If you , for example, had a function called
someEvent(address), yourcontract.interface.events.someeventis an object that can parse the results fromprovider.getLogs. This is more advanced, so let me know if you need more specifics for this method.
I'm trying to figure out how contract.interface.events.someevent object could help me parse the results from provider.getLogs, however, I'm stuck. How do you suggest approaching that?
There is also a contract.interface.parseLog(log) function, which will help identify all the things you care about, which may be easier... Just pass in the log entry. :)
Otherwise, the contract.interface.events,someEvent.decode(data, topics) is what you will likely need. But I recommend trying out the parseLog first.
Thanks @ricmoo ! parseLog was very helpful.
I think the resetEventsBlock and new contract.filter.eventName has solved this for most people. If not, please feel free to re-open this issue.
Thanks! :)
I have a specific use-case where I'm trying to query multiple past events simultaneously. Running provider.resetEventsBlock(0) doesn't really work because you get a race condition where the check for one event will reset the provider, but the check for another event will reset the provider again. If things go wrong then you end up missing events.
Currently I'm handling this by creating a new provider for each event, but that doesn't seem particularly scalable. Any suggestions on handling this?
Also doesn't seem like this plays particularly nicely with getting events between two specific blocks. Client might get a ton of more recent events and have to filter them out manually instead of just getting the events for specific blocks.
@kfichter - maybe check out the recipe for using etherscan to get all transactions for an address:
https://docs.ethers.io/ethers.js/html/cookbook-accounts.html#get-transaction-history
You will likely need to do your filtering yourself, but it should avoid the race condition
How is it possible to fetch all events by signature for all possible smart contract addresses?
Quick note: If you want all possible smart contract addresses, you can simply omit the address and just include the topics. The array of addresses is only necessary to filter based on those addresses. :)
Would be great if resetEventsBlock would return a promise that resolves once it's done getting past events and calling the callbacks; maybe also if the callbacks return a promise, then waiting on those promises before resolving. Now it seems it's not knowable to the caller of resetEventsBlock when the "playback" completes, although internally the function would query the past events, then resume polling, right?
To answer @gazambuja the original poster, there's also provider.getLogs documented in https://docs.ethers.io/ethers.js/html/api-providers.html#contract-state
Downside is, you need to do the log parsing yourself with parseLogs documented in https://docs.ethers.io/ethers.js/html/api-advanced.html#parsing-objects (as explained above :)
Also, if you use v5, you can use contract.queryFilter(filter [ , fromBlock [ , toBlock ] ] ), which will include the parsed events. :)
Still using v4, so this may or may not apply to v5.
An odd thing in Contract.parseLog (for querying) and Contract.on (for listening) is differences in naming:
parseLog seems to put the event name in .name while on puts it in .eventparseLog puts arguments in .values while on puts them in .argsDetails, but it would be nice to have consistency in how the events look.
(EDIT: I agree that event.event just looks stupid, and event.name looks good, but ofc the question is if changing event member names at .on is a good idea (no, breaks compatibility), or if adding a redundant .name == .event is less bad... maybe adding a redundant .event == .name to parseLogs would be less bad, since parseLogs is a helper)
And as an answer to "If anonymous, and the only method, and the input count matches, should we parse and return it?", YES, I think in this kind of edge cases it might even be better to err on the magic side, if the alternative is just to silently go ¯\_(ツ)_/¯ (ofc I'm not a proponent of silent type coercion or similar that should flag an error :)
EDIT: Obviously it's a tradeoff of what kind of "why doesn't this work" questions you'll have to answer then :D and since I'm not the answering the questions, I'm also not calling the shots...
Most helpful comment
Also, if you use v5, you can use
contract.queryFilter(filter [ , fromBlock [ , toBlock ] ] ), which will include the parsed events. :)