Skip to content

Commit 24b5065

Browse files
author
Stuart Reed
committed
Contract APIs for event elements in a contract ABI
Contract utils for `get_event_by_name`, `get_event_by_signature`, `get_event_by_selector`, `get_event_by_topic` Contract utils for `find_events_by_name`, `find_events_by_signature`, `find_events_by_selector`, `find_events_by_topic`
1 parent 6f923ee commit 24b5065

File tree

10 files changed

+659
-96
lines changed

10 files changed

+659
-96
lines changed

docs/web3.contract.rst

Lines changed: 174 additions & 54 deletions
Large diffs are not rendered by default.
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import pytest
2+
from typing import (
3+
Any,
4+
Callable,
5+
Sequence,
6+
)
7+
8+
from eth_typing import (
9+
ABIEvent,
10+
)
11+
from eth_utils.toolz import (
12+
compose,
13+
curry,
14+
)
15+
16+
from web3 import (
17+
Web3,
18+
)
19+
from web3.contract.async_contract import (
20+
AsyncContractEvent,
21+
)
22+
from web3.contract.contract import (
23+
Contract,
24+
ContractEvent,
25+
)
26+
27+
map_repr = compose(list, curry(map, repr))
28+
29+
30+
@pytest.mark.parametrize(
31+
"method,args,repr_func,expected",
32+
(
33+
(
34+
"all_events",
35+
(),
36+
map_repr,
37+
[
38+
"<Event LogSingleArg(uint256)>",
39+
"<Event LogSingleWithIndex(uint256)>",
40+
],
41+
),
42+
(
43+
"get_event_by_signature",
44+
("LogSingleArg(uint256)",),
45+
repr,
46+
"<Event LogSingleArg(uint256)>",
47+
),
48+
(
49+
"find_events_by_name",
50+
("LogSingleArg",),
51+
map_repr,
52+
[
53+
"<Event LogSingleArg(uint256)>",
54+
],
55+
),
56+
(
57+
"get_event_by_name",
58+
("LogSingleArg",),
59+
repr,
60+
"<Event LogSingleArg(uint256)>",
61+
),
62+
(
63+
"find_events_by_selector",
64+
(
65+
b"\xf7\x0f\xe6\x89\xe2\x90\xd8\xce+*8\x8a\xc2\x8d\xb3o\xbb\x0e\x16\xa6\xd8\x9ch\x04\xc4a\xf6Z\x1b@\xbb\x15", # noqa: E501
66+
),
67+
map_repr,
68+
[
69+
"<Event LogSingleWithIndex(uint256)>",
70+
],
71+
),
72+
(
73+
"get_event_by_selector",
74+
(
75+
b"\xf7\x0f\xe6\x89\xe2\x90\xd8\xce+*8\x8a\xc2\x8d\xb3o\xbb\x0e\x16\xa6\xd8\x9ch\x04\xc4a\xf6Z\x1b@\xbb\x15", # noqa: E501
76+
),
77+
repr,
78+
"<Event LogSingleWithIndex(uint256)>",
79+
),
80+
(
81+
"get_event_by_selector",
82+
(0xF70FE689E290D8CE2B2A388AC28DB36FBB0E16A6D89C6804C461F65A1B40BB15,),
83+
repr,
84+
"<Event LogSingleWithIndex(uint256)>",
85+
),
86+
(
87+
"get_event_by_selector",
88+
("0xf70fe689e290d8ce2b2a388ac28db36fbb0e16a6d89c6804c461f65a1b40bb15",),
89+
repr,
90+
"<Event LogSingleWithIndex(uint256)>",
91+
),
92+
(
93+
"get_event_by_topic",
94+
("0x56d2ef3c5228bf5d88573621e325a4672ab50e033749a601e4f4a5e1dce905d4",),
95+
repr,
96+
"<Event LogSingleArg(uint256)>",
97+
),
98+
),
99+
)
100+
def test_find_or_get_events_by_type(
101+
w3: "Web3",
102+
method: str,
103+
args: Sequence[str],
104+
repr_func: Callable[[Any], str],
105+
expected: str,
106+
event_contract: "Contract",
107+
) -> None:
108+
contract = w3.eth.contract(abi=event_contract.abi)
109+
contract_event = getattr(contract, method)(*args)
110+
assert repr_func(contract_event) == expected
111+
112+
113+
def test_find_events_by_identifier(w3: "Web3", event_contract: "Contract") -> None:
114+
def callable_check(event_abi: ABIEvent) -> bool:
115+
return event_abi["name"] == "LogSingleArg"
116+
117+
contract_events = event_contract.find_events_by_identifier(
118+
event_contract.abi, w3, event_contract.address, callable_check
119+
)
120+
assert [repr(evt) for evt in contract_events] == ["<Event LogSingleArg(uint256)>"]
121+
122+
123+
def test_get_event_by_identifier(w3: "Web3", event_contract: "Contract") -> None:
124+
contract_event = event_contract.get_event_by_identifier(
125+
[event_contract.events.LogSingleArg], "LogSingleArg"
126+
)
127+
assert repr(contract_event) == "<Event LogSingleArg(uint256)>"
128+
129+
130+
def test_events_iterator(math_contract):
131+
all_events = math_contract.all_events()
132+
events_iter = math_contract.events
133+
134+
for event, expected_event in zip(iter(events_iter), all_events):
135+
assert isinstance(event, ContractEvent)
136+
assert event.name == expected_event.name
137+
138+
139+
def test_async_events_iterator(async_math_contract):
140+
all_events = async_math_contract.all_events()
141+
events_iter = async_math_contract.events
142+
143+
for event, expected_event in zip(iter(events_iter), all_events):
144+
assert isinstance(event, AsyncContractEvent)
145+
assert event.name == expected_event.name

