web3 7.6.1__py3-none-any.whl → 7.8.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. ens/async_ens.py +1 -1
  2. ens/ens.py +1 -1
  3. web3/_utils/caching/caching_utils.py +64 -0
  4. web3/_utils/caching/request_caching_validation.py +1 -0
  5. web3/_utils/events.py +1 -1
  6. web3/_utils/http_session_manager.py +32 -3
  7. web3/_utils/module_testing/eth_module.py +5 -18
  8. web3/_utils/module_testing/module_testing_utils.py +1 -43
  9. web3/_utils/module_testing/persistent_connection_provider.py +696 -207
  10. web3/_utils/module_testing/utils.py +99 -33
  11. web3/beacon/api_endpoints.py +10 -0
  12. web3/beacon/async_beacon.py +47 -0
  13. web3/beacon/beacon.py +45 -0
  14. web3/contract/async_contract.py +2 -206
  15. web3/contract/base_contract.py +217 -13
  16. web3/contract/contract.py +2 -205
  17. web3/datastructures.py +15 -16
  18. web3/eth/async_eth.py +23 -5
  19. web3/exceptions.py +7 -0
  20. web3/main.py +24 -3
  21. web3/manager.py +140 -48
  22. web3/method.py +1 -1
  23. web3/middleware/attrdict.py +12 -22
  24. web3/middleware/base.py +14 -6
  25. web3/module.py +17 -21
  26. web3/providers/async_base.py +23 -14
  27. web3/providers/base.py +6 -8
  28. web3/providers/ipc.py +7 -6
  29. web3/providers/legacy_websocket.py +1 -1
  30. web3/providers/persistent/async_ipc.py +5 -3
  31. web3/providers/persistent/persistent.py +121 -17
  32. web3/providers/persistent/persistent_connection.py +11 -4
  33. web3/providers/persistent/request_processor.py +49 -41
  34. web3/providers/persistent/subscription_container.py +56 -0
  35. web3/providers/persistent/subscription_manager.py +298 -0
  36. web3/providers/persistent/websocket.py +4 -4
  37. web3/providers/rpc/async_rpc.py +16 -3
  38. web3/providers/rpc/rpc.py +9 -5
  39. web3/types.py +28 -14
  40. web3/utils/__init__.py +4 -0
  41. web3/utils/subscriptions.py +289 -0
  42. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/LICENSE +1 -1
  43. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/METADATA +68 -56
  44. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/RECORD +46 -43
  45. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/WHEEL +1 -1
  46. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/top_level.txt +0 -0
@@ -10,10 +10,14 @@ from typing import (
10
10
  cast,
11
11
  )
12
12
 
13
- from toolz import (
13
+ from eth_utils.toolz import (
14
14
  merge,
15
15
  )
16
16
 
17
+ from web3.providers.persistent import (
18
+ PersistentConnectionProvider,
19
+ )
20
+
17
21
  if TYPE_CHECKING:
18
22
  from web3 import ( # noqa: F401
19
23
  AsyncWeb3,
@@ -26,6 +30,7 @@ if TYPE_CHECKING:
26
30
  AsyncMakeRequestFn,
27
31
  MakeRequestFn,
28
32
  RPCEndpoint,
33
+ RPCRequest,
29
34
  RPCResponse,
30
35
  )
31
36
 
@@ -101,9 +106,21 @@ class RequestMocker:
101
106
  self.mock_results = mock_results or {}
102
107
  self.mock_errors = mock_errors or {}
103
108
  self.mock_responses = mock_responses or {}
104
- self._make_request: Union[
105
- "AsyncMakeRequestFn", "MakeRequestFn"
106
- ] = w3.provider.make_request
109
+ if isinstance(w3.provider, PersistentConnectionProvider):
110
+ self._send_request = w3.provider.send_request
111
+ self._recv_for_request = w3.provider.recv_for_request
112
+ else:
113
+ self._make_request: Union[
114
+ "AsyncMakeRequestFn", "MakeRequestFn"
115
+ ] = w3.provider.make_request
116
+
117
+ def _build_request_id(self) -> int:
118
+ request_id = (
119
+ next(copy.deepcopy(self.w3.provider.request_counter))
120
+ if hasattr(self.w3.provider, "request_counter")
121
+ else 1
122
+ )
123
+ return request_id
107
124
 
108
125
  def __enter__(self) -> "Self":
109
126
  # mypy error: Cannot assign to a method
@@ -131,11 +148,7 @@ class RequestMocker:
131
148
  ):
