web3 7.0.0b2__py3-none-any.whl → 7.7.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 (144) hide show
  1. ens/__init__.py +13 -2
  2. ens/_normalization.py +4 -4
  3. ens/async_ens.py +27 -15
  4. ens/base_ens.py +3 -1
  5. ens/contract_data.py +2 -2
  6. ens/ens.py +10 -7
  7. ens/exceptions.py +16 -29
  8. ens/specs/nf.json +1 -1
  9. ens/specs/normalization_spec.json +1 -1
  10. ens/utils.py +24 -32
  11. web3/__init__.py +23 -12
  12. web3/_utils/abi.py +157 -263
  13. web3/_utils/async_transactions.py +34 -20
  14. web3/_utils/batching.py +217 -0
  15. web3/_utils/blocks.py +6 -2
  16. web3/_utils/caching/__init__.py +12 -0
  17. web3/_utils/caching/caching_utils.py +433 -0
  18. web3/_utils/caching/request_caching_validation.py +287 -0
  19. web3/_utils/compat/__init__.py +2 -3
  20. web3/_utils/contract_sources/compile_contracts.py +1 -1
  21. web3/_utils/contract_sources/contract_data/ambiguous_function_contract.py +42 -0
  22. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  23. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  24. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  25. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  26. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  27. web3/_utils/contract_sources/contract_data/event_contracts.py +50 -5
  28. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  29. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  30. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  31. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  32. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  33. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  34. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  35. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  36. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  37. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  38. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  39. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  40. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  41. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  42. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  43. web3/_utils/contracts.py +172 -220
  44. web3/_utils/datatypes.py +5 -1
  45. web3/_utils/decorators.py +6 -1
  46. web3/_utils/empty.py +1 -1
  47. web3/_utils/encoding.py +16 -12
  48. web3/_utils/error_formatters_utils.py +5 -3
  49. web3/_utils/events.py +78 -72
  50. web3/_utils/fee_utils.py +1 -3
  51. web3/_utils/filters.py +24 -22
  52. web3/_utils/formatters.py +2 -2
  53. web3/_utils/http.py +8 -2
  54. web3/_utils/http_session_manager.py +314 -0
  55. web3/_utils/math.py +14 -15
  56. web3/_utils/method_formatters.py +161 -34
  57. web3/_utils/module.py +2 -1
  58. web3/_utils/module_testing/__init__.py +3 -2
  59. web3/_utils/module_testing/eth_module.py +736 -583
  60. web3/_utils/module_testing/go_ethereum_debug_module.py +128 -0
  61. web3/_utils/module_testing/module_testing_utils.py +81 -24
  62. web3/_utils/module_testing/persistent_connection_provider.py +702 -220
  63. web3/_utils/module_testing/utils.py +114 -33
  64. web3/_utils/module_testing/web3_module.py +438 -17
  65. web3/_utils/normalizers.py +13 -11
  66. web3/_utils/rpc_abi.py +10 -22
  67. web3/_utils/threads.py +8 -7
  68. web3/_utils/transactions.py +32 -25
  69. web3/_utils/type_conversion.py +5 -1
  70. web3/_utils/validation.py +20 -17
  71. web3/beacon/__init__.py +5 -0
  72. web3/beacon/api_endpoints.py +3 -0
  73. web3/beacon/async_beacon.py +29 -6
  74. web3/beacon/beacon.py +24 -6
  75. web3/contract/__init__.py +7 -0
  76. web3/contract/async_contract.py +285 -82
  77. web3/contract/base_contract.py +556 -258
  78. web3/contract/contract.py +295 -84
  79. web3/contract/utils.py +251 -55
  80. web3/datastructures.py +49 -34
  81. web3/eth/__init__.py +7 -0
  82. web3/eth/async_eth.py +89 -69
  83. web3/eth/base_eth.py +7 -3
  84. web3/eth/eth.py +43 -66
  85. web3/exceptions.py +158 -83
  86. web3/gas_strategies/time_based.py +8 -6
  87. web3/geth.py +53 -184
  88. web3/main.py +77 -17
  89. web3/manager.py +362 -95
  90. web3/method.py +43 -15
  91. web3/middleware/__init__.py +17 -0
  92. web3/middleware/attrdict.py +12 -22
  93. web3/middleware/base.py +55 -2
  94. web3/middleware/filter.py +45 -23
  95. web3/middleware/formatting.py +6 -3
  96. web3/middleware/names.py +4 -1
  97. web3/middleware/signing.py +15 -6
  98. web3/middleware/stalecheck.py +2 -1
  99. web3/module.py +61 -25
  100. web3/providers/__init__.py +21 -0
  101. web3/providers/async_base.py +87 -32
  102. web3/providers/base.py +77 -32
  103. web3/providers/eth_tester/__init__.py +5 -0
  104. web3/providers/eth_tester/defaults.py +2 -55
  105. web3/providers/eth_tester/main.py +41 -15
  106. web3/providers/eth_tester/middleware.py +16 -17
  107. web3/providers/ipc.py +41 -17
  108. web3/providers/legacy_websocket.py +26 -1
  109. web3/providers/persistent/__init__.py +7 -0
  110. web3/providers/persistent/async_ipc.py +61 -121
  111. web3/providers/persistent/persistent.py +323 -16
  112. web3/providers/persistent/persistent_connection.py +54 -5
  113. web3/providers/persistent/request_processor.py +136 -56
  114. web3/providers/persistent/subscription_container.py +56 -0
  115. web3/providers/persistent/subscription_manager.py +233 -0
  116. web3/providers/persistent/websocket.py +29 -92
  117. web3/providers/rpc/__init__.py +5 -0
  118. web3/providers/rpc/async_rpc.py +73 -18
  119. web3/providers/rpc/rpc.py +73 -30
  120. web3/providers/rpc/utils.py +1 -13
  121. web3/scripts/install_pre_releases.py +33 -0
  122. web3/scripts/parse_pygeth_version.py +16 -0
  123. web3/testing.py +4 -4
  124. web3/tracing.py +9 -5
  125. web3/types.py +141 -74
  126. web3/utils/__init__.py +64 -5
  127. web3/utils/abi.py +790 -10
  128. web3/utils/address.py +8 -0
  129. web3/utils/async_exception_handling.py +20 -11
  130. web3/utils/caching.py +34 -4
  131. web3/utils/exception_handling.py +9 -12
  132. web3/utils/subscriptions.py +285 -0
  133. {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/LICENSE +1 -1
  134. web3-7.7.0.dist-info/METADATA +130 -0
  135. web3-7.7.0.dist-info/RECORD +171 -0
  136. {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/WHEEL +1 -1
  137. web3/_utils/caching.py +0 -155
  138. web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
  139. web3/_utils/module_testing/go_ethereum_personal_module.py +0 -300
  140. web3/_utils/request.py +0 -265
  141. web3-7.0.0b2.dist-info/METADATA +0 -106
  142. web3-7.0.0b2.dist-info/RECORD +0 -163
  143. /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
  144. {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,3 @@
1
- import copy
2
1
  from typing import (
3
2
  TYPE_CHECKING,
4
3
  Any,
@@ -14,11 +13,18 @@ from typing import (
14
13
  )
15
14
 
16
15
  from eth_typing import (
16
+ ABI,
17
+ ABIFunction,
17
18
  ChecksumAddress,
18
19
  )
19
20
  from eth_utils import (
20
21
  combomethod,
21
22
  )
23
+ from eth_utils.abi import (
24
+ abi_to_signature,
25
+ filter_abi_by_type,
26
+ get_abi_input_names,
27
+ )
22
28
  from eth_utils.toolz import (
23
29
  partial,
24
30
  )
@@ -28,9 +34,13 @@ from hexbytes import (
28
34
 
29
35
  from web3._utils.abi import (
30
36
  fallback_func_abi_exists,
31
- filter_by_type,
37
+ get_name_from_abi_element_identifier,
32
38
  receive_func_abi_exists,
33
39
  )
40
+ from web3._utils.abi_element_identifiers import (
41
+ FallbackFn,
42
+ ReceiveFn,
43
+ )
34
44
  from web3._utils.async_transactions import (
35
45
  async_fill_transaction_defaults,
36
46
  )
@@ -39,6 +49,8 @@ from web3._utils.compat import (
39
49
  )
40
50
  from web3._utils.contracts import (
41
51
  async_parse_block_identifier,
52
+ copy_contract_event,
53
+ copy_contract_function,
42
54
  )
43
55
  from web3._utils.datatypes import (
44
56
  PropertyCheckingFactory,
@@ -50,10 +62,6 @@ from web3._utils.events import (
50
62
  from web3._utils.filters import (
51
63
  AsyncLogFilter,
52
64
  )
53
- from web3._utils.function_identifiers import (
54
- FallbackFn,
55
- ReceiveFn,
56
- )
57
65
  from web3._utils.normalizers import (
58
66
  normalize_abi,
59
67
  normalize_address_no_ens,
@@ -75,24 +83,34 @@ from web3.contract.utils import (
75
83
  async_call_contract_function,
76
84
  async_estimate_gas_for_function,
77
85
  async_transact_with_contract_function,
86
+ find_events_by_identifier,
78
87
  find_functions_by_identifier,
88
+ get_event_by_identifier,
79
89
  get_function_by_identifier,
80
90
  )
81
91
  from web3.exceptions import (
92
+ ABIEventNotFound,
82
93
  ABIFunctionNotFound,
94
+ MismatchedABI,
95
+ NoABIEventsFound,
83
96
  NoABIFound,
84
97
  NoABIFunctionsFound,
98
+ Web3AttributeError,
99
+ Web3TypeError,
85
100
  Web3ValidationError,
101
+ Web3ValueError,
86
102
  )
87
103
  from web3.types import (
88
- ABI,
89
104
  BlockIdentifier,
90
105
  EventData,
91
106
  StateOverride,
92
107
  TxParams,
93
108
  )
94
- from web3.utils import (
95
- get_abi_input_names,
109
+ from web3.utils.abi import (
110
+ _filter_by_argument_count,
111
+ _get_any_abi_signature_with_name,
112
+ _mismatched_abi_error_diagnosis,
113
+ get_abi_element,
96
114
  )
97
115
 
98
116
  if TYPE_CHECKING:
@@ -104,15 +122,19 @@ class AsyncContractEvent(BaseContractEvent):
104
122
  # mypy types
105
123
  w3: "AsyncWeb3"
106
124
 
125
+ def __call__(self, *args: Any, **kwargs: Any) -> "AsyncContractEvent":
126
+ return copy_contract_event(self, *args, **kwargs)
127
+
107
128
  @combomethod
108
129
  async def get_logs(
109
130
  self,
110
131
  argument_filters: Optional[Dict[str, Any]] = None,
111
- fromBlock: Optional[BlockIdentifier] = None,
112
- toBlock: Optional[BlockIdentifier] = None,
132
+ from_block: Optional[BlockIdentifier] = None,
133
+ to_block: Optional[BlockIdentifier] = None,
113
134
  block_hash: Optional[HexBytes] = None,
114
135
  ) -> Awaitable[Iterable[EventData]]:
115
- """Get events for this contract instance using eth_getLogs API.
136
+ """
137
+ Get events for this contract instance using eth_getLogs API.
116
138
 
117
139
  This is a stateless method, as opposed to createFilter.
118
140
  It can be safely called against nodes which do not provide
@@ -131,7 +153,7 @@ class AsyncContractEvent(BaseContractEvent):
131
153
  from = max(mycontract.web3.eth.block_number - 10, 1)
132
154
  to = mycontract.web3.eth.block_number
133
155
 
134
- events = mycontract.events.Transfer.getLogs(fromBlock=from, toBlock=to)
156
+ events = mycontract.events.Transfer.get_logs(from_block=from, to_block=to)
135
157
 
136
158
  for e in events:
137
159
  print(e["args"]["from"],
@@ -161,17 +183,15 @@ class AsyncContractEvent(BaseContractEvent):
161
183
 
162
184
  :param argument_filters: Filter by argument values. Indexed arguments are
163
185
  filtered by the node while non-indexed arguments are filtered by the library.
164
- :param fromBlock: block number or "latest", defaults to "latest"
165
- :param toBlock: block number or "latest". Defaults to "latest"
186
+ :param from_block: block number or "latest", defaults to "latest"
187
+ :param to_block: block number or "latest". Defaults to "latest"
166
188
  :param block_hash: block hash. Cannot be set at the
167
- same time as fromBlock or toBlock
189
+ same time as ``from_block`` or ``to_block``
168
190
  :yield: Tuple of :class:`AttributeDict` instances
169
191
  """
170
- event_abi = self._get_event_abi()
171
-
172
192
  # validate ``argument_filters`` if present
173
193
  if argument_filters is not None:
174
- event_arg_names = get_abi_input_names(event_abi)
194
+ event_arg_names = get_abi_input_names(self.abi)
175
195
  if not all(arg in event_arg_names for arg in argument_filters.keys()):
176
196
  raise Web3ValidationError(
177
197
  "When filtering by argument names, all argument names must be "
@@ -179,48 +199,48 @@ class AsyncContractEvent(BaseContractEvent):
179
199
  )
180
200
 
181
201
  _filter_params = self._get_event_filter_params(
182
- event_abi, argument_filters, fromBlock, toBlock, block_hash
202
+ self.abi, argument_filters, from_block, to_block, block_hash
183
203
  )
184
204
  # call JSON-RPC API
185
205
  logs = await self.w3.eth.get_logs(_filter_params)
186
206
 
187
207
  # convert raw binary data to Python proxy objects as described by ABI:
188
208
  all_event_logs = tuple(
189
- get_event_data(self.w3.codec, event_abi, entry) for entry in logs
209
+ get_event_data(self.w3.codec, self.abi, entry) for entry in logs
190
210
  )
191
211
  filtered_logs = self._process_get_logs_argument_filters(
192
- event_abi,
212
+ self.abi,
193
213
  all_event_logs,
194
214
  argument_filters,
195
215
  )
196
- return cast(Awaitable[Iterable[EventData]], filtered_logs)
216
+ sorted_logs = sorted(filtered_logs, key=lambda e: e["logIndex"])
217
+ sorted_logs = sorted(sorted_logs, key=lambda e: e["blockNumber"])
218
+ return cast(Awaitable[Iterable[EventData]], sorted_logs)
197
219
 
198
220
  @combomethod
199
221
  async def create_filter(
200
222
  self,
201
223
  *, # PEP 3102
202
224
  argument_filters: Optional[Dict[str, Any]] = None,
203
- fromBlock: Optional[BlockIdentifier] = None,
204
- toBlock: BlockIdentifier = "latest",
225
+ from_block: Optional[BlockIdentifier] = None,
226
+ to_block: BlockIdentifier = "latest",
205
227
  address: Optional[ChecksumAddress] = None,
206
228
  topics: Optional[Sequence[Any]] = None,
207
229
  ) -> AsyncLogFilter:
208
230
  """
209
231
  Create filter object that tracks logs emitted by this contract event.
210
232
  """
211
- filter_builder = AsyncEventFilterBuilder(self._get_event_abi(), self.w3.codec)
233
+ filter_builder = AsyncEventFilterBuilder(self.abi, self.w3.codec)
212
234
  self._set_up_filter_builder(
213
235
  argument_filters,
214
- fromBlock,
215
- toBlock,
236
+ from_block,
237
+ to_block,
216
238
  address,
217
239
  topics,
218
240
  filter_builder,
219
241
  )
220
242
  log_filter = await filter_builder.deploy(self.w3)
221
- log_filter.log_entry_formatter = get_event_data(
222
- self.w3.codec, self._get_event_abi()
223
- )
243
+ log_filter.log_entry_formatter = get_event_data(self.w3.codec, self.abi)
224
244
  log_filter.builder = filter_builder
225
245
 
226
246
  return log_filter
@@ -228,13 +248,17 @@ class AsyncContractEvent(BaseContractEvent):
228
248
  @combomethod
229
249
  def build_filter(self) -> AsyncEventFilterBuilder:
230
250
  builder = AsyncEventFilterBuilder(
231
- self._get_event_abi(),
251
+ self.abi,
232
252
  self.w3.codec,
233
- formatter=get_event_data(self.w3.codec, self._get_event_abi()),
253
+ formatter=get_event_data(self.w3.codec, self.abi),
234
254
  )
235
255
  builder.address = self.address
236
256
  return builder
237
257
 
258
+ @classmethod
259
+ def factory(cls, class_name: str, **kwargs: Any) -> Self:
260
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
261
+
238
262
 
239
263
  class AsyncContractEvents(BaseContractEvents):
240
264
  def __init__(
@@ -242,28 +266,150 @@ class AsyncContractEvents(BaseContractEvents):
242
266
  ) -> None:
243
267
  super().__init__(abi, w3, AsyncContractEvent, address)
244
268
 
269
+ def __iter__(self) -> Iterable["AsyncContractEvent"]:
270
+ if not hasattr(self, "_events") or not self._events:
271
+ return
272
+
273
+ for event in self._events:
274
+ yield self[abi_to_signature(event)]
275
+
276
+ def __getattr__(self, event_name: str) -> "AsyncContractEvent":
277
+ if super().__getattribute__("abi") is None:
278
+ raise NoABIFound(
279
+ "There is no ABI found for this contract.",
280
+ )
281
+ elif "_events" not in self.__dict__ or len(self._events) == 0:
282
+ raise NoABIEventsFound(
283
+ "The abi for this contract contains no event definitions. ",
284
+ "Are you sure you provided the correct contract abi?",
285
+ )
286
+ elif get_name_from_abi_element_identifier(event_name) not in [
287
+ get_name_from_abi_element_identifier(event["name"])
288
+ for event in self._events
289
+ ]:
290
+ raise ABIEventNotFound(
291
+ f"The event '{event_name}' was not found in this contract's abi. ",
292
+ "Are you sure you provided the correct contract abi?",
293
+ )
294
+
295
+ if "(" not in event_name:
296
+ event_name = _get_any_abi_signature_with_name(event_name, self._events)
297
+ else:
298
+ event_name = f"_{event_name}"
299
+
300
+ return super().__getattribute__(event_name)
301
+
302
+ def __getitem__(self, event_name: str) -> "AsyncContractEvent":
303
+ return getattr(self, event_name)
304
+
245
305
 
246
306
  class AsyncContractFunction(BaseContractFunction):
247
307
  # mypy types
248
308
  w3: "AsyncWeb3"
249
309
 
250
310
  def __call__(self, *args: Any, **kwargs: Any) -> "AsyncContractFunction":
251
- clone = copy.copy(self)
252
- if args is None:
253
- clone.args = tuple()
254
- else:
255
- clone.args = args
311
+ # When a function is called, check arguments to obtain the correct function
312
+ # in the contract. self will be used if all args and kwargs are
313
+ # encodable to self.abi, otherwise the correct function is obtained from
314
+ # the contract.
315
+ if (
316
+ self.abi_element_identifier in [FallbackFn, ReceiveFn]
317
+ or self.abi_element_identifier == "constructor"
318
+ ):
319
+ return copy_contract_function(self, *args, **kwargs)
320
+
321
+ all_functions = cast(
322
+ List[ABIFunction],
323
+ filter_abi_by_type(
324
+ "function",
325
+ self.contract_abi,
326
+ ),
327
+ )
328
+ # Filter functions by name to obtain function signatures
329
+ function_name = get_name_from_abi_element_identifier(
330
+ self.abi_element_identifier
331
+ )
332
+ function_abis = [
333
+ function for function in all_functions if function["name"] == function_name
334
+ ]
335
+ num_args = len(args) + len(kwargs)
336
+ function_abis_with_arg_count = cast(
337
+ List[ABIFunction],
338
+ _filter_by_argument_count(
339
+ num_args,
340
+ function_abis,
341
+ ),
342
+ )
256
343
 
257
- if kwargs is None:
258
- clone.kwargs = {}
344
+ if not len(function_abis_with_arg_count):
345
+ # Build an ABI without arguments to determine if one exists
346
+ function_abis_with_arg_count = [
347
+ ABIFunction({"type": "function", "name": function_name})
348
+ ]
349
+
350
+ # Check that arguments in call match a function ABI
351
+ num_attempts = 0
352
+ function_abi_matches = []
353
+ contract_function = None
354
+ for abi in function_abis_with_arg_count:
355
+ try:
356
+ num_attempts += 1
357
+
358
+ # Search for a function ABI that matches the arguments used
359
+ function_abi_matches.append(
360
+ cast(
361
+ ABIFunction,
362
+ get_abi_element(
363
+ function_abis,
364
+ abi_to_signature(abi),
365
+ *args,
366
+ abi_codec=self.w3.codec,
367
+ **kwargs,
368
+ ),
369
+ )
370
+ )
371
+ except MismatchedABI:
372
+ # ignore exceptions
373
+ continue
374
+
375
+ if len(function_abi_matches) == 1:
376
+ function_abi = function_abi_matches[0]
377
+ if abi_to_signature(self.abi) == abi_to_signature(function_abi):
378
+ contract_function = self
379
+ else:
380
+ # Found a match that is not self
381
+ contract_function = AsyncContractFunction.factory(
382
+ abi_to_signature(function_abi),
383
+ w3=self.w3,
384
+ contract_abi=self.contract_abi,
385
+ address=self.address,
386
+ abi_element_identifier=abi_to_signature(function_abi),
387
+ abi=function_abi,
388
+ )
259
389
  else:
260
- clone.kwargs = kwargs
261
- clone._set_function_info()
262
- return clone
390
+ for abi in function_abi_matches:
391
+ if abi_to_signature(self.abi) == abi_to_signature(abi):
392
+ contract_function = self
393
+ break
394
+ else:
395
+ # Raise exception if multiple found
396
+ raise MismatchedABI(
397
+ _mismatched_abi_error_diagnosis(
398
+ function_name,
399
+ self.contract_abi,
400
+ len(function_abi_matches),
401
+ num_args,
402
+ *args,
403
+ abi_codec=self.w3.codec,
404
+ **kwargs,
405
+ )
406
+ )
407
+
408
+ return copy_contract_function(contract_function, *args, **kwargs)
263
409
 
264
410
  @classmethod
265
411
  def factory(cls, class_name: str, **kwargs: Any) -> Self:
266
- return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi"))
412
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
267
413
 
268
414
  async def call(
269
415
  self,
@@ -303,11 +449,13 @@ class AsyncContractFunction(BaseContractFunction):
303
449
 
304
450
  block_id = await async_parse_block_identifier(self.w3, block_identifier)
305
451
 
452
+ abi_element_identifier = abi_to_signature(self.abi)
453
+
306
454
  return await async_call_contract_function(
307
455
  self.w3,
308
456
  self.address,
309
457
  self._return_data_normalizers,
310
- self.function_identifier,
458
+ abi_element_identifier,
311
459
  call_transaction,
312
460
  block_id,
313
461
  self.contract_abi,
@@ -315,21 +463,22 @@ class AsyncContractFunction(BaseContractFunction):
315
463
  state_override,
316
464
  ccip_read_enabled,
317
465
  self.decode_tuples,
318
- *self.args,
319
- **self.kwargs,
466
+ *self.args or (),
467
+ **self.kwargs or {},
320
468
  )
321
469
 
322
470
  async def transact(self, transaction: Optional[TxParams] = None) -> HexBytes:
323
471
  setup_transaction = self._transact(transaction)
472
+ abi_element_identifier = abi_to_signature(self.abi)
324
473
  return await async_transact_with_contract_function(
325
474
  self.address,
326
475
  self.w3,
327
- self.function_identifier,
476
+ abi_element_identifier,
328
477
  setup_transaction,
329
478
  self.contract_abi,
330
479
  self.abi,
331
- *self.args,
332
- **self.kwargs,
480
+ *self.args or (),
481
+ **self.kwargs or {},
333
482
  )
334
483
 
335
484
  async def estimate_gas(
@@ -339,32 +488,34 @@ class AsyncContractFunction(BaseContractFunction):
339
488
  state_override: Optional[StateOverride] = None,
340
489
  ) -> int:
341
490
  setup_transaction = self._estimate_gas(transaction)
491
+ abi_element_identifier = abi_to_signature(self.abi)
342
492
  return await async_estimate_gas_for_function(
343
493
  self.address,
344
494
  self.w3,
345
- self.function_identifier,
495
+ abi_element_identifier,
346
496
  setup_transaction,
347
497
  self.contract_abi,
348
498
  self.abi,
349
499
  block_identifier,
350
500
  state_override,
351
- *self.args,
352
- **self.kwargs,
501
+ *self.args or (),
502
+ **self.kwargs or {},
353
503
  )
354
504
 
355
505
  async def build_transaction(
356
506
  self, transaction: Optional[TxParams] = None
357
507
  ) -> TxParams:
358
508
  built_transaction = self._build_transaction(transaction)
509
+ abi_element_identifier = abi_to_signature(self.abi)
359
510
  return await async_build_transaction_for_function(
360
511
  self.address,
361
512
  self.w3,
362
- self.function_identifier,
513
+ abi_element_identifier,
363
514
  built_transaction,
364
515
  self.contract_abi,
365
516
  self.abi,
366
- *self.args,
367
- **self.kwargs,
517
+ *self.args or (),
518
+ **self.kwargs or {},
368
519
  )
369
520
 
370
521
  @staticmethod
@@ -379,7 +530,7 @@ class AsyncContractFunction(BaseContractFunction):
379
530
  w3=async_w3,
380
531
  contract_abi=abi,
381
532
  address=address,
382
- function_identifier=FallbackFn,
533
+ abi_element_identifier=FallbackFn,
383
534
  )()
384
535
  return cast(AsyncContractFunction, NonExistentFallbackFunction())
385
536
 
@@ -395,7 +546,7 @@ class AsyncContractFunction(BaseContractFunction):
395
546
  w3=async_w3,
396
547
  contract_abi=abi,
397
548
  address=address,
398
- function_identifier=ReceiveFn,
549
+ abi_element_identifier=ReceiveFn,
399
550
  )()
400
551
  return cast(AsyncContractFunction, NonExistentReceiveFunction())
401
552
 
@@ -410,23 +561,45 @@ class AsyncContractFunctions(BaseContractFunctions):
410
561
  ) -> None:
411
562
  super().__init__(abi, w3, AsyncContractFunction, address, decode_tuples)
412
563
 
564
+ def __iter__(self) -> Iterable["AsyncContractFunction"]:
565
+ if not hasattr(self, "_functions") or not self._functions:
566
+ return
567
+
568
+ for func in self._functions:
569
+ yield self[abi_to_signature(func)]
570
+
413
571
  def __getattr__(self, function_name: str) -> "AsyncContractFunction":
414
- if self.abi is None:
572
+ if super().__getattribute__("abi") is None:
415
573
  raise NoABIFound(
416
574
  "There is no ABI found for this contract.",
417
575
  )
418
- if "_functions" not in self.__dict__:
576
+ elif "_functions" not in self.__dict__ or len(self._functions) == 0:
419
577
  raise NoABIFunctionsFound(
420
578
  "The abi for this contract contains no function definitions. ",
421
579
  "Are you sure you provided the correct contract abi?",
422
580
  )
423
- elif function_name not in self.__dict__["_functions"]:
581
+ elif get_name_from_abi_element_identifier(function_name) not in [
582
+ get_name_from_abi_element_identifier(function["name"])
583
+ for function in self._functions
584
+ ]:
424
585
  raise ABIFunctionNotFound(
425
- f"The function '{function_name}' was not found in this contract's abi.",
426
- " Are you sure you provided the correct contract abi?",
586
+ f"The function '{function_name}' was not found in this ",
587
+ "contract's abi.",
588
+ )
589
+
590
+ if "(" not in function_name:
591
+ function_name = _get_any_abi_signature_with_name(
592
+ function_name, self._functions
427
593
  )
428
594
  else:
429
- return super().__getattribute__(function_name)
595
+ function_name = f"_{function_name}"
596
+
597
+ return super().__getattribute__(
598
+ function_name,
599
+ )
600
+
601
+ def __getitem__(self, function_name: str) -> "AsyncContractFunction":
602
+ return getattr(self, function_name)
430
603
 
431
604
 
432
605
  class AsyncContract(BaseContract):
@@ -440,12 +613,13 @@ class AsyncContract(BaseContract):
440
613
  events: AsyncContractEvents = None
441
614
 
442
615
  def __init__(self, address: Optional[ChecksumAddress] = None) -> None:
443
- """Create a new smart contract proxy object.
444
-
445
- :param address: Contract address as 0x hex string"""
616
+ """
617
+ Create a new smart contract proxy object.
446
618
 
619
+ :param address: Contract address as 0x hex string
620
+ """
447
621
  if self.w3 is None:
448
- raise AttributeError(
622
+ raise Web3AttributeError(
449
623
  "The `Contract` class has not been initialized. Please use the "
450
624
  "`web3.contract` interface to create your contract class."
451
625
  )
@@ -454,14 +628,18 @@ class AsyncContract(BaseContract):
454
628
  self.address = normalize_address_no_ens(address)
455
629
 
456
630
  if not self.address:
457
- raise TypeError(
631
+ raise Web3TypeError(
458
632
  "The address argument is required to instantiate a contract."
459
633
  )
460
634
  self.functions = AsyncContractFunctions(
461
635
  self.abi, self.w3, self.address, decode_tuples=self.decode_tuples
462
636
  )
463
637
  self.caller = AsyncContractCaller(
464
- self.abi, self.w3, self.address, decode_tuples=self.decode_tuples
638
+ self.abi,
639
+ self.w3,
640
+ self.address,
641
+ decode_tuples=self.decode_tuples,
642
+ contract_functions=self.functions,
465
643
  )
466
644
  self.events = AsyncContractEvents(self.abi, self.w3, self.address)
467
645
  self.fallback = AsyncContract.get_fallback_function(
@@ -493,6 +671,16 @@ class AsyncContract(BaseContract):
493
671
  normalizers=normalizers,
494
672
  ),
495
673
  )
674
+
675
+ if contract.abi:
676
+ for abi in contract.abi:
677
+ abi_name = abi.get("name")
678
+ if abi_name in ["abi", "address"]:
679
+ raise Web3AttributeError(
680
+ f"Contract contains a reserved word `{abi_name}` "
681
+ f"and could not be instantiated."
682
+ )
683
+
496
684
  contract.functions = AsyncContractFunctions(
497
685
  contract.abi, contract.w3, decode_tuples=contract.decode_tuples
498
686
  )
@@ -501,6 +689,7 @@ class AsyncContract(BaseContract):
501
689
  contract.w3,
502
690
  contract.address,
503
691
  decode_tuples=contract.decode_tuples,
692
+ contract_functions=contract.functions,
504
693
  )
505
694
  contract.events = AsyncContractEvents(contract.abi, contract.w3)
506
695
  contract.fallback = AsyncContract.get_fallback_function(
@@ -523,7 +712,7 @@ class AsyncContract(BaseContract):
523
712
  :return: a contract constructor object
524
713
  """
525
714
  if cls.bytecode is None:
526
- raise ValueError(
715
+ raise Web3ValueError(
527
716
  "Cannot call constructor on a contract that does not have "
528
717
  "'bytecode' associated with it"
529
718
  )
@@ -551,6 +740,24 @@ class AsyncContract(BaseContract):
551
740
  ) -> "AsyncContractFunction":
552
741
  return get_function_by_identifier(fns, identifier)
553
742
 
743
+ @combomethod
744
+ def find_events_by_identifier(
745
+ cls,
746
+ contract_abi: ABI,
747
+ w3: "AsyncWeb3",
748
+ address: ChecksumAddress,
749
+ callable_check: Callable[..., Any],
750
+ ) -> List["AsyncContractEvent"]:
751
+ return find_events_by_identifier(
752
+ contract_abi, w3, address, callable_check, AsyncContractEvent
753
+ )
754
+
755
+ @combomethod
756
+ def get_event_by_identifier(
757
+ cls, events: Sequence["AsyncContractEvent"], identifier: str
758
+ ) -> "AsyncContractEvent":
759
+ return get_event_by_identifier(events, identifier)
760
+
554
761
 
555
762
  class AsyncContractCaller(BaseContractCaller):
556
763
  # mypy types
@@ -565,6 +772,7 @@ class AsyncContractCaller(BaseContractCaller):
565
772
  block_identifier: BlockIdentifier = None,
566
773
  ccip_read_enabled: Optional[bool] = None,
567
774
  decode_tuples: Optional[bool] = False,
775
+ contract_functions: Optional[AsyncContractFunctions] = None,
568
776
  ) -> None:
569
777
  super().__init__(abi, w3, address, decode_tuples=decode_tuples)
570
778
 
@@ -572,17 +780,13 @@ class AsyncContractCaller(BaseContractCaller):
572
780
  if transaction is None:
573
781
  transaction = {}
574
782
 
575
- self._functions = filter_by_type("function", self.abi)
576
- for func in self._functions:
577
- fn = AsyncContractFunction.factory(
578
- func["name"],
579
- w3=w3,
580
- contract_abi=self.abi,
581
- address=self.address,
582
- function_identifier=func["name"],
583
- decode_tuples=decode_tuples,
783
+ if contract_functions is None:
784
+ contract_functions = AsyncContractFunctions(
785
+ abi, w3, address, decode_tuples=decode_tuples
584
786
  )
585
787
 
788
+ self._functions = contract_functions._functions
789
+ for fn in contract_functions.__iter__():
586
790
  caller_method = partial(
587
791
  self.call_function,
588
792
  fn,
@@ -590,8 +794,7 @@ class AsyncContractCaller(BaseContractCaller):
590
794
  block_identifier=block_identifier,
591
795
  ccip_read_enabled=ccip_read_enabled,
592
796
  )
593
-
594
- setattr(self, func["name"], caller_method)
797
+ setattr(self, str(fn.abi_element_identifier), caller_method)
595
798
 
596
799
  def __call__(
597
800
  self,