tests/core/contracts/test_extracting_event_data.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def test_event_data_extraction(
128128
assert len(txn_receipt["logs"]) == 1
129129
log_entry = txn_receipt["logs"][0]
130130

131-
event_abi = emitter._get_event_abi(event_name)
131+
event_abi = emitter.get_event_by_name(event_name).abi
132132

133133
event_topic = getattr(emitter_contract_log_topics, event_name)
134134
is_anonymous = event_abi["anonymous"]
@@ -335,7 +335,7 @@ def test_event_data_extraction_bytes(
335335
log_entry = txn_receipt["logs"][0]
336336

337337
event_name = "LogListArgs"
338-
event_abi = emitter._get_event_abi(event_name)
338+
event_abi = emitter.get_event_by_name(event_name).abi
339339

340340
event_topic = getattr(emitter_contract_log_topics, event_name)
341341

@@ -386,7 +386,7 @@ def test_event_data_extraction_bytes_non_strict(
386386
log_entry = txn_receipt["logs"][0]
387387

388388
event_name = "LogListArgs"
389-
event_abi = non_strict_emitter._get_event_abi(event_name)
389+
event_abi = non_strict_emitter.get_event_by_name(event_name).abi
390390

391391
event_topic = getattr(emitter_contract_log_topics, event_name)
392392

@@ -431,7 +431,7 @@ def test_dynamic_length_argument_extraction(
431431
assert len(txn_receipt["logs"]) == 1
432432
log_entry = txn_receipt["logs"][0]
433433

434-
event_abi = emitter._get_event_abi("LogDynamicArgs")
434+
event_abi = emitter.get_event_by_name("LogDynamicArgs").abi
435435

436436
event_topic = emitter_contract_log_topics.LogDynamicArgs
437437
assert event_topic in log_entry["topics"]
@@ -466,7 +466,7 @@ def test_argument_extraction_strict_bytes_types(
466466
log_entry = txn_receipt["logs"][0]
467467
assert len(log_entry["topics"]) == 2
468468

469-
event_abi = emitter._get_event_abi("LogListArgs")
469+
event_abi = emitter.get_event_by_name("LogListArgs").abi
470470

471471
event_topic = emitter_contract_log_topics.LogListArgs
472472
assert event_topic in log_entry["topics"]
@@ -545,14 +545,14 @@ def test_contract_event_get_logs_sorted_by_log_index(w3, emitter, request_mocker
545545
]
546546

547547
with request_mocker(w3, mock_results={"eth_getLogs": get_logs_response}):
548-
logs = emitter.events.LogNoArguments().get_logs()
548+
logs = emitter.events.LogNoArguments.get_logs()
549549

550550
sorted_logs = sorted(
551-
emitter.events.LogNoArguments().get_logs(),
551+
emitter.events.LogNoArguments.get_logs(),
552552
key=lambda log: log["logIndex"],
553553
)
554554
sorted_logs = sorted(
555-
emitter.events.LogNoArguments().get_logs(),
555+
emitter.events.LogNoArguments.get_logs(),
556556
key=lambda log: log["blockNumber"],
557557
)
558558

@@ -796,7 +796,7 @@ def test_event_rich_log(
796796
txn_hash = emitter_fn(*call_args).transact()
797797
txn_receipt = wait_for_transaction(w3, txn_hash)
798798

799-
event_instance = emitter.events[event_name]()
799+
event_instance = emitter.events[event_name]
800800

801801
if process_receipt:
802802
processed_logs = event_instance.process_receipt(txn_receipt)
@@ -819,7 +819,7 @@ def test_event_rich_log(
819819

820820
quiet_event = emitter.events["LogBytes"]
821821
with pytest.warns(UserWarning, match=warning_msg):
822-
empty_rich_log = quiet_event().process_receipt(txn_receipt)
822+
empty_rich_log = quiet_event.process_receipt(txn_receipt)
823823
assert empty_rich_log == tuple()
824824

825825

@@ -1059,7 +1059,7 @@ def test_event_rich_log_non_strict(
10591059
txn_hash = emitter_fn(*call_args).transact()
10601060
txn_receipt = wait_for_transaction(w3_non_strict_abi, txn_hash)
10611061

1062-
event_instance = non_strict_emitter.events[event_name]()
1062+
event_instance = non_strict_emitter.events[event_name]
10631063

10641064
if process_receipt:
10651065
processed_logs = event_instance.process_receipt(txn_receipt)
@@ -1082,7 +1082,7 @@ def test_event_rich_log_non_strict(
10821082

10831083
quiet_event = non_strict_emitter.events["LogBytes"]
10841084
with pytest.warns(UserWarning, match=warning_msg):
1085-
empty_rich_log = quiet_event().process_receipt(txn_receipt)
1085+
empty_rich_log = quiet_event.process_receipt(txn_receipt)
10861086
assert empty_rich_log == tuple()
10871087

10881088

@@ -1093,7 +1093,7 @@ def test_event_rich_log_with_byte_args(
10931093
txn_hash = emitter.functions.logListArgs([b"13"], [b"54"]).transact()
10941094
txn_receipt = wait_for_transaction(w3, txn_hash)
10951095

1096-
event_instance = emitter.events.LogListArgs()
1096+
event_instance = emitter.events.LogListArgs
10971097

10981098
if process_receipt:
10991099
processed_logs = event_instance.process_receipt(txn_receipt)
@@ -1122,7 +1122,7 @@ def test_event_rich_log_with_byte_args(
11221122
def test_receipt_processing_with_discard_flag(
11231123
event_contract, indexed_event_contract, dup_txn_receipt, wait_for_transaction
11241124
):
1125-
event_instance = indexed_event_contract.events.LogSingleWithIndex()
1125+
event_instance = indexed_event_contract.events.LogSingleWithIndex
11261126

11271127
returned_logs = event_instance.process_receipt(dup_txn_receipt, errors=DISCARD)
11281128
assert returned_logs == ()
@@ -1131,7 +1131,7 @@ def test_receipt_processing_with_discard_flag(
11311131
def test_receipt_processing_with_ignore_flag(
11321132
event_contract, indexed_event_contract, dup_txn_receipt, wait_for_transaction
11331133
):
1134-
event_instance = indexed_event_contract.events.LogSingleWithIndex()
1134+
event_instance = indexed_event_contract.events.LogSingleWithIndex
11351135

11361136
returned_logs = event_instance.process_receipt(dup_txn_receipt, errors=IGNORE)
11371137
assert len(returned_logs) == 2
@@ -1155,37 +1155,37 @@ def test_receipt_processing_with_ignore_flag(
11551155

11561156

11571157
def test_receipt_processing_with_warn_flag(indexed_event_contract, dup_txn_receipt):
1158-
event_instance = indexed_event_contract.events.LogSingleWithIndex()
1158+
event_instance = indexed_event_contract.events.LogSingleWithIndex
11591159

11601160
with pytest.warns(UserWarning, match="Expected 1 log topics. Got 0"):
11611161
returned_logs = event_instance.process_receipt(dup_txn_receipt, errors=WARN)
11621162
assert len(returned_logs) == 0
11631163

11641164

11651165
def test_receipt_processing_with_strict_flag(indexed_event_contract, dup_txn_receipt):
1166-
event_instance = indexed_event_contract.events.LogSingleWithIndex()
1166+
event_instance = indexed_event_contract.events.LogSingleWithIndex
11671167

11681168
with pytest.raises(LogTopicError, match="Expected 1 log topics. Got 0"):
11691169
event_instance.process_receipt(dup_txn_receipt, errors=STRICT)
11701170

11711171

11721172
def test_receipt_processing_with_invalid_flag(indexed_event_contract, dup_txn_receipt):
1173-
event_instance = indexed_event_contract.events.LogSingleWithIndex()
1173+
event_instance = indexed_event_contract.events.LogSingleWithIndex
11741174

11751175
with pytest.raises(Web3AttributeError, match="Error flag must be one of: "):
11761176
event_instance.process_receipt(dup_txn_receipt, errors="not-a-flag")
11771177

11781178

11791179
def test_receipt_processing_with_no_flag(indexed_event_contract, dup_txn_receipt):
1180-
event_instance = indexed_event_contract.events.LogSingleWithIndex()
1180+
event_instance = indexed_event_contract.events.LogSingleWithIndex
11811181

11821182
with pytest.warns(UserWarning, match="Expected 1 log topics. Got 0"):
11831183
returned_log = event_instance.process_receipt(dup_txn_receipt)
11841184
assert len(returned_log) == 0
11851185

11861186

11871187
def test_single_log_processing_with_errors(indexed_event_contract, dup_txn_receipt):
1188-
event_instance = indexed_event_contract.events.LogSingleWithIndex()
1188+
event_instance = indexed_event_contract.events.LogSingleWithIndex
11891189

11901190
with pytest.raises(LogTopicError, match="Expected 1 log topics. Got 0"):
11911191
event_instance.process_log(dup_txn_receipt["logs"][0])
@@ -1244,7 +1244,7 @@ def test_receipt_processing_catches_insufficientdatabytes_error_by_default(
12441244
):
12451245
txn_hash = emitter.functions.logListArgs([b"13"], [b"54"]).transact()
12461246
txn_receipt = wait_for_transaction(w3, txn_hash)
1247-
event_instance = emitter.events.LogListArgs()
1247+
event_instance = emitter.events.LogListArgs
12481248

12491249
# web3 doesn't generate logs with non-standard lengths, so we have to do it manually
12501250
txn_receipt_dict = copy.deepcopy(txn_receipt)

tests/core/contracts/test_extracting_event_data_old.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def test_event_data_extraction(
9797
assert len(txn_receipt["logs"]) == 1
9898
log_entry = txn_receipt["logs"][0]
9999

100-
event_abi = emitter._get_event_abi(event_name)
100+
event_abi = emitter.get_event_by_name(event_name).abi
101101

102102
event_topic = getattr(emitter_contract_log_topics, event_name)
103103
is_anonymous = event_abi["anonymous"]
@@ -132,7 +132,7 @@ def test_dynamic_length_argument_extraction(
132132
assert len(txn_receipt["logs"]) == 1
133133
log_entry = txn_receipt["logs"][0]
134134

135-
event_abi = emitter._get_event_abi("LogDynamicArgs")
135+
event_abi = emitter.get_event_by_name("LogDynamicArgs").abi
136136

137137
event_topic = emitter_contract_log_topics.LogDynamicArgs
138138
assert event_topic in log_entry["topics"]

tests/core/filtering/test_filters_against_many_blocks.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ def test_event_filter_new_events(
5050
builder.from_block = "latest"
5151
event_filter = builder.deploy(w3)
5252
else:
53-
event_filter = emitter.events.LogNoArguments().create_filter(
54-
from_block="latest"
55-
)
53+
event_filter = emitter.events.LogNoArguments.create_filter(from_block="latest")
5654

5755
expected_match_counter = 0
5856

@@ -109,9 +107,7 @@ def gen_non_matching_transact():
109107
builder.from_block = "latest"
110108
event_filter = builder.deploy(w3)
111109
else:
112-
event_filter = emitter.events.LogNoArguments().create_filter(
113-
from_block="latest"
114-
)
110+
event_filter = emitter.events.LogNoArguments.create_filter(from_block="latest")
115111

116112
expected_match_counter = 0
117113

@@ -179,7 +175,7 @@ async def test_async_event_filter_new_events(
179175
builder.from_block = "latest"
180176
event_filter = await builder.deploy(async_w3)
181177
else:
182-
event_filter = await async_emitter.events.LogNoArguments().create_filter(
178+
event_filter = await async_emitter.events.LogNoArguments.create_filter(
183179
from_block="latest"
184180
)
185181

@@ -247,7 +243,7 @@ async def gen_non_matching_transact():
247243
builder.from_block = "latest"
248244
event_filter = await builder.deploy(async_w3)
249245
else:
250-
event_filter = await async_emitter.events.LogNoArguments().create_filter(
246+
event_filter = await async_emitter.events.LogNoArguments.create_filter(
251247
from_block="latest"
252248
)
253249

0 commit comments

Comments
 (0)