132
149
  return self._make_request(method, params)
133
150
 
134
- request_id = (
135
- next(copy.deepcopy(self.w3.provider.request_counter))
136
- if hasattr(self.w3.provider, "request_counter")
137
- else 1
138
- )
151
+ request_id = self._build_request_id()
139
152
  response_dict = {"jsonrpc": "2.0", "id": request_id}
140
153
 
141
154
  if method in self.mock_responses:
@@ -176,35 +189,34 @@ class RequestMocker:
176
189
  # -- async -- #
177
190
 
178
191
  async def __aenter__(self) -> "Self":
179
- # mypy error: Cannot assign to a method
180
- self.w3.provider.make_request = self._async_mock_request_handler # type: ignore[method-assign] # noqa: E501
181
- # reset request func cache to re-build request_func with mocked make_request
182
- self.w3.provider._request_func_cache = (None, None)
192
+ if not isinstance(self.w3.provider, PersistentConnectionProvider):
193
+ # mypy error: Cannot assign to a method
194
+ self.w3.provider.make_request = self._async_mock_request_handler # type: ignore[method-assign] # noqa: E501
195
+ # reset request func cache to re-build request_func w/ mocked make_request
196
+ self.w3.provider._request_func_cache = (None, None)
197
+ else:
198
+ self.w3.provider.send_request = self._async_mock_send_handler # type: ignore[method-assign] # noqa: E501
199
+ self.w3.provider.recv_for_request = self._async_mock_recv_handler # type: ignore[method-assign] # noqa: E501
200
+ self.w3.provider._send_func_cache = (None, None)
201
+ self.w3.provider._recv_func_cache = (None, None)
183
202
  return self
184
203
 
185
204
  async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
186
- # mypy error: Cannot assign to a method
187
- self.w3.provider.make_request = self._make_request # type: ignore[assignment]
188
- # reset request func cache to re-build request_func with original make_request
189
- self.w3.provider._request_func_cache = (None, None)
205
+ if not isinstance(self.w3.provider, PersistentConnectionProvider):
206
+ # mypy error: Cannot assign to a method
207
+ self.w3.provider.make_request = self._make_request # type: ignore[assignment] # noqa: E501
208
+ # reset request func cache to re-build request_func w/ original make_request
209
+ self.w3.provider._request_func_cache = (None, None)
210
+ else:
211
+ self.w3.provider.send_request = self._send_request # type: ignore[method-assign] # noqa: E501
212
+ self.w3.provider.recv_for_request = self._recv_for_request # type: ignore[method-assign] # noqa: E501
213
+ self.w3.provider._send_func_cache = (None, None)
214
+ self.w3.provider._recv_func_cache = (None, None)
190
215
 
191
- async def _async_mock_request_handler(
192
- self, method: "RPCEndpoint", params: Any
216
+ async def _async_build_mock_result(
217
+ self, method: "RPCEndpoint", params: Any, request_id: int = None
193
218
  ) -> "RPCResponse":
194
- self.w3 = cast("AsyncWeb3", self.w3)
195
- self._make_request = cast("AsyncMakeRequestFn", self._make_request)
196
-
197
- if all(
198
- method not in mock_dict
199
- for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses)
200
- ):
201
- return await self._make_request(method, params)
202
-
203
- request_id = (
204
- next(copy.deepcopy(self.w3.provider.request_counter))
205
- if hasattr(self.w3.provider, "request_counter")
206
- else 1
207
- )
219
+ request_id = request_id if request_id else self._build_request_id()
208
220
  response_dict = {"jsonrpc": "2.0", "id": request_id}
209
221
 
210
222
  if method in self.mock_responses:
@@ -244,6 +256,19 @@ class RequestMocker:
244
256
  else:
245
257
  raise Exception("Invariant: unreachable code path")
246
258
 
259
+ return mocked_result
260
+
261
+ async def _async_mock_request_handler(
262
+ self, method: "RPCEndpoint", params: Any
263
+ ) -> "RPCResponse":
264
+ self.w3 = cast("AsyncWeb3", self.w3)
265
+ self._make_request = cast("AsyncMakeRequestFn", self._make_request)
266
+ if all(
267
+ method not in mock_dict
268
+ for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses)
269
+ ):
270
+ return await self._make_request(method, params)
271
+ mocked_result = await self._async_build_mock_result(method, params)
247
272
  decorator = getattr(self._make_request, "_decorator", None)
