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

Contract Events Sorted by ContractEvent.logIndex and ContractEvent.blockNumber #3228

Merged
merged 5 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/web3.contract.rst
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,8 @@ For example:

Fetches all logs for a given event within the specified block range or block hash.

Returns a list of decoded event logs sorted by ``logIndex``.

``argument_filters`` is an optional dictionary argument that can be used to filter
for logs where the event's argument values match the values provided in the
dictionary. The keys must match the event argument names as they exist in the ABI.
Expand Down
1 change: 1 addition & 0 deletions newsfragments/3228.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Contract event ``get_logs`` results sorted by each ``ContractEvent`` ``logIndex``.
8 changes: 6 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()")
Expand Down
72 changes: 72 additions & 0 deletions tests/core/contracts/test_extracting_event_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
(
Expand Down
4 changes: 3 additions & 1 deletion web3/contract/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down