web3 7.4.0__py3-none-any.whl → 7.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. web3/_utils/abi.py +59 -1
  2. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  3. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  4. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  5. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  6. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  7. web3/_utils/contract_sources/contract_data/event_contracts.py +50 -5
  8. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  9. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  10. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  11. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  12. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  13. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  14. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  15. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  16. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  17. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  18. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  19. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  20. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  21. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  22. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  23. web3/_utils/contracts.py +48 -7
  24. web3/_utils/method_formatters.py +109 -1
  25. web3/_utils/module_testing/__init__.py +4 -0
  26. web3/_utils/module_testing/eth_module.py +11 -0
  27. web3/_utils/module_testing/go_ethereum_debug_module.py +128 -0
  28. web3/_utils/rpc_abi.py +4 -0
  29. web3/_utils/validation.py +3 -0
  30. web3/contract/async_contract.py +191 -41
  31. web3/contract/base_contract.py +373 -152
  32. web3/contract/contract.py +182 -34
  33. web3/contract/utils.py +51 -2
  34. web3/eth/async_eth.py +11 -0
  35. web3/eth/eth.py +11 -0
  36. web3/geth.py +59 -0
  37. web3/main.py +4 -0
  38. web3/manager.py +10 -0
  39. web3/providers/eth_tester/defaults.py +1 -0
  40. web3/types.py +87 -2
  41. web3/utils/abi.py +308 -76
  42. {web3-7.4.0.dist-info → web3-7.6.0.dist-info}/METADATA +3 -2
  43. {web3-7.4.0.dist-info → web3-7.6.0.dist-info}/RECORD +46 -45
  44. {web3-7.4.0.dist-info → web3-7.6.0.dist-info}/WHEEL +1 -1
  45. {web3-7.4.0.dist-info → web3-7.6.0.dist-info}/LICENSE +0 -0
  46. {web3-7.4.0.dist-info → web3-7.6.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,128 @@
1
+ import pytest
2
+ from typing import (
3
+ cast,
4
+ )
5
+
6
+ from eth_typing import (
7
+ HexStr,
8
+ )
9
+
10
+ from web3 import (
11
+ AsyncWeb3,
12
+ Web3,
13
+ )
14
+ from web3.types import (
15
+ BlockData,
16
+ CallTrace,
17
+ ChecksumAddress,
18
+ DiffModeTrace,
19
+ OpcodeTrace,
20
+ PrestateTrace,
21
+ )
22
+
23
+
24
+ class GoEthereumAsyncDebugModuleTest:
25
+ @pytest.mark.asyncio
26
+ async def test_async_geth_debug_trace_transaction_opcode_logger(
27
+ self, async_w3: "AsyncWeb3", txn_hash_with_log: HexStr
28
+ ) -> None:
29
+ result = await async_w3.geth.debug.trace_transaction(txn_hash_with_log)
30
+ assert "structLogs" in dict(result).keys()
31
+ assert "gas" in dict(result).keys()
32
+ assert "failed" in dict(result).keys()
33
+
34
+ @pytest.mark.asyncio
35
+ async def test_async_geth_debug_trace_transaction_call_tracer(
36
+ self, async_w3: "AsyncWeb3", txn_hash_with_log: HexStr
37
+ ) -> None:
38
+ result = cast(
39
+ CallTrace,
40
+ await async_w3.geth.debug.trace_transaction(
41
+ txn_hash_with_log, {"tracer": "callTracer"}
42
+ ),
43
+ )
44
+ assert result.get("type") == "CALL"
45
+
46
+ @pytest.mark.asyncio
47
+ async def test_async_geth_debug_trace_transaction_prestate_tracer_diffMode(
48
+ self, async_w3: "AsyncWeb3", txn_hash_with_log: HexStr
49
+ ) -> None:
50
+ result = cast(
51
+ DiffModeTrace,
52
+ await async_w3.geth.debug.trace_transaction(
53
+ txn_hash_with_log,
54
+ {"tracer": "prestateTracer", "tracerConfig": {"diffMode": True}},
55
+ ),
56
+ )
57
+ assert "post" in dict(result).keys()
58
+ assert "pre" in dict(result).keys()
59
+
60
+ @pytest.mark.asyncio
61
+ async def test_async_geth_debug_trace_transaction_prestate_tracer(
62
+ self,
63
+ async_w3: "AsyncWeb3",
64
+ txn_hash_with_log: HexStr,
65
+ async_block_with_txn_with_log: BlockData,
66
+ ) -> None:
67
+ result = cast(
68
+ PrestateTrace,
69
+ await async_w3.geth.debug.trace_transaction(
70
+ txn_hash_with_log,
71
+ {"tracer": "prestateTracer"},
72
+ ),
73
+ )
74
+ tx = await async_w3.eth.get_transaction(txn_hash_with_log)
75
+ from_addr: ChecksumAddress = tx["from"]
76
+ assert isinstance(result[from_addr].get("balance"), int)
77
+ assert "post" not in dict(result).keys()
78
+ assert "pre" not in dict(result).keys()
79
+
80
+
81
+ class GoEthereumDebugModuleTest:
82
+ def test_geth_debug_trace_transaction_opcode_logger(
83
+ self, w3: "Web3", txn_hash_with_log: HexStr
84
+ ) -> None:
85
+ result = cast(OpcodeTrace, w3.geth.debug.trace_transaction(txn_hash_with_log))
86
+ assert "structLogs" in dict(result).keys()
87
+ assert "gas" in dict(result).keys()
88
+ assert "failed" in dict(result).keys()
89
+
90
+ def test_geth_debug_trace_transaction_call_tracer(
91
+ self, w3: "Web3", txn_hash_with_log: HexStr
92
+ ) -> None:
93
+ result = cast(
94
+ CallTrace,
95
+ w3.geth.debug.trace_transaction(
96
+ txn_hash_with_log, {"tracer": "callTracer"}
97
+ ),
98
+ )
99
+ assert result.get("type") == "CALL"
100
+
101
+ def test_geth_debug_trace_transaction_prestate_tracer_diffmode(
102
+ self, w3: "Web3", txn_hash_with_log: HexStr
103
+ ) -> None:
104
+ result = cast(
105
+ DiffModeTrace,
106
+ w3.geth.debug.trace_transaction(
107
+ txn_hash_with_log,
108
+ {"tracer": "prestateTracer", "tracerConfig": {"diffMode": True}},
109
+ ),
110
+ )
111
+ assert "post" in dict(result).keys()
112
+ assert "pre" in dict(result).keys()
113
+
114
+ def test_geth_debug_trace_transaction_prestate_tracer(
115
+ self, w3: "Web3", txn_hash_with_log: HexStr
116
+ ) -> None:
117
+ result = cast(
118
+ PrestateTrace,
119
+ w3.geth.debug.trace_transaction(
120
+ txn_hash_with_log,
121
+ {"tracer": "prestateTracer"},
122
+ ),
123
+ )
124
+ tx = w3.eth.get_transaction(txn_hash_with_log)
125
+ from_addr: ChecksumAddress = tx["from"]
126
+ assert isinstance(result[from_addr].get("balance"), int)
127
+ assert "post" not in dict(result).keys()
128
+ assert "pre" not in dict(result).keys()
web3/_utils/rpc_abi.py CHANGED
@@ -48,6 +48,7 @@ class RPC:
48
48
 
49
49
  # eth
50
50
  eth_accounts = RPCEndpoint("eth_accounts")
51
+ eth_blobBaseFee = RPCEndpoint("eth_blobBaseFee")
51
52
  eth_blockNumber = RPCEndpoint("eth_blockNumber")
52
53
  eth_call = RPCEndpoint("eth_call")
53
54
  eth_createAccessList = RPCEndpoint("eth_createAccessList")
@@ -140,6 +141,9 @@ class RPC:
140
141
  # web3
141
142
  web3_clientVersion = RPCEndpoint("web3_clientVersion")
142
143
 
144
+ # debug
145
+ debug_traceTransaction = RPCEndpoint("debug_traceTransaction")
146
+
143
147
 
144
148
  TRANSACTION_PARAMS_ABIS = {
145
149
  "data": "bytes",
web3/_utils/validation.py CHANGED
@@ -79,6 +79,9 @@ def validate_abi(abi: ABI) -> None:
79
79
  if not all(is_dict(e) for e in abi):
80
80
  raise Web3ValueError("'abi' is not a list of dictionaries")
81
81
 
82
+ if not all("type" in e for e in abi):
83
+ raise Web3ValueError("'abi' must contain a list of elements each with a type")
84
+
82
85
  functions = filter_abi_by_type("function", abi)
83
86
  selectors = groupby(compose(encode_hex, function_abi_to_4byte_selector), functions)
84
87
  duplicates = valfilter(lambda funcs: len(funcs) > 1, selectors)
@@ -1,4 +1,3 @@
1
- import copy
2
1
  from typing import (
3
2
  TYPE_CHECKING,
4
3
  Any,
@@ -21,7 +20,9 @@ from eth_utils import (
21
20
  combomethod,
22
21
  )
23
22
  from eth_utils.abi import (
23
+ abi_to_signature,
24
24
  get_abi_input_names,
25
+ get_abi_input_types,
25
26
  get_all_function_abis,
26
27
  )
27
28
  from eth_utils.toolz import (
@@ -33,6 +34,9 @@ from hexbytes import (
33
34
 
34
35
  from web3._utils.abi import (
35
36
  fallback_func_abi_exists,
37
+ filter_by_types,
38
+ get_abi_element_signature,
39
+ get_name_from_abi_element_identifier,
36
40
  receive_func_abi_exists,
37
41
  )
38
42
  from web3._utils.abi_element_identifiers import (
@@ -47,6 +51,8 @@ from web3._utils.compat import (
47
51
  )
48
52
  from web3._utils.contracts import (
49
53
  async_parse_block_identifier,
54
+ copy_contract_event,
55
+ copy_contract_function,
50
56
  )
51
57
  from web3._utils.datatypes import (
52
58
  PropertyCheckingFactory,
@@ -79,11 +85,15 @@ from web3.contract.utils import (
79
85
  async_call_contract_function,
80
86
  async_estimate_gas_for_function,
81
87
  async_transact_with_contract_function,
88
+ find_events_by_identifier,
82
89
  find_functions_by_identifier,
90
+ get_event_by_identifier,
83
91
  get_function_by_identifier,
84
92
  )
85
93
  from web3.exceptions import (
94
+ ABIEventNotFound,
86
95
  ABIFunctionNotFound,
96
+ NoABIEventsFound,
87
97
  NoABIFound,
88
98
  NoABIFunctionsFound,
89
99
  Web3AttributeError,
@@ -97,6 +107,11 @@ from web3.types import (
97
107
  StateOverride,
98
108
  TxParams,
99
109
  )
110
+ from web3.utils.abi import (
111
+ _get_any_abi_signature_with_name,
112
+ filter_abi_by_type,
113
+ get_abi_element,
114
+ )
100
115
 
101
116
  if TYPE_CHECKING:
102
117
  from ens import AsyncENS # noqa: F401
@@ -107,6 +122,28 @@ class AsyncContractEvent(BaseContractEvent):
107
122
  # mypy types
108
123
  w3: "AsyncWeb3"
109
124
 
125
+ def __call__(self, *args: Any, **kwargs: Any) -> "AsyncContractEvent":
126
+ event_abi = get_abi_element(
127
+ filter_abi_by_type("event", self.contract_abi),
128
+ self.name,
129
+ *args,
130
+ abi_codec=self.w3.codec,
131
+ **kwargs,
132
+ )
133
+ argument_types = get_abi_input_types(event_abi)
134
+ event_signature = str(
135
+ get_abi_element_signature(self.abi_element_identifier, argument_types)
136
+ )
137
+ contract_event = AsyncContractEvent.factory(
138
+ event_signature,
139
+ w3=self.w3,
140
+ contract_abi=self.contract_abi,
141
+ address=self.address,
142
+ abi_element_identifier=event_signature,
143
+ )
144
+
145
+ return copy_contract_event(contract_event, *args, **kwargs)
146
+
110
147
  @combomethod
111
148
  async def get_logs(
112
149
  self,
@@ -135,7 +172,7 @@ class AsyncContractEvent(BaseContractEvent):
135
172
  from = max(mycontract.web3.eth.block_number - 10, 1)
136
173
  to = mycontract.web3.eth.block_number
137
174
 
138
- events = mycontract.events.Transfer.getLogs(from_block=from, to_block=to)
175
+ events = mycontract.events.Transfer.get_logs(from_block=from, to_block=to)
139
176
 
140
177
  for e in events:
141
178
  print(e["args"]["from"],
@@ -171,11 +208,9 @@ class AsyncContractEvent(BaseContractEvent):
171
208
  same time as ``from_block`` or ``to_block``
172
209
  :yield: Tuple of :class:`AttributeDict` instances
173
210
  """
174
- event_abi = self._get_event_abi()
175
-
176
211
  # validate ``argument_filters`` if present
177
212
  if argument_filters is not None:
178
- event_arg_names = get_abi_input_names(event_abi)
213
+ event_arg_names = get_abi_input_names(self.abi)
179
214
  if not all(arg in event_arg_names for arg in argument_filters.keys()):
180
215
  raise Web3ValidationError(
181
216
  "When filtering by argument names, all argument names must be "
@@ -183,21 +218,23 @@ class AsyncContractEvent(BaseContractEvent):
183
218
  )
184
219
 
185
220
  _filter_params = self._get_event_filter_params(
186
- event_abi, argument_filters, from_block, to_block, block_hash
221
+ self.abi, argument_filters, from_block, to_block, block_hash
187
222
  )
188
223
  # call JSON-RPC API
189
224
  logs = await self.w3.eth.get_logs(_filter_params)
190
225
 
191
226
  # convert raw binary data to Python proxy objects as described by ABI:
192
227
  all_event_logs = tuple(
193
- get_event_data(self.w3.codec, event_abi, entry) for entry in logs
228
+ get_event_data(self.w3.codec, self.abi, entry) for entry in logs
194
229
  )
195
230
  filtered_logs = self._process_get_logs_argument_filters(
196
- event_abi,
231
+ self.abi,
197
232
  all_event_logs,
198
233
  argument_filters,
199
234
  )
200
- return cast(Awaitable[Iterable[EventData]], filtered_logs)
235
+ sorted_logs = sorted(filtered_logs, key=lambda e: e["logIndex"])
236
+ sorted_logs = sorted(sorted_logs, key=lambda e: e["blockNumber"])
237
+ return cast(Awaitable[Iterable[EventData]], sorted_logs)
201
238
 
202
239
  @combomethod
203
240
  async def create_filter(
@@ -212,7 +249,7 @@ class AsyncContractEvent(BaseContractEvent):
212
249
  """
213
250
  Create filter object that tracks logs emitted by this contract event.
214
251
  """
215
- filter_builder = AsyncEventFilterBuilder(self._get_event_abi(), self.w3.codec)
252
+ filter_builder = AsyncEventFilterBuilder(self.abi, self.w3.codec)
216
253
  self._set_up_filter_builder(
217
254
  argument_filters,
218
255
  from_block,
@@ -222,9 +259,7 @@ class AsyncContractEvent(BaseContractEvent):
222
259
  filter_builder,
223
260
  )
224
261
  log_filter = await filter_builder.deploy(self.w3)
225
- log_filter.log_entry_formatter = get_event_data(
226
- self.w3.codec, self._get_event_abi()
227
- )
262
+ log_filter.log_entry_formatter = get_event_data(self.w3.codec, self.abi)
228
263
  log_filter.builder = filter_builder
229
264
 
230
265
  return log_filter
@@ -232,13 +267,17 @@ class AsyncContractEvent(BaseContractEvent):
232
267
  @combomethod
233
268
  def build_filter(self) -> AsyncEventFilterBuilder:
234
269
  builder = AsyncEventFilterBuilder(
235
- self._get_event_abi(),
270
+ self.abi,
236
271
  self.w3.codec,
237
- formatter=get_event_data(self.w3.codec, self._get_event_abi()),
272
+ formatter=get_event_data(self.w3.codec, self.abi),
238
273
  )
239
274
  builder.address = self.address
240
275
  return builder
241
276
 
277
+ @classmethod
278
+ def factory(cls, class_name: str, **kwargs: Any) -> Self:
279
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
280
+
242
281
 
243
282
  class AsyncContractEvents(BaseContractEvents):
244
283
  def __init__(
@@ -246,28 +285,84 @@ class AsyncContractEvents(BaseContractEvents):
246
285
  ) -> None:
247
286
  super().__init__(abi, w3, AsyncContractEvent, address)
248
287
 
288
+ def __iter__(self) -> Iterable["AsyncContractEvent"]:
289
+ if not hasattr(self, "_events") or not self._events:
290
+ return
291
+
292
+ for event in self._events:
293
+ yield self[abi_to_signature(event)]
294
+
295
+ def __getattr__(self, event_name: str) -> "AsyncContractEvent":
296
+ if super().__getattribute__("abi") is None:
297
+ raise NoABIFound(
298
+ "There is no ABI found for this contract.",
299
+ )
300
+ if "_events" not in self.__dict__ or len(self._events) == 0:
301
+ raise NoABIEventsFound(
302
+ "The abi for this contract contains no event definitions. ",
303
+ "Are you sure you provided the correct contract abi?",
304
+ )
305
+ elif get_name_from_abi_element_identifier(event_name) not in [
306
+ get_name_from_abi_element_identifier(event["name"])
307
+ for event in self._events
308
+ ]:
309
+ raise ABIEventNotFound(
310
+ f"The event '{event_name}' was not found in this contract's abi. ",
311
+ "Are you sure you provided the correct contract abi?",
312
+ )
313
+ else:
314
+ event_abi = get_abi_element(self._events, event_name)
315
+ argument_types = get_abi_input_types(event_abi)
316
+ event_signature = str(get_abi_element_signature(event_name, argument_types))
317
+ return super().__getattribute__(event_signature)
318
+
319
+ def __getitem__(self, event_name: str) -> "AsyncContractEvent":
320
+ return getattr(self, event_name)
321
+
249
322
 
250
323
  class AsyncContractFunction(BaseContractFunction):
251
324
  # mypy types
252
325
  w3: "AsyncWeb3"
253
326
 
254
327
  def __call__(self, *args: Any, **kwargs: Any) -> "AsyncContractFunction":
255
- clone = copy.copy(self)
256
- if args is None:
257
- clone.args = tuple()
258
- else:
259
- clone.args = args
328
+ element_name = self.abi_element_identifier
329
+ if element_name in ["fallback", "receive"] or len(args) + len(kwargs):
330
+ # Use only the name if a fallback, receive function
331
+ # or when args/kwargs are present to find the proper element
332
+ element_name = self.fn_name
333
+
334
+ function_abi = get_abi_element(
335
+ filter_by_types(
336
+ ["function", "constructor", "fallback", "receive"],
337
+ self.contract_abi,
338
+ ),
339
+ element_name,
340
+ *args,
341
+ abi_codec=self.w3.codec,
342
+ **kwargs,
343
+ )
260
344
 
261
- if kwargs is None:
262
- clone.kwargs = {}
263
- else:
264
- clone.kwargs = kwargs
265
- clone._set_function_info()
266
- return clone
345
+ argument_types = None
346
+ if function_abi["type"] not in ["fallback", "receive"]:
347
+ argument_types = get_abi_input_types(function_abi)
348
+
349
+ function_signature = str(
350
+ get_abi_element_signature(self.abi_element_identifier, argument_types)
351
+ )
352
+ contract_function = AsyncContractFunction.factory(
353
+ function_signature,
354
+ w3=self.w3,
355
+ contract_abi=self.contract_abi,
356
+ address=self.address,
357
+ abi_element_identifier=function_signature,
358
+ decode_tuples=self.decode_tuples,
359
+ )
360
+
361
+ return copy_contract_function(contract_function, *args, **kwargs)
267
362
 
268
363
  @classmethod
269
364
  def factory(cls, class_name: str, **kwargs: Any) -> Self:
270
- return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi"))
365
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
271
366
 
272
367
  async def call(
273
368
  self,
@@ -307,11 +402,13 @@ class AsyncContractFunction(BaseContractFunction):
307
402
 
308
403
  block_id = await async_parse_block_identifier(self.w3, block_identifier)
309
404
 
405
+ abi_element_identifier = abi_to_signature(self.abi)
406
+
310
407
  return await async_call_contract_function(
311
408
  self.w3,
312
409
  self.address,
313
410
  self._return_data_normalizers,
314
- self.abi_element_identifier,
411
+ abi_element_identifier,
315
412
  call_transaction,
316
413
  block_id,
317
414
  self.contract_abi,
@@ -325,10 +422,11 @@ class AsyncContractFunction(BaseContractFunction):
325
422
 
326
423
  async def transact(self, transaction: Optional[TxParams] = None) -> HexBytes:
327
424
  setup_transaction = self._transact(transaction)
425
+ abi_element_identifier = abi_to_signature(self.abi)
328
426
  return await async_transact_with_contract_function(
329
427
  self.address,
330
428
  self.w3,
331
- self.abi_element_identifier,
429
+ abi_element_identifier,
332
430
  setup_transaction,
333
431
  self.contract_abi,
334
432
  self.abi,
@@ -343,10 +441,11 @@ class AsyncContractFunction(BaseContractFunction):
343
441
  state_override: Optional[StateOverride] = None,
344
442
  ) -> int:
345
443
  setup_transaction = self._estimate_gas(transaction)
444
+ abi_element_identifier = abi_to_signature(self.abi)
346
445
  return await async_estimate_gas_for_function(
347
446
  self.address,
348
447
  self.w3,
349
- self.abi_element_identifier,
448
+ abi_element_identifier,
350
449
  setup_transaction,
351
450
  self.contract_abi,
352
451
  self.abi,
@@ -360,10 +459,11 @@ class AsyncContractFunction(BaseContractFunction):
360
459
  self, transaction: Optional[TxParams] = None
361
460
  ) -> TxParams:
362
461
  built_transaction = self._build_transaction(transaction)
462
+ abi_element_identifier = abi_to_signature(self.abi)
363
463
  return await async_build_transaction_for_function(
364
464
  self.address,
365
465
  self.w3,
366
- self.abi_element_identifier,
466
+ abi_element_identifier,
367
467
  built_transaction,
368
468
  self.contract_abi,
369
469
  self.abi,
@@ -414,23 +514,45 @@ class AsyncContractFunctions(BaseContractFunctions):
414
514
  ) -> None:
415
515
  super().__init__(abi, w3, AsyncContractFunction, address, decode_tuples)
416
516
 
517
+ def __iter__(self) -> Iterable["AsyncContractFunction"]:
518
+ if not hasattr(self, "_functions") or not self._functions:
519
+ return
520
+
521
+ for func in self._functions:
522
+ yield self[abi_to_signature(func)]
523
+
417
524
  def __getattr__(self, function_name: str) -> "AsyncContractFunction":
418
- if self.abi is None:
525
+ if super().__getattribute__("abi") is None:
419
526
  raise NoABIFound(
420
527
  "There is no ABI found for this contract.",
421
528
  )
422
- if "_functions" not in self.__dict__:
529
+ elif "_functions" not in self.__dict__ or len(self._functions) == 0:
423
530
  raise NoABIFunctionsFound(
424
531
  "The abi for this contract contains no function definitions. ",
425
532
  "Are you sure you provided the correct contract abi?",
426
533
  )
427
- elif function_name not in self.__dict__["_functions"]:
534
+ elif get_name_from_abi_element_identifier(function_name) not in [
535
+ get_name_from_abi_element_identifier(function["name"])
536
+ for function in self._functions
537
+ ]:
428
538
  raise ABIFunctionNotFound(
429
- f"The function '{function_name}' was not found in this contract's abi.",
430
- " Are you sure you provided the correct contract abi?",
539
+ f"The function '{function_name}' was not found in this contract's "
540
+ "abi. Are you sure you provided the correct contract abi?",
431
541
  )
432
- else:
433
- return super().__getattribute__(function_name)
542
+
543
+ function_identifier = function_name
544
+
545
+ if "(" not in function_name:
546
+ function_identifier = _get_any_abi_signature_with_name(
547
+ function_name, self._functions
548
+ )
549
+
550
+ return super().__getattribute__(
551
+ function_identifier,
552
+ )
553
+
554
+ def __getitem__(self, function_name: str) -> "AsyncContractFunction":
555
+ return getattr(self, function_name)
434
556
 
435
557
 
436
558
  class AsyncContract(BaseContract):
@@ -498,6 +620,16 @@ class AsyncContract(BaseContract):
498
620
  normalizers=normalizers,
499
621
  ),
500
622
  )
623
+
624
+ if contract.abi:
625
+ for abi in contract.abi:
626
+ abi_name = abi.get("name")
627
+ if abi_name in ["abi", "address"]:
628
+ raise Web3AttributeError(
629
+ f"Contract contains a reserved word `{abi_name}` "
630
+ f"and could not be instantiated."
631
+ )
632
+
501
633
  contract.functions = AsyncContractFunctions(
502
634
  contract.abi, contract.w3, decode_tuples=contract.decode_tuples
503
635
  )
@@ -556,6 +688,24 @@ class AsyncContract(BaseContract):
556
688
  ) -> "AsyncContractFunction":
557
689
  return get_function_by_identifier(fns, identifier)
558
690
 
691
+ @combomethod
692
+ def find_events_by_identifier(
693
+ cls,
694
+ contract_abi: ABI,
695
+ w3: "AsyncWeb3",
696
+ address: ChecksumAddress,
697
+ callable_check: Callable[..., Any],
698
+ ) -> List["AsyncContractEvent"]:
699
+ return find_events_by_identifier(
700
+ contract_abi, w3, address, callable_check, AsyncContractEvent
701
+ )
702
+
703
+ @combomethod
704
+ def get_event_by_identifier(
705
+ cls, events: Sequence["AsyncContractEvent"], identifier: str
706
+ ) -> "AsyncContractEvent":
707
+ return get_event_by_identifier(events, identifier)
708
+
559
709
 
560
710
  class AsyncContractCaller(BaseContractCaller):
561
711
  # mypy types
@@ -580,12 +730,12 @@ class AsyncContractCaller(BaseContractCaller):
580
730
  self._functions = get_all_function_abis(self.abi)
581
731
 
582
732
  for func in self._functions:
733
+ abi_signature = abi_to_signature(func)
583
734
  fn = AsyncContractFunction.factory(
584
- func["name"],
735
+ abi_signature,
585
736
  w3=w3,
586
737
  contract_abi=self.abi,
587
738
  address=self.address,
588
- abi_element_identifier=func["name"],
589
739
  decode_tuples=decode_tuples,
590
740
  )
591
741
 
@@ -597,7 +747,7 @@ class AsyncContractCaller(BaseContractCaller):
597
747
  ccip_read_enabled=ccip_read_enabled,
598
748
  )
599
749
 
600
- setattr(self, func["name"], caller_method)
750
+ setattr(self, abi_signature, caller_method)
601
751
 
602
752
  def __call__(
603
753
  self,