Skip to content

Commit 85fecdf

Browse files
author
Stuart Reed
committed
Additional test coverage for invalid addresses and return string values for single item lists
1 parent 60abf98 commit 85fecdf

File tree

2 files changed

+184
-64
lines changed

2 files changed

+184
-64
lines changed

tests/core/utilities/test_construct_event_filter_params.py

Lines changed: 158 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,34 @@
33
from web3._utils.filters import (
44
construct_event_filter_params,
55
)
6+
from web3.exceptions import (
7+
InvalidAddress,
8+
Web3ValueError,
9+
)
10+
11+
12+
def hex_and_pad(i):
13+
unpadded_hex_value = hex(i).rstrip("L")
14+
return "0x" + unpadded_hex_value[2:].zfill(64)
615

7-
EVENT_1_ABI = {
8-
"anonymous": False,
9-
"inputs": [
10-
{"indexed": False, "name": "arg0", "type": "uint256"},
11-
{"indexed": True, "name": "arg1", "type": "uint256"},
12-
],
13-
"name": "Event_1",
14-
"type": "event",
15-
}
16+
17+
@pytest.fixture
18+
def event_abi():
19+
return {
20+
"anonymous": False,
21+
"inputs": [
22+
{"indexed": False, "name": "arg0", "type": "uint256"},
23+
{"indexed": True, "name": "arg1", "type": "uint256"},
24+
],
25+
"name": "Event_1",
26+
"type": "event",
27+
}
1628

1729

