Repository: ethereum/web3.py

Issue:
Add API to iterate through all events in a contract
### What was wrong?

No easy way to get all events for a contract (while still parsing the results). See [this StackExchange question](https://ethereum.stackexchange.com/questions/54473/how-to-read-allevents-using-python-web3-theres-capability-in-web3-js). One option is to iterate over all the events, but it's a bit awkward right now. I think the easiest way is:

```py
    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:

```py
    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.


Discussion:
+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.
Maybe we could publish a recipe on combining filters into a single object that can be polled.
>  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.
@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.
> 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.
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 

```python

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)

```
Ping @kclowes for an old issue that could use some love.
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.
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... 
@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.
https://github.com/ethereum/web3.py/commit/321e39bf86320d03c9718c782c393ce3f44cedbe#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

Resolving patch:
diff --git a/web3/contract/contract.py b/web3/contract/contract.py
--- a/web3/contract/contract.py
+++ b/web3/contract/contract.py
@@ -192,7 +192,9 @@ def get_logs(
             all_event_logs,
             argument_filters,
         )
-        return filtered_logs
+        sorted_logs = sorted(filtered_logs, key=lambda e: e["logIndex"])
+        sorted_logs = sorted(sorted_logs, key=lambda e: e["blockNumber"])
+        return sorted_logs
 
     @combomethod
     def create_filter(


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -46,9 +46,10 @@ def emitter_contract_data():
     return EMITTER_CONTRACT_DATA
 
 
+# This class defines events for the EmitterContract and are used to construct
+# a fixture for contract event logs. Parameterized tests that utilize an `emitter`
+# contract fixture will use this data.
 class LogFunctions:
-    # These appear to be for a very specific test and this doesn't need to be updated
-    # for every event in the emitter contract. That ends up breaking that test.
     LogAnonymous = 0
     LogNoArguments = 1
     LogSingleArg = 2
@@ -74,6 +75,9 @@ def emitter_contract_event_ids():
     return LogFunctions
 
 
+# This class defines topics for the EmitterContract and are used to construct
+# a fixture for contract event log topics. Parameterized tests that utilize
+# an `emitter` contract fixture will use this data.
 class LogTopics:
     LogAnonymous = event_signature_to_log_topic("LogAnonymous()")
     LogNoArguments = event_signature_to_log_topic("LogNoArguments()")
diff --git a/tests/core/contracts/test_extracting_event_data.py b/tests/core/contracts/test_extracting_event_data.py
--- a/tests/core/contracts/test_extracting_event_data.py
+++ b/tests/core/contracts/test_extracting_event_data.py
@@ -331,6 +331,78 @@ def test_argument_extraction_strict_bytes_types(
     assert event_data["event"] == "LogListArgs"
 
 
+def test_contract_event_get_logs_sorted_by_log_index(w3, emitter, request_mocker):
+    get_logs_response = [
+        {
+            "type": "mined",
+            "logIndex": 10,
+            "transactionIndex": 0,
+            "transactionHash": "0xaef7f312d863780b861d8c38984b2a33f77e9508810735e2b042143f7f189f83",  # noqa: E501
+            "blockHash": "0x2200ec3324fdaca4ee2f4629489d2d06fb28108dae61b63b84ef39702e2b64e7",  # noqa: E501
+            "blockNumber": 3,
+            "address": "0xF2E246BB76DF876Cef8b38ae84130F4F55De395b",
+            "data": "0x",
+            "topics": [
+                "0x1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c"
+            ],
+        },
+        {
+            "type": "mined",
+            "logIndex": 0,
+            "transactionIndex": 0,
+            "transactionHash": "0x61e57bb1b5af14ca1b0964a84fb640bf39927961f26311a6450475a749e00cbb",  # noqa: E501
+            "blockHash": "0x73dd9a3b0f581689ebd67adea0debe05672a334c723379dc506fb71a666c1754",  # noqa: E501
+            "blockNumber": 4,
+            "address": "0xF2E246BB76DF876Cef8b38ae84130F4F55De395b",
+            "data": "0x",
+            "topics": [
+                "0x1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c"
+            ],
+        },
+        {
+            "type": "mined",
+            "logIndex": 123,
+            "transactionIndex": 0,
+            "transactionHash": "0x61e57bb1b5af14ca1b0964a84fb640bf39927961f26311a6450475a749e00cbb",  # noqa: E501
+            "blockHash": "0x73dd9a3b0f581689ebd67adea0debe05672a334c723379dc506fb71a666c1754",  # noqa: E501
+            "blockNumber": 1,
+            "address": "0xF2E246BB76DF876Cef8b38ae84130F4F55De395b",
+            "data": "0x",
+            "topics": [
+                "0x1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c"
+            ],
+        },
+        {
+            "type": "mined",
+            "logIndex": 54,
+            "transactionIndex": 0,
+            "transactionHash": "0x61e57bb1b5af14ca1b0964a84fb640bf39927961f26311a6450475a749e00cbb",  # noqa: E501
+            "blockHash": "0x73dd9a3b0f581689ebd67adea0debe05672a334c723379dc506fb71a666c1754",  # noqa: E501
+            "blockNumber": 1,
+            "address": "0xF2E246BB76DF876Cef8b38ae84130F4F55De395b",
+            "data": "0x",
+            "topics": [
+                "0x1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c"
+            ],
+        },
+    ]
+
+    with request_mocker(w3, mock_results={"eth_getLogs": get_logs_response}):
+        logs = emitter.events.LogNoArguments().get_logs()
+
+        sorted_logs = sorted(
+            emitter.events.LogNoArguments().get_logs(),
+            key=lambda l: l["logIndex"],
+        )
+        sorted_logs = sorted(
+            emitter.events.LogNoArguments().get_logs(),
+            key=lambda l: l["blockNumber"],
+        )
+
+    assert len(logs) == 4
+    assert logs == sorted_logs
+
+
 @pytest.mark.parametrize(
     "contract_fn,event_name,call_args,expected_args,warning_msg,process_receipt",
     (