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
web3/contract/contract.py CHANGED
@@ -1,4 +1,3 @@
1
- import copy
2
1
  from typing import (
3
2
  TYPE_CHECKING,
4
3
  Any,
@@ -13,10 +12,15 @@ from typing import (
13
12
  )
14
13
 
15
14
  from eth_typing import (
15
+ ABI,
16
+ ABIFunction,
16
17
  ChecksumAddress,
17
18
  )
18
19
  from eth_utils import (
20
+ abi_to_signature,
19
21
  combomethod,
22
+ filter_abi_by_type,
23
+ get_abi_input_names,
20
24
  )
21
25
  from eth_utils.toolz import (
22
26
  partial,
@@ -27,13 +31,19 @@ from hexbytes import (
27
31
 
28
32
  from web3._utils.abi import (
29
33
  fallback_func_abi_exists,
30
- filter_by_type,
34
+ get_name_from_abi_element_identifier,
31
35
  receive_func_abi_exists,
32
36
  )
37
+ from web3._utils.abi_element_identifiers import (
38
+ FallbackFn,
39
+ ReceiveFn,
40
+ )
33
41
  from web3._utils.compat import (
34
42
  Self,
35
43
  )
36
44
  from web3._utils.contracts import (
45
+ copy_contract_event,
46
+ copy_contract_function,
37
47
  parse_block_identifier,
38
48
  )
39
49
  from web3._utils.datatypes import (
@@ -46,10 +56,6 @@ from web3._utils.events import (
46
56
  from web3._utils.filters import (
47
57
  LogFilter,
48
58
  )
49
- from web3._utils.function_identifiers import (
50
- FallbackFn,
51
- ReceiveFn,
52
- )
53
59
  from web3._utils.normalizers import (
54
60
  normalize_abi,
55
61
  normalize_address,
@@ -73,25 +79,35 @@ from web3.contract.utils import (
73
79
  build_transaction_for_function,
74
80
  call_contract_function,
75
81
  estimate_gas_for_function,
82
+ find_events_by_identifier,
76
83
  find_functions_by_identifier,
84
+ get_event_by_identifier,
77
85
  get_function_by_identifier,
78
86
  transact_with_contract_function,
79
87
  )
80
88
  from web3.exceptions import (
89
+ ABIEventNotFound,
81
90
  ABIFunctionNotFound,
91
+ MismatchedABI,
92
+ NoABIEventsFound,
82
93
  NoABIFound,
83
94
  NoABIFunctionsFound,
95
+ Web3AttributeError,
96
+ Web3TypeError,
84
97
  Web3ValidationError,
98
+ Web3ValueError,
85
99
  )
86
100
  from web3.types import (
87
- ABI,
88
101
  BlockIdentifier,
89
102
  EventData,
90
103
  StateOverride,
91
104
  TxParams,
92
105
  )
93
- from web3.utils import (
94
- get_abi_input_names,
106
+ from web3.utils.abi import (
107
+ _filter_by_argument_count,
108
+ _get_any_abi_signature_with_name,
109
+ _mismatched_abi_error_diagnosis,
110
+ get_abi_element,
95
111
  )
96
112
 
97
113
  if TYPE_CHECKING:
@@ -103,15 +119,19 @@ class ContractEvent(BaseContractEvent):
103
119
  # mypy types
104
120
  w3: "Web3"
105
121
 
122
+ def __call__(self, *args: Any, **kwargs: Any) -> "ContractEvent":
123
+ return copy_contract_event(self, *args, **kwargs)
124
+
106
125
  @combomethod
107
126
  def get_logs(
108
127
  self,
109
128
  argument_filters: Optional[Dict[str, Any]] = None,
110
- fromBlock: Optional[BlockIdentifier] = None,
111
- toBlock: Optional[BlockIdentifier] = None,
129
+ from_block: Optional[BlockIdentifier] = None,
130
+ to_block: Optional[BlockIdentifier] = None,
112
131
  block_hash: Optional[HexBytes] = None,
113
132
  ) -> Iterable[EventData]:
114
- """Get events for this contract instance using eth_getLogs API.
133
+ """
134
+ Get events for this contract instance using eth_getLogs API.
115
135
 
116
136
  This is a stateless method, as opposed to create_filter.
117
137
  It can be safely called against nodes which do not provide
@@ -127,10 +147,10 @@ class ContractEvent(BaseContractEvent):
127
147
 
128
148
  .. code-block:: python
129
149
 
130
- from = max(mycontract.web3.eth.block_number - 10, 1)
131
- to = mycontract.web3.eth.block_number
150
+ from = max(my_contract.web3.eth.block_number - 10, 1)
151
+ to = my_contract.web3.eth.block_number
132
152
 
133
- events = mycontract.events.Transfer.get_logs(fromBlock=from, toBlock=to)
153
+ events = my_contract.events.Transfer.get_logs(from_block=from, to_block=to)
134
154
 
135
155
  for e in events:
136
156
  print(e["args"]["from"],
@@ -160,14 +180,13 @@ class ContractEvent(BaseContractEvent):
160
180
 
161
181
  :param argument_filters: Filter by argument values. Indexed arguments are
162
182
  filtered by the node while non-indexed arguments are filtered by the library.
163
- :param fromBlock: block number or "latest", defaults to "latest"
164
- :param toBlock: block number or "latest". Defaults to "latest"
183
+ :param from_block: block number or "latest", defaults to "latest"
184
+ :param to_block: block number or "latest". Defaults to "latest"
165
185
  :param block_hash: block hash. block_hash cannot be set at the
166
- same time as fromBlock or toBlock
186
+ same time as ``from_block`` or ``to_block``
167
187
  :yield: Tuple of :class:`AttributeDict` instances
168
188
  """
169
189
  event_abi = self._get_event_abi()
170
-
171
190
  # validate ``argument_filters`` if present
172
191
  if argument_filters is not None:
173
192
  event_arg_names = get_abi_input_names(event_abi)
@@ -178,7 +197,7 @@ class ContractEvent(BaseContractEvent):
178
197
  )
179
198
 
180
199
  _filter_params = self._get_event_filter_params(
181
- event_abi, argument_filters, fromBlock, toBlock, block_hash
200
+ event_abi, argument_filters, from_block, to_block, block_hash
182
201
  )
183
202
  # call JSON-RPC API
184
203
  logs = self.w3.eth.get_logs(_filter_params)
@@ -201,41 +220,45 @@ class ContractEvent(BaseContractEvent):
201
220
  self,
202
221
  *, # PEP 3102
203
222
  argument_filters: Optional[Dict[str, Any]] = None,
204
- fromBlock: Optional[BlockIdentifier] = None,
205
- toBlock: BlockIdentifier = "latest",
223
+ from_block: Optional[BlockIdentifier] = None,
224
+ to_block: BlockIdentifier = "latest",
206
225
  address: Optional[ChecksumAddress] = None,
207
226
  topics: Optional[Sequence[Any]] = None,
208
227
  ) -> LogFilter:
209
228
  """
210
229
  Create filter object that tracks logs emitted by this contract event.
211
230
  """
212
- filter_builder = EventFilterBuilder(self._get_event_abi(), self.w3.codec)
231
+ abi = self._get_event_abi()
232
+ filter_builder = EventFilterBuilder(abi, self.w3.codec)
213
233
  self._set_up_filter_builder(
214
234
  argument_filters,
215
- fromBlock,
216
- toBlock,
235
+ from_block,
236
+ to_block,
217
237
  address,
218
238
  topics,
219
239
  filter_builder,
220
240
  )
221
241
  log_filter = filter_builder.deploy(self.w3)
222
- log_filter.log_entry_formatter = get_event_data(
223
- self.w3.codec, self._get_event_abi()
224
- )
242
+ log_filter.log_entry_formatter = get_event_data(self.w3.codec, abi)
225
243
  log_filter.builder = filter_builder
226
244
 
227
245
  return log_filter
228
246
 
229
247
  @combomethod
230
248
  def build_filter(self) -> EventFilterBuilder:
249
+ abi = self._get_event_abi()
231
250
  builder = EventFilterBuilder(
232
- self._get_event_abi(),
251
+ abi,
233
252
  self.w3.codec,
234
- formatter=get_event_data(self.w3.codec, self._get_event_abi()),
253
+ formatter=get_event_data(self.w3.codec, abi),
235
254
  )
236
255
  builder.address = self.address
237
256
  return builder
238
257
 
258
+ @classmethod
259
+ def factory(cls, class_name: str, **kwargs: Any) -> Self:
260
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
261
+
239
262
 
240
263
  class ContractEvents(BaseContractEvents):
241
264
  def __init__(
@@ -243,33 +266,155 @@ class ContractEvents(BaseContractEvents):
243
266
  ) -> None:
244
267
  super().__init__(abi, w3, ContractEvent, address)
245
268
 
269
+ def __getattr__(self, event_name: str) -> "ContractEvent":
270
+ if super().__getattribute__("abi") is None:
271
+ raise NoABIFound(
272
+ "There is no ABI found for this contract.",
273
+ )
274
+ elif "_events" not in self.__dict__ or len(self._events) == 0:
275
+ raise NoABIEventsFound(
276
+ "The abi for this contract contains no event definitions. ",
277
+ "Are you sure you provided the correct contract abi?",
278
+ )
279
+ elif get_name_from_abi_element_identifier(event_name) not in [
280
+ get_name_from_abi_element_identifier(event["name"])
281
+ for event in self._events
282
+ ]:
283
+ raise ABIEventNotFound(
284
+ f"The event '{event_name}' was not found in this contract's abi. ",
285
+ "Are you sure you provided the correct contract abi?",
286
+ )
287
+
288
+ if "(" not in event_name:
289
+ event_name = _get_any_abi_signature_with_name(event_name, self._events)
290
+ else:
291
+ event_name = f"_{event_name}"
292
+
293
+ return super().__getattribute__(event_name)
294
+
295
+ def __getitem__(self, event_name: str) -> "ContractEvent":
296
+ return getattr(self, event_name)
297
+
298
+ def __iter__(self) -> Iterable["ContractEvent"]:
299
+ if not hasattr(self, "_events") or not self._events:
300
+ return
301
+
302
+ for event in self._events:
303
+ yield self[abi_to_signature(event)]
304
+
246
305
 
247
306
  class ContractFunction(BaseContractFunction):
248
307
  # mypy types
249
308
  w3: "Web3"
250
309
 
251
310
  def __call__(self, *args: Any, **kwargs: Any) -> "ContractFunction":
252
- clone = copy.copy(self)
253
- if args is None:
254
- clone.args = tuple()
255
- else:
256
- 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
+ )
257
343
 
258
- if kwargs is None:
259
- 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 = ContractFunction.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
+ )
260
389
  else:
261
- clone.kwargs = kwargs
262
- clone._set_function_info()
263
- 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)
264
409
 
265
410
  @classmethod
266
411
  def factory(cls, class_name: str, **kwargs: Any) -> Self:
267
- return PropertyCheckingFactory(class_name, (cls,), kwargs)(kwargs.get("abi"))
412
+ return PropertyCheckingFactory(class_name, (cls,), kwargs)()
268
413
 
269
414
  def call(
270
415
  self,
271
416
  transaction: Optional[TxParams] = None,
272
- block_identifier: BlockIdentifier = None,
417
+ block_identifier: Optional[BlockIdentifier] = None,
273
418
  state_override: Optional[StateOverride] = None,
274
419
  ccip_read_enabled: Optional[bool] = None,
275
420
  ) -> Any:
@@ -294,9 +439,9 @@ class ContractFunction(BaseContractFunction):
294
439
  addr = contract.functions.owner().call()
295
440
 
296
441
  :param transaction: Dictionary of transaction info for web3 interface
297
- :param block_identifier: TODO
298
- :param state_override TODO
299
- :param ccip_read_enabled TODO
442
+ :param block_identifier: Block number or string "latest", "pending", "earliest"
443
+ :param state_override: Dictionary of state override values
444
+ :param ccip_read_enabled: Enable CCIP read operations for the call
300
445
  :return: ``Caller`` object that has contract public functions
301
446
  and variables exposed as Python methods
302
447
  """
@@ -304,11 +449,13 @@ class ContractFunction(BaseContractFunction):
304
449
 
305
450
  block_id = parse_block_identifier(self.w3, block_identifier)
306
451
 
452
+ abi_element_identifier = abi_to_signature(self.abi)
453
+
307
454
  return call_contract_function(
308
455
  self.w3,
309
456
  self.address,
310
457
  self._return_data_normalizers,
311
- self.function_identifier,
458
+ abi_element_identifier,
312
459
  call_transaction,
313
460
  block_id,
314
461
  self.contract_abi,
@@ -316,21 +463,23 @@ class ContractFunction(BaseContractFunction):
316
463
  state_override,
317
464
  ccip_read_enabled,
318
465
  self.decode_tuples,
319
- *self.args,
320
- **self.kwargs,
466
+ *self.args or (),
467
+ **self.kwargs or {},
321
468
  )
322
469
 
323
470
  def transact(self, transaction: Optional[TxParams] = None) -> HexBytes:
324
471
  setup_transaction = self._transact(transaction)
472
+ abi_element_identifier = abi_to_signature(self.abi)
473
+
325
474
  return transact_with_contract_function(
326
475
  self.address,
327
476
  self.w3,
328
- self.function_identifier,
477
+ abi_element_identifier,
329
478
  setup_transaction,
330
479
  self.contract_abi,
331
480
  self.abi,
332
- *self.args,
333
- **self.kwargs,
481
+ *self.args or (),
482
+ **self.kwargs or {},
334
483
  )
335
484
 
336
485
  def estimate_gas(
@@ -340,30 +489,33 @@ class ContractFunction(BaseContractFunction):
340
489
  state_override: Optional[StateOverride] = None,
341
490
  ) -> int:
342
491
  setup_transaction = self._estimate_gas(transaction)
492
+ abi_element_identifier = abi_to_signature(self.abi)
343
493
  return estimate_gas_for_function(
344
494
  self.address,
345
495
  self.w3,
346
- self.function_identifier,
496
+ abi_element_identifier,
347
497
  setup_transaction,
348
498
  self.contract_abi,
349
499
  self.abi,
350
500
  block_identifier,
351
501
  state_override,
352
- *self.args,
353
- **self.kwargs,
502
+ *self.args or (),
503
+ **self.kwargs or {},
354
504
  )
355
505
 
356
506
  def build_transaction(self, transaction: Optional[TxParams] = None) -> TxParams:
357
507
  built_transaction = self._build_transaction(transaction)
508
+ abi_element_identifier = abi_to_signature(self.abi)
509
+
358
510
  return build_transaction_for_function(
359
511
  self.address,
360
512
  self.w3,
361
- self.function_identifier,
513
+ abi_element_identifier,
362
514
  built_transaction,
363
515
  self.contract_abi,
364
516
  self.abi,
365
- *self.args,
366
- **self.kwargs,
517
+ *self.args or (),
518
+ **self.kwargs or {},
367
519
  )
368
520
 
369
521
  @staticmethod
@@ -373,12 +525,14 @@ class ContractFunction(BaseContractFunction):
373
525
  address: Optional[ChecksumAddress] = None,
374
526
  ) -> "ContractFunction":
375
527
  if abi and fallback_func_abi_exists(abi):
528
+ fallback_abi = filter_abi_by_type("fallback", abi)[0]
376
529
  return ContractFunction.factory(
377
530
  "fallback",
378
531
  w3=w3,
379
532
  contract_abi=abi,
380
533
  address=address,
381
- function_identifier=FallbackFn,
534
+ abi_element_identifier=FallbackFn,
535
+ abi=fallback_abi,
382
536
  )()
383
537
  return cast(ContractFunction, NonExistentFallbackFunction())
384
538
 
@@ -389,12 +543,14 @@ class ContractFunction(BaseContractFunction):
389
543
  address: Optional[ChecksumAddress] = None,
390
544
  ) -> "ContractFunction":
391
545
  if abi and receive_func_abi_exists(abi):
546
+ receive_abi = filter_abi_by_type("receive", abi)[0]
392
547
  return ContractFunction.factory(
393
548
  "receive",
394
549
  w3=w3,
395
550
  contract_abi=abi,
396
551
  address=address,
397
- function_identifier=ReceiveFn,
552
+ abi_element_identifier=ReceiveFn,
553
+ abi=receive_abi,
398
554
  )()
399
555
  return cast(ContractFunction, NonExistentReceiveFunction())
400
556
 
@@ -409,23 +565,45 @@ class ContractFunctions(BaseContractFunctions):
409
565
  ) -> None:
410
566
  super().__init__(abi, w3, ContractFunction, address, decode_tuples)
411
567
 
568
+ def __iter__(self) -> Iterable["ContractFunction"]:
569
+ if not hasattr(self, "_functions") or not self._functions:
570
+ return
571
+
572
+ for func in self._functions:
573
+ yield self[abi_to_signature(func)]
574
+
412
575
  def __getattr__(self, function_name: str) -> "ContractFunction":
413
- if self.abi is None:
576
+ if super().__getattribute__("abi") is None:
414
577
  raise NoABIFound(
415
578
  "There is no ABI found for this contract.",
416
579
  )
417
- if "_functions" not in self.__dict__:
580
+ elif "_functions" not in self.__dict__ or len(self._functions) == 0:
418
581
  raise NoABIFunctionsFound(
419
582
  "The abi for this contract contains no function definitions. ",
420
583
  "Are you sure you provided the correct contract abi?",
421
584
  )
422
- elif function_name not in self.__dict__["_functions"]:
585
+ elif get_name_from_abi_element_identifier(function_name) not in [
586
+ get_name_from_abi_element_identifier(function["name"])
587
+ for function in self._functions
588
+ ]:
423
589
  raise ABIFunctionNotFound(
424
- f"The function '{function_name}' was not found in this contract's abi.",
425
- " Are you sure you provided the correct contract abi?",
590
+ f"The function '{function_name}' was not found in this ",
591
+ "contract's abi.",
592
+ )
593
+
594
+ if "(" not in function_name:
595
+ function_name = _get_any_abi_signature_with_name(
596
+ function_name, self._functions
426
597
  )
427
598
  else:
428
- return super().__getattribute__(function_name)
599
+ function_name = f"_{function_name}"
600
+
601
+ return super().__getattribute__(
602
+ function_name,
603
+ )
604
+
605
+ def __getitem__(self, function_name: str) -> "ContractFunction":
606
+ return getattr(self, function_name)
429
607
 
430
608
 
431
609
  class Contract(BaseContract):
@@ -438,20 +616,23 @@ class Contract(BaseContract):
438
616
  events: ContractEvents = None
439
617
 
440
618
  def __init__(self, address: Optional[ChecksumAddress] = None) -> None:
441
- """Create a new smart contract proxy object.
442
- :param address: Contract address as 0x hex string"""
619
+ """
620
+ Create a new smart contract proxy object.
621
+ :param address: Contract address as 0x hex string
622
+ """
443
623
  _w3 = self.w3
444
624
  if _w3 is None:
445
- raise AttributeError(
625
+ raise Web3AttributeError(
446
626
  "The `Contract` class has not been initialized. Please use the "
447
627
  "`web3.contract` interface to create your contract class."
448
628
  )
449
629
 
450
630
  if address:
451
- self.address = normalize_address(cast("ENS", _w3.ens), address)
631
+ # invoke ``w3._ens`` over ``w3.ens`` to avoid premature instantiation
632
+ self.address = normalize_address(cast("ENS", _w3._ens), address)
452
633
 
453
634
  if not self.address:
454
- raise TypeError(
635
+ raise Web3TypeError(
455
636
  "The address argument is required to instantiate a contract."
456
637
  )
457
638
 
@@ -459,7 +640,11 @@ class Contract(BaseContract):
459
640
  self.abi, _w3, self.address, decode_tuples=self.decode_tuples
460
641
  )
461
642
  self.caller = ContractCaller(
462
- self.abi, _w3, self.address, decode_tuples=self.decode_tuples
643
+ self.abi,
644
+ _w3,
645
+ self.address,
646
+ decode_tuples=self.decode_tuples,
647
+ contract_functions=self.functions,
463
648
  )
464
649
  self.events = ContractEvents(self.abi, _w3, self.address)
465
650
  self.fallback = Contract.get_fallback_function(
@@ -483,7 +668,8 @@ class Contract(BaseContract):
483
668
 
484
669
  normalizers = {
485
670
  "abi": normalize_abi,
486
- "address": partial(normalize_address, w3.ens),
671
+ # invoke ``w3._ens`` over ``w3.ens`` to avoid premature instantiation
672
+ "address": partial(normalize_address, w3._ens),
487
673
  "bytecode": normalize_bytecode,
488
674
  "bytecode_runtime": normalize_bytecode,
489
675
  }
@@ -497,6 +683,16 @@ class Contract(BaseContract):
497
683
  normalizers=normalizers,
498
684
  ),
499
685
  )
686
+
687
+ if contract.abi:
688
+ for abi in contract.abi:
689
+ abi_name = abi.get("name")
690
+ if abi_name in ["abi", "address"]:
691
+ raise Web3AttributeError(
692
+ f"Contract contains a reserved word `{abi_name}` "
693
+ f"and could not be instantiated."
694
+ )
695
+
500
696
  contract.functions = ContractFunctions(
501
697
  contract.abi, contract.w3, decode_tuples=contract.decode_tuples
502
698
  )
@@ -505,6 +701,7 @@ class Contract(BaseContract):
505
701
  contract.w3,
506
702
  contract.address,
507
703
  decode_tuples=contract.decode_tuples,
704
+ contract_functions=contract.functions,
508
705
  )
509
706
  contract.events = ContractEvents(contract.abi, contract.w3)
510
707
  contract.fallback = Contract.get_fallback_function(
@@ -528,7 +725,7 @@ class Contract(BaseContract):
528
725
  :return: a contract constructor object
529
726
  """
530
727
  if cls.bytecode is None:
531
- raise ValueError(
728
+ raise Web3ValueError(
532
729
  "Cannot call constructor on a contract that does not have "
533
730
  "'bytecode' associated with it"
534
731
  )
@@ -556,6 +753,24 @@ class Contract(BaseContract):
556
753
  ) -> "ContractFunction":
557
754
  return get_function_by_identifier(fns, identifier)
558
755
 
756
+ @combomethod
757
+ def find_events_by_identifier(
758
+ cls,
759
+ contract_abi: ABI,
760
+ w3: "Web3",
761
+ address: ChecksumAddress,
762
+ callable_check: Callable[..., Any],
763
+ ) -> List["ContractEvent"]:
764
+ return find_events_by_identifier(
765
+ contract_abi, w3, address, callable_check, ContractEvent
766
+ )
767
+
768
+ @combomethod
769
+ def get_event_by_identifier(
770
+ self, events: Sequence["ContractEvent"], identifier: str
771
+ ) -> "ContractEvent":
772
+ return get_event_by_identifier(events, identifier)
773
+
559
774
 
560
775
  class ContractCaller(BaseContractCaller):
561
776
  # mypy types
@@ -570,6 +785,7 @@ class ContractCaller(BaseContractCaller):
570
785
  block_identifier: BlockIdentifier = None,
571
786
  ccip_read_enabled: Optional[bool] = None,
572
787
  decode_tuples: Optional[bool] = False,
788
+ contract_functions: Optional[ContractFunctions] = None,
573
789
  ) -> None:
574
790
  super().__init__(abi, w3, address, decode_tuples=decode_tuples)
575
791
 
@@ -577,17 +793,13 @@ class ContractCaller(BaseContractCaller):
577
793
  if transaction is None:
578
794
  transaction = {}
579
795
 
580
- self._functions = filter_by_type("function", self.abi)
581
- for func in self._functions:
582
- fn = ContractFunction.factory(
583
- func["name"],
584
- w3=w3,
585
- contract_abi=self.abi,
586
- address=self.address,
587
- function_identifier=func["name"],
588
- decode_tuples=decode_tuples,
796
+ if contract_functions is None:
797
+ contract_functions = ContractFunctions(
798
+ abi, w3, address=address, decode_tuples=decode_tuples
589
799
  )
590
800
 
801
+ self._functions = contract_functions._functions
802
+ for fn in contract_functions.__iter__():
591
803
  caller_method = partial(
592
804
  self.call_function,
593
805
  fn,
@@ -595,8 +807,7 @@ class ContractCaller(BaseContractCaller):
595
807
  block_identifier=block_identifier,
596
808
  ccip_read_enabled=ccip_read_enabled,
597
809
  )
598
-
599
- setattr(self, func["name"], caller_method)
810
+ setattr(self, str(fn.abi_element_identifier), caller_method)
600
811
 
601
812
  def __call__(
602
813
  self,