248
273
  if decorator is not None:
249
274
  # If the original make_request was decorated, we need to re-apply
@@ -259,6 +284,47 @@ class RequestMocker:
259
284
  else:
260
285
  return mocked_result
261
286
 
287
+ async def _async_mock_send_handler(
288
+ self, method: "RPCEndpoint", params: Any
289
+ ) -> "RPCRequest":
290
+ if all(
291
+ method not in mock_dict
292
+ for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses)
293
+ ):
294
+ return await self._send_request(method, params)
295
+ else:
296
+ request_id = self._build_request_id()
297
+ return {"id": request_id, "method": method, "params": params}
298
+
299
+ async def _async_mock_recv_handler(
300
+ self, rpc_request: "RPCRequest"
301
+ ) -> "RPCResponse":
302
+ self.w3 = cast("AsyncWeb3", self.w3)
303
+ method = rpc_request["method"]
304
+ request_id = rpc_request["id"]
305
+ if all(
306
+ method not in mock_dict
307
+ for mock_dict in (self.mock_errors, self.mock_results, self.mock_responses)
308
+ ):
309
+ return await self._recv_for_request(request_id)
310
+ mocked_result = await self._async_build_mock_result(
311
+ method, rpc_request["params"], request_id=int(request_id)
312
+ )
313
+ decorator = getattr(self._recv_for_request, "_decorator", None)
314
+ if decorator is not None:
315
+ # If the original recv_for_request was decorated, we need to re-apply
316
+ # the decorator to the mocked recv_for_request. This is necessary for
317
+ # the request caching decorator to work properly.
318
+
319
+ async def _coro(
320
+ _provider: Any, _rpc_request: "RPCRequest"
321
+ ) -> "RPCResponse":
322
+ return mocked_result
323
+
324
+ return await decorator(_coro)(self.w3.provider, rpc_request)
325
+ else:
326
+ return mocked_result
327
+
262
328
  @staticmethod
263
329
  def _create_error_object(error: Dict[str, Any]) -> Dict[str, Any]:
264
330
  code = error.get("code", -32000)
@@ -59,6 +59,16 @@ GET_BEACON_HEADS = "/eth/v1/debug/beacon/heads"
59
59
  GET_NODE_IDENTITY = "/eth/v1/node/identity"
60
60
  GET_PEERS = "/eth/v1/node/peers"
61
61
  GET_PEER = "/eth/v1/node/peers/{0}"
62
+ GET_PEER_COUNT = "/eth/v1/node/peer_count"
62
63
  GET_HEALTH = "/eth/v1/node/health"
63
64
  GET_VERSION = "/eth/v1/node/version"
64
65
  GET_SYNCING = "/eth/v1/node/syncing"
66
+
67
+ # [ VALIDATOR endpoints ]
68
+
69
+ GET_ATTESTER_DUTIES = "/eth/v1/validator/duties/attester/{0}"
70
+ GET_BLOCK_PROPOSERS_DUTIES = "/eth/v1/validator/duties/proposer/{0}"
71
+ GET_SYNC_COMMITTEE_DUTIES = "/eth/v1/validator/duties/sync/{0}"
72
+
73
+ # [ REWARDS endpoints ]
74
+ GET_ATTESTATIONS_REWARDS = "/eth/v1/beacon/rewards/attestations/{0}"
@@ -3,6 +3,7 @@ from typing import (
3
3
  Dict,
4
4
  List,
5
5
  Optional,
6
+ Union,
6
7
  )
7
8
 