1830
@pytest.mark.parametrize(
19-
"event_abi,fn_kwargs,expected",
31+
"fn_kwargs,expected",
2032
(
2133
pytest.param(
22-
EVENT_1_ABI,
2334
{},
2435
{
2536
"topics": [
@@ -29,13 +40,20 @@
2940
id="no-args",
3041
),
3142
pytest.param(
32-
EVENT_1_ABI,
3343
{"topics": ["should-overwrite-topics"]},
3444
{"topics": ["should-overwrite-topics"]},
3545
id="overwrite-topics",
3646
),
3747
pytest.param(
38-
EVENT_1_ABI,
48+
{"address": None},
49+
{
50+
"topics": [
51+
"0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6"
52+
],
53+
},
54+
id="no-address",
55+
),
56+
pytest.param(
3957
{"contract_address": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601"},
4058
{
4159
"topics": [
@@ -46,7 +64,6 @@
4664
id="contract_address-string",
4765
),
4866
pytest.param(
49-
EVENT_1_ABI,
5067
{
5168
"contract_address": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
5269
"address": "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413",
@@ -63,7 +80,22 @@
6380
id="address-with-contract_address",
6481
),
6582
pytest.param(
66-
EVENT_1_ABI,
83+
{
84+
"contract_address": ["0xd3CdA913deB6f67967B99D67aCDFa1712C293601"],
85+
"address": ["0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"],
86+
},
87+
{
88+
"topics": [
89+
"0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6"
90+
],
91+
"address": [
92+
"0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413",
93+
"0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
94+
],
95+
},
96+
id="single-item-address-list-with-contract_address-list",
97+
),
98+
pytest.param(
6799
{"address": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601"},
68100
{
69101
"topics": [
@@ -74,18 +106,16 @@
74106
id="address-string",
75107
),
76108
pytest.param(
77-
EVENT_1_ABI,
78109
{"address": ["0xd3CdA913deB6f67967B99D67aCDFa1712C293601"]},
79110
{
80111
"topics": [
81112
"0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6"
82113
],
83-
"address": ["0xd3CdA913deB6f67967B99D67aCDFa1712C293601"],
114+
"address": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
84115
},
85116
id="address-list",
86117
),
87118
pytest.param(
88-
EVENT_1_ABI,
89119
{
90120
"address": [
91121
"0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
@@ -104,7 +134,6 @@
104134
id="multiple-address",
105135
),
106136
pytest.param(
107-
EVENT_1_ABI,
108137
{
109138
"address": "0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
110139
"contract_address": ["0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"],
@@ -121,7 +150,6 @@
121150
id="one-address-with-multiple-contract_address",
122151
),
123152
pytest.param(
124-
EVENT_1_ABI,
125153
{
126154
"address": [
127155
"0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
@@ -141,26 +169,125 @@
141169
},
142170
id="multiple-address-with-one-contract_address",
143171
),
172+
pytest.param(
173+
{
174+
"address": [
175+
"0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
176+
"0x1234567890123456789012345678901234567890",
177+
],
178+
"contract_address": [
179+
"0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413",
180+
"0x1234567890123456789012345678901234567890",
181+
],
182+
},
183+
{
184+
"topics": [
185+
"0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6"
186+
],
187+
"address": [
188+
"0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
189+
"0x1234567890123456789012345678901234567890",
190+
"0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413",
191+
],
192+
},
193+
id="multiple-address-with-multiple-contract_address",
194+
),
195+
pytest.param(
196+
{
197+
"contract_address": "0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413",
198+
"topics": [
199+
"0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6"
200+
],
201+
"from_block": "latest",
202+
"to_block": "latest",
203+
"address": [
204+
"0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
205+
"0x1234567890123456789012345678901234567890",
206+
"0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413",
207+
],
208+
},
209+
{
210+
"topics": [
211+
"0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6"
212+
],
213+
"address": [
214+
"0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
215+
"0x1234567890123456789012345678901234567890",
216+
"0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413",
217+
],
218+
"fromBlock": "latest",
219+
"toBlock": "latest",
220+
},
221+
id="all-arguments-with-address-list",
222+
),
144223
),
145224
)
146225
def test_construct_event_filter_params(w3, event_abi, fn_kwargs, expected):
147-
_, actual = construct_event_filter_params(event_abi, w3.codec, **fn_kwargs)
148-
assert actual == expected
226+
_, filter_params = construct_event_filter_params(event_abi, w3.codec, **fn_kwargs)
227+
# Ensure that the filter_params contains the expected keys
228+
assert (
229+
filter_params.keys() == expected.keys()
230+
), f"Keys don't match. Expected {set(expected.keys())}, got {set(filter_params.keys())}" # noqa: E501
231+
# Verify all values in filter_params match the expected values
232+
for key, value in expected.items():
233+
if isinstance(value, list) and isinstance(filter_params[key], list):
234+
assert sorted(filter_params[key]) == sorted(
235+
value
236+
), f"Expected {key}={value}, got {key}={filter_params.get(key)}"
237+
else:
238+
assert (
239+
filter_params[key] == value
240+
), f"Expected {key}={value}, got {key}={filter_params.get(key)}"
149241

150242

151-
def hex_and_pad(i):
152-
unpadded_hex_value = hex(i).rstrip("L")
153-
return "0x" + unpadded_hex_value[2:].zfill(64)
243+
@pytest.mark.parametrize(
244+
"fn_kwargs, expected_exception",
245+
[
246+
pytest.param(
247+
{
248+
"contract_address": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
249+
"topics": [
250+
"0xb470a829ed7792f06947f0ca3730a570cb378329ddcf09f2b4efabd6326f51f6"
251+
],
252+
"address": [
253+
"0xd3cda913deb6f67967b99d67acdfa1712c293601",
254+
"0x1234567890123456789012345678901234567890",
255+
"0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413",
256+
],
257+
"from_block": 1,
258+
"to_block": 2,
259+
},
260+
InvalidAddress,
261+
id="invalid-checksum-address",
262+
),
263+
pytest.param(
264+
{
265+
"contract_address": "0xbb9bc244d798123fde783fcc1c72d3bb8c189413",
266+
},
267+
InvalidAddress,
268+
id="invalid-checksum-contract-address",
269+
),
270+
pytest.param(
271+
{"address": {"invalid": "0x1234567890123456789012345678901234567890"}},
272+
Web3ValueError,
273+
id="unsupported-type-exception",
274+
),
275+
],
276+
)
277+
def test_construct_event_filter_params_exceptions(
278+
w3, event_abi, fn_kwargs, expected_exception
279+
):
280+
with pytest.raises(expected_exception):
281+
construct_event_filter_params(event_abi, w3.codec, **fn_kwargs)
154282

155283

156284
@pytest.mark.parametrize(
157-
"event_abi,fn_kwargs,expected",
285+
"fn_kwargs,expected",
158286
(
159-
(EVENT_1_ABI, {}, [[]]),
160-
(EVENT_1_ABI, {"argument_filters": {"arg0": 1}}, [[hex_and_pad(1)]]),
161-
(EVENT_1_ABI, {"argument_filters": {"arg0": [1]}}, [[hex_and_pad(1)]]),
287+
({}, [[]]),
288+
({"argument_filters": {"arg0": 1}}, [[hex_and_pad(1)]]),
289+
({"argument_filters": {"arg0": [1]}}, [[hex_and_pad(1)]]),
162290
(
163-
EVENT_1_ABI,
164291
{"argument_filters": {"arg0": [1, 2]}},
165292
[
166293
[hex_and_pad(1)],
@@ -170,7 +297,7 @@ def hex_and_pad(i):
170297
),
171298
)
172299
def test_construct_event_filter_params_for_data_filters(
173-
event_abi, w3, fn_kwargs, expected
300+
w3, event_abi, fn_kwargs, expected
174301
):
175302
actual, _ = construct_event_filter_params(event_abi, w3.codec, **fn_kwargs)
176303
assert actual == expected

web3/_utils/filters.py

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
List,
99
Optional,
1010
Sequence,
11+
Set,
1112
Tuple,
1213
Union,
1314
)
@@ -26,7 +27,6 @@
2627
)
2728
from eth_utils import (
2829
is_hex,
29-
is_list_like,
3030
is_string,
3131
is_text,
3232
)
@@ -67,33 +67,37 @@
6767
from web3.eth import Eth # noqa: F401
6868

6969

70-
def _validate_addresses_as_list(
71-
address: Union[ChecksumAddress, List[ChecksumAddress]],
72-
) -> List[ChecksumAddress]:
70+
def _sanitize_addresses(
71+
*address: Union[ChecksumAddress, List[ChecksumAddress]],
72+
) -> Union[ChecksumAddress, List[ChecksumAddress]]:
7373
"""
74-
Validates the address or list of addresses and returns a list of
75-
ChecksumAddress.
74+
Validates an address or list of addresses and returns a single
75+
ChecksumAddress or a list of ChecksumAddresses.
7676
Raises Web3ValueError if the address is not valid.
7777
7878
:param address: A single address or a list of addresses.
7979
:return: A list of ChecksumAddress.
8080
"""
81-
address_list: List[ChecksumAddress] = []
82-
if address:
83-
if isinstance(address, List):
84-
address_list = address
81+
address_set: Set[ChecksumAddress] = set()
82+
for arg in address:
83+
if not arg:
84+
continue
85+
elif isinstance(arg, str):
86+
address_set.add(arg)
87+
elif isinstance(arg, list):
88+
address_set.update(arg)
8589
else:
86-
address_list = [address]
90+
raise Web3ValueError(
91+
f"Unsupported type for `address` parameter: {type(address)}"
92+
)
8793

88-
for addr in address_list:
89-
try:
90-
validate_address(addr)
91-
except Web3ValidationError:
92-
raise Web3ValueError(
93-
f"Unsupported type for `address` parameter: {type(address)}"
94-
)
94+
if not address_set:
95+
return []
96+
else:
97+
for addr in address_set:
98+
validate_address(addr)
9599

96-
return address_list
100+
return list(address_set) if len(address_set) > 1 else address_set.pop()
97101

98102

99103
def construct_event_filter_params(
@@ -121,20 +125,9 @@ def construct_event_filter_params(
121125

122126
filter_params["topics"] = topic_set
123127

124-
if address and contract_address:
125-
filter_params["address"] = _validate_addresses_as_list(
126-
address
127-
) + _validate_addresses_as_list(contract_address)
128-
elif address and not contract_address:
129-
if is_list_like(address):
130-
filter_params["address"] = _validate_addresses_as_list(address)
131-
else:
132-
filter_params["address"] = address
133-
elif contract_address:
134-
if is_list_like(contract_address):
135-
filter_params["address"] = _validate_addresses_as_list(contract_address)
136-
else:
137-
filter_params["address"] = contract_address
128+
sanitized_addresses = _sanitize_addresses(address, contract_address)
129+
if sanitized_addresses:
130+
filter_params["address"] = sanitized_addresses
138131

139132
if from_block is not None:
140133
filter_params["fromBlock"] = from_block

0 commit comments

Comments
 (0)