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

Add API to iterate through all events in a contract #961

Closed
carver opened this issue Jul 16, 2018 · 10 comments · Fixed by #3228
Closed

Add API to iterate through all events in a contract #961

carver opened this issue Jul 16, 2018 · 10 comments · Fixed by #3228

Comments

@carver
Copy link
Collaborator

carver commented Jul 16, 2018

What was wrong?

No easy way to get all events for a contract (while still parsing the results). See this StackExchange question. One option is to iterate over all the events, but it's a bit awkward right now. I think the easiest way is:

    from web3.contract import ContractEvent
    
    filters = [
      event.createFilter(fromBlock='latest')
      for event in myContract.events
      if isinstance(event, ContractEvent)
    ]

How can it be fixed?

Some options:

  • Implement __iter__ on Contract.events to iterate through all events in the ABI (my favorite option, except that it's inconsistent with contract.functions, which is doing the wrong thing IMO)
  • Add a new Contract.all_events() equivalent to Contract.all_functions()

Then the example changes to:

    filters = [
      event.createFilter(fromBlock='latest')
      for event in myContract.events
    ]

Of course, we could also implement contract.create_filter() like web3.js's contract.events.allEvents. I kind of like that the filters are event specific right now, though. I don't think it's too big a deal to require callers to write a filter loop on events.

@dylanjw
Copy link
Contributor

dylanjw commented Jul 17, 2018

+1 for implementing __iter__. I need to think about all_events. While I see the use, it would be a simple filter with just the contract address, and not allow one to filter on arguments or topics. It might be better to just steer users towards instantiating filter object with just the contract address rather than providing a helper function to do the same.

@dylanjw
Copy link
Contributor

dylanjw commented Jul 17, 2018

Maybe we could publish a recipe on combining filters into a single object that can be polled.

@carver
Copy link
Collaborator Author

carver commented Jul 17, 2018

It might be better to just steer users towards instantiating filter object with just the contract address rather than providing a helper function to do the same.

The key difference is that they want the automatic argument decoding, which they don't get with the generic filter on an address. I like the recipe idea.

@dylanjw dylanjw self-assigned this Aug 9, 2018
@dylanjw
Copy link
Contributor

dylanjw commented Aug 22, 2018

@carver

Implement iter on Contract.events to iterate through all events in the ABI (my favorite option, except that it's inconsistent with contract.functions, which is doing the wrong thing IMO)

__iter__() has been implemented in ContractFunctions. Is there something specific about that implementation that you would like to see changed? This might be a good time to figure out a consistent approach for both functions and events.

@carver
Copy link
Collaborator Author

carver commented Aug 23, 2018

Is there something specific about that implementation that you would like to see changed?

Yeah, it's only returning the name (str) of the function. I think it should return the full ContractFunction object.

@HarryR
Copy link

HarryR commented Jan 3, 2021

Wow, this ticket is still open! Here is a snippet that emits all decode-able events for a specific contract. I too found this was an annoyance @carver

from web3.contract import Contract
from web3._utils.events import get_event_data
from eth_utils import event_abi_to_log_topic


def fetch_events(contract: Contract, fromBlock, toBlock=None):
    topic2abi = {event_abi_to_log_topic(_): _
                 for _ in contract.abi if _['type'] == 'event'}

    logs = w3.eth.getLogs(dict(
        address=contract.address,
        fromBlock=fromBlock,
        toBlock=toBlock
    ))

    for entry in logs:
        topic0 = entry['topics'][0]
        if topic0 in topic2abi:
            yield get_event_data(w3.codec, topic2abi[topic0], entry)

@carver
Copy link
Collaborator Author

carver commented Jan 5, 2021

Ping @kclowes for an old issue that could use some love.

@jpiabrantes
Copy link

Getting all the events sorted by their log index would be really helpful. Sometimes, two different events are emitted on the same block. With the current solution, sometimes I can find the latest emitted event first instead of finding the oldest emitted event first.

@HarryR
Copy link

HarryR commented May 20, 2023

Resurrecting this issue as something needs to be done to make web3.py more usable, I keep running into this issue and a few others where all the straightforward intuitive things that you'd expect to just work... don't...

@reedsa
Copy link
Contributor

reedsa commented Jan 18, 2024

@fselmo @kclowes @pacrob Did some sleuthing and found an iterator over contract events has been implemented as of 2019, where each event is returned within the iterator.
321e39b#diff-acb0927c7310cb122dc0a59c91a638cabbec4625ece1eab48c0870ba197a0af9R1

Remaining:

  • Event data utility get_event_data_for_log_topic Event decoded, cannot go any further for strings (represented as bytes)
  • Return events sorted by log index
  • Update function iterator to return the function rather than just the name

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

Successfully merging a pull request may close this issue.

6 participants