8
9
  from aiohttp import (
@@ -18,6 +19,8 @@ from web3._utils.http_session_manager import (
18
19
  )
19
20
  from web3.beacon.api_endpoints import (
20
21
  GET_ATTESTATIONS,
22
+ GET_ATTESTATIONS_REWARDS,
23
+ GET_ATTESTER_DUTIES,
21
24
  GET_ATTESTER_SLASHINGS,
22
25
  GET_BEACON_HEADS,
23
26
  GET_BEACON_STATE,
@@ -27,6 +30,7 @@ from web3.beacon.api_endpoints import (
27
30
  GET_BLOCK_ATTESTATIONS,
28
31
  GET_BLOCK_HEADER,
29
32
  GET_BLOCK_HEADERS,
33
+ GET_BLOCK_PROPOSERS_DUTIES,
30
34
  GET_BLOCK_ROOT,
31
35
  GET_BLS_TO_EXECUTION_CHANGES,
32
36
  GET_DEPOSIT_CONTRACT,
@@ -45,10 +49,12 @@ from web3.beacon.api_endpoints import (
45
49
  GET_LIGHT_CLIENT_UPDATES,
46
50
  GET_NODE_IDENTITY,
47
51
  GET_PEER,
52
+ GET_PEER_COUNT,
48
53
  GET_PEERS,
49
54
  GET_PROPOSER_SLASHINGS,
50
55
  GET_REWARDS,
51
56
  GET_SPEC,
57
+ GET_SYNC_COMMITTEE_DUTIES,
52
58
  GET_SYNCING,
53
59
  GET_VALIDATOR,
54
60
  GET_VALIDATOR_BALANCES,
@@ -78,6 +84,14 @@ class AsyncBeacon:
78
84
  uri, params=params, timeout=ClientTimeout(self.request_timeout)
79
85
  )
80
86
 
87
+ async def _async_make_post_request(
88
+ self, endpoint_uri: str, body: Union[List[str], Dict[str, Any]]
89
+ ) -> Dict[str, Any]:
90
+ uri = URI(self.base_url + endpoint_uri)
91
+ return await self._request_session_manager.async_json_make_post_request(
92
+ uri, json=body, timeout=self.request_timeout
93
+ )
94
+
81
95
  # [ BEACON endpoints ]
82
96
 
83
97
  # states
@@ -216,6 +230,9 @@ class AsyncBeacon:
216
230
  async def get_peer(self, peer_id: str) -> Dict[str, Any]:
217
231
  return await self._async_make_get_request(GET_PEER.format(peer_id))
218
232
 
233
+ async def get_peer_count(self) -> Dict[str, Any]:
234
+ return await self._async_make_get_request(GET_PEER_COUNT)
235
+
219
236
  async def get_health(self) -> int:
220
237
  url = URI(self.base_url + GET_HEALTH)
221
238
  response = (
@@ -239,3 +256,33 @@ class AsyncBeacon:
239
256
  GET_BLOB_SIDECARS.format(block_id),
240
257
  params=indices_param,
241
258
  )
259
+
260
+ # [ VALIDATOR endpoints ]
261
+
262
+ async def get_attester_duties(
263
+ self, epoch: str, validator_indices: List[str]
264
+ ) -> Dict[str, Any]:
265
+ return await self._async_make_post_request(
266
+ GET_ATTESTER_DUTIES.format(epoch), validator_indices
267
+ )
268
+
269
+ async def get_block_proposer_duties(self, epoch: str) -> Dict[str, Any]:
270
+ return await self._async_make_get_request(
271
+ GET_BLOCK_PROPOSERS_DUTIES.format(epoch)
272
+ )
273
+
274
+ async def get_sync_committee_duties(
275
+ self, epoch: str, validator_indices: List[str]
276
+ ) -> Dict[str, Any]:
277
+ return await self._async_make_post_request(
278
+ GET_SYNC_COMMITTEE_DUTIES.format(epoch), validator_indices
279
+ )
280
+
281
+ # [ REWARDS endpoints ]
282
+
283
+ async def get_attestations_rewards(
284
+ self, epoch: str, validator_indices: List[str]
285
+ ) -> Dict[str, Any]:
286
+ return await self._async_make_post_request(
287
+ GET_ATTESTATIONS_REWARDS.format(epoch), validator_indices
288
+ )
web3/beacon/beacon.py CHANGED
@@ -3,6 +3,7 @@ from typing import (
3
3
  Dict,
4
4
  List,
5
5
  Optional,
6
+ Union,
6
7
  )
7
8
 
8
9
  from eth_typing import (
@@ -15,6 +16,8 @@ from web3._utils.http_session_manager import (
15
16
  )
16
17
  from web3.beacon.api_endpoints import (
17
18
  GET_ATTESTATIONS,
19
+ GET_ATTESTATIONS_REWARDS,
20
+ GET_ATTESTER_DUTIES,
18
21
  GET_ATTESTER_SLASHINGS,
19
22
  GET_BEACON_HEADS,
20
23
  GET_BEACON_STATE,
@@ -24,6 +27,7 @@ from web3.beacon.api_endpoints import (
24
27
  GET_BLOCK_ATTESTATIONS,
25
28
  GET_BLOCK_HEADER,
26
29
  GET_BLOCK_HEADERS,
30
+ GET_BLOCK_PROPOSERS_DUTIES,
27
31
  GET_BLOCK_ROOT,
28
32
  GET_BLS_TO_EXECUTION_CHANGES,
29
33
  GET_DEPOSIT_CONTRACT,
@@ -42,10 +46,12 @@ from web3.beacon.api_endpoints import (
42
46
  GET_LIGHT_CLIENT_UPDATES,
43
47
  GET_NODE_IDENTITY,
44
48
  GET_PEER,
49
+ GET_PEER_COUNT,
45
50
  GET_PEERS,
46
51
  GET_PROPOSER_SLASHINGS,
47
52
  GET_REWARDS,
48
53
  GET_SPEC,
54
+ GET_SYNC_COMMITTEE_DUTIES,
49
55
  GET_SYNCING,
50
56
  GET_VALIDATOR,
51
57
  GET_VALIDATOR_BALANCES,
@@ -73,6 +79,14 @@ class Beacon:
73
79
  uri, params=params, timeout=self.request_timeout
74
80
  )
75
81
 
82
+ def _make_post_request(
83
+ self, endpoint_url: str, body: Union[List[str], Dict[str, Any]]
84
+ ) -> Dict[str, Any]:
85
+ uri = URI(self.base_url + endpoint_url)
86
+ return self._request_session_manager.json_make_post_request(
87
+ uri, json=body, timeout=self.request_timeout
88
+ )
89
+
76
90
  # [ BEACON endpoints ]
77
91
 
78
92
  # states
@@ -201,6 +215,9 @@ class Beacon:
201
215
  def get_peer(self, peer_id: str) -> Dict[str, Any]:
202
216
  return self._make_get_request(GET_PEER.format(peer_id))
203
217
 
218
+ def get_peer_count(self) -> Dict[str, Any]:
219
+ return self._make_get_request(GET_PEER_COUNT)
220
+
204
221
  def get_health(self) -> int:
205
222
  url = URI(self.base_url + GET_HEALTH)
206
223
  response = self._request_session_manager.get_response_from_get_request(url)
@@ -222,3 +239,31 @@ class Beacon:
222
239
  GET_BLOB_SIDECARS.format(block_id),
223
240
  params=indices_param,
224
241
  )
242
+
243
+ # [ VALIDATOR endpoints ]
244
+
245
+ def get_attester_duties(
246
+ self, epoch: str, validator_indices: List[str]
247
+ ) -> Dict[str, Any]:
248
+ return self._make_post_request(
249
+ GET_ATTESTER_DUTIES.format(epoch), validator_indices
250
+ )
251
+
252
+ def get_block_proposer_duties(self, epoch: str) -> Dict[str, Any]:
253
+ return self._make_get_request(GET_BLOCK_PROPOSERS_DUTIES.format(epoch))
254
+
255
+ def get_sync_committee_duties(
256
+ self, epoch: str, validator_indices: List[str]
257
+ ) -> Dict[str, Any]:
258
+ return self._make_post_request(
259
+ GET_SYNC_COMMITTEE_DUTIES.format(epoch), validator_indices
260
+ )
261
+
262
+ # [ REWARDS endpoints ]
263
+
264
+ def get_attestations_rewards(
265
+ self, epoch: str, validator_indices: List[str]
266
+ ) -> Dict[str, Any]:
267
+ return self._make_post_request(
268
+ GET_ATTESTATIONS_REWARDS.format(epoch), validator_indices
269
+ )
@@ -14,7 +14,6 @@ from typing import (
14
14
 
15
15
  from eth_typing import (
16
16
  ABI,
17
- ABIFunction,
18
17
  ChecksumAddress,
19
18
  )
20
19
  from eth_utils import (
@@ -22,7 +21,6 @@ from eth_utils import (
22
21
  )
23
22
  from eth_utils.abi import (
24
23
  abi_to_signature,
25
- filter_abi_by_type,
26
24
  get_abi_input_names,
27
25
  )
28
26
  from eth_utils.toolz import (
@@ -34,7 +32,6 @@ from hexbytes import (
34
32
 
35
33
  from web3._utils.abi import (
36
34
  fallback_func_abi_exists,
37
- get_name_from_abi_element_identifier,
38
35
  receive_func_abi_exists,
39
36
  )
40
37
  from web3._utils.abi_element_identifiers import (
@@ -49,8 +46,6 @@ from web3._utils.compat import (
49
46
  )
50
47
  from web3._utils.contracts import (
51
48
  async_parse_block_identifier,
52
- copy_contract_event,
53
- copy_contract_function,
54
49
  )
55
50
  from web3._utils.datatypes import (
56
51
  PropertyCheckingFactory,
@@ -89,12 +84,6 @@ from web3.contract.utils import (
89
84
  get_function_by_identifier,
90
85
  )
91
86
  from web3.exceptions import (
92
- ABIEventNotFound,
93
- ABIFunctionNotFound,
94
- MismatchedABI,
95
- NoABIEventsFound,
96
- NoABIFound,
97
- NoABIFunctionsFound,
98
87
  Web3AttributeError,
99
88
  Web3TypeError,
100
89
  Web3ValidationError,
@@ -106,12 +95,6 @@ from web3.types import (
106
95
  StateOverride,
107
96
  TxParams,
108
97
  )
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,
114
- )
115
98
 
116
99
  if TYPE_CHECKING:
117
100
  from ens import AsyncENS # noqa: F401
@@ -122,9 +105,6 @@ class AsyncContractEvent(BaseContractEvent):
122
105
  # mypy types
123
106
  w3: "AsyncWeb3"
124
107
 
125
- def __call__(self, *args: Any, **kwargs: Any) -> "AsyncContractEvent":
126
- return copy_contract_event(self, *args, **kwargs)
127
-
128
108
  @combomethod
129
109
  async def get_logs(
130
110
  self,
@@ -255,162 +235,18 @@ class AsyncContractEvent(BaseContractEvent):
255
235
  builder.address = self.address
256
236
  return builder
257
237
 
258
- @classmethod
259
- def factory(cls, class_name: str, **kwargs: Any) -> Self:
260
- return PropertyCheckingFactory(class_name, (cls,), kwargs)()
261
238
 
262
-
263
- class AsyncContractEvents(BaseContractEvents):
239
+ class AsyncContractEvents(BaseContractEvents[AsyncContractEvent]):
264
240
  def __init__(
265
241
  self, abi: ABI, w3: "AsyncWeb3", address: Optional[ChecksumAddress] = None
266
242
  ) -> None:
267
243
  super().__init__(abi, w3, AsyncContractEvent, address)
268
244
 
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
-
305
245
 
306
246
  class AsyncContractFunction(BaseContractFunction):
307
247
  # mypy types
308
248
  w3: "AsyncWeb3"
309
249
 
310
- def __call__(self, *args: Any, **kwargs: Any) -> "AsyncContractFunction":
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
- )
343
-
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
- )
389
- else:
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)
409
-
410
- @classmethod
411
- def factory(cls, class_name: str, **kwargs: Any) -> Self:
412
- return PropertyCheckingFactory(class_name, (cls,), kwargs)()
413
-
414
250
  async def call(
415
251
  self,
416
252
  transaction: Optional[TxParams] = None,
@@ -551,7 +387,7 @@ class AsyncContractFunction(BaseContractFunction):
551
387
  return cast(AsyncContractFunction, NonExistentReceiveFunction())
552
388
 
553
389
 
554
- class AsyncContractFunctions(BaseContractFunctions):
390
+ class AsyncContractFunctions(BaseContractFunctions[AsyncContractFunction]):
555
391
  def __init__(
556
392
  self,
557
393
  abi: ABI,
@@ -561,46 +397,6 @@ class AsyncContractFunctions(BaseContractFunctions):
561
397
  ) -> None:
562
398
  super().__init__(abi, w3, AsyncContractFunction, address, decode_tuples)
563
399
 
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
-
571
- def __getattr__(self, function_name: str) -> "AsyncContractFunction":
572
- if super().__getattribute__("abi") is None:
573
- raise NoABIFound(
574
- "There is no ABI found for this contract.",
575
- )
576
- elif "_functions" not in self.__dict__ or len(self._functions) == 0:
577
- raise NoABIFunctionsFound(
578
- "The abi for this contract contains no function definitions. ",
579
- "Are you sure you provided the correct contract abi?",
580
- )
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
- ]:
585
- raise ABIFunctionNotFound(
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
593
- )
594
- else:
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)
603
-
604
400
 
605
401
  class AsyncContract(BaseContract):
606
402
  functions: AsyncContractFunctions = None