web3 7.7.0__py3-none-any.whl → 7.9.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 (61) hide show
  1. ens/async_ens.py +1 -1
  2. ens/ens.py +1 -1
  3. web3/_utils/contract_sources/contract_data/ambiguous_function_contract.py +3 -3
  4. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  5. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  6. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  7. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  8. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  9. web3/_utils/contract_sources/contract_data/event_contracts.py +7 -7
  10. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  11. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  12. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  13. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  14. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  15. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  16. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  17. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  18. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  19. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  20. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  21. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  22. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  23. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  24. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  25. web3/_utils/error_formatters_utils.py +1 -1
  26. web3/_utils/events.py +1 -1
  27. web3/_utils/formatters.py +28 -0
  28. web3/_utils/http_session_manager.py +19 -1
  29. web3/_utils/method_formatters.py +121 -20
  30. web3/_utils/module_testing/eth_module.py +102 -2
  31. web3/_utils/module_testing/module_testing_utils.py +0 -42
  32. web3/_utils/module_testing/persistent_connection_provider.py +39 -1
  33. web3/_utils/rpc_abi.py +1 -0
  34. web3/_utils/validation.py +191 -0
  35. web3/beacon/api_endpoints.py +10 -0
  36. web3/beacon/async_beacon.py +47 -0
  37. web3/beacon/beacon.py +45 -0
  38. web3/contract/async_contract.py +2 -206
  39. web3/contract/base_contract.py +208 -12
  40. web3/contract/contract.py +2 -205
  41. web3/datastructures.py +4 -0
  42. web3/eth/async_eth.py +19 -0
  43. web3/eth/eth.py +15 -0
  44. web3/gas_strategies/time_based.py +1 -1
  45. web3/manager.py +47 -194
  46. web3/middleware/base.py +14 -6
  47. web3/providers/async_base.py +5 -4
  48. web3/providers/base.py +3 -3
  49. web3/providers/persistent/persistent.py +29 -3
  50. web3/providers/persistent/request_processor.py +11 -1
  51. web3/providers/persistent/subscription_manager.py +116 -46
  52. web3/providers/persistent/websocket.py +8 -3
  53. web3/providers/rpc/async_rpc.py +8 -3
  54. web3/providers/rpc/rpc.py +8 -3
  55. web3/types.py +36 -13
  56. web3/utils/subscriptions.py +5 -1
  57. {web3-7.7.0.dist-info → web3-7.9.0.dist-info}/LICENSE +1 -1
  58. {web3-7.7.0.dist-info → web3-7.9.0.dist-info}/METADATA +11 -7
  59. {web3-7.7.0.dist-info → web3-7.9.0.dist-info}/RECORD +61 -61
  60. {web3-7.7.0.dist-info → web3-7.9.0.dist-info}/WHEEL +1 -1
  61. {web3-7.7.0.dist-info → web3-7.9.0.dist-info}/top_level.txt +0 -0
web3/contract/contract.py CHANGED
@@ -13,7 +13,6 @@ from typing import (
13
13
 
14
14
  from eth_typing import (
15
15
  ABI,
16
- ABIFunction,
17
16
  ChecksumAddress,
18
17
  )
19
18
  from eth_utils import (
@@ -31,7 +30,6 @@ from hexbytes import (
31
30
 
32
31
  from web3._utils.abi import (
33
32
  fallback_func_abi_exists,
34
- get_name_from_abi_element_identifier,
35
33
  receive_func_abi_exists,
36
34
  )
37
35
  from web3._utils.abi_element_identifiers import (
@@ -42,8 +40,6 @@ from web3._utils.compat import (
42
40
  Self,
43
41
  )
44
42
  from web3._utils.contracts import (
45
- copy_contract_event,
46
- copy_contract_function,
47
43
  parse_block_identifier,
48
44
  )
49
45
  from web3._utils.datatypes import (
@@ -86,12 +82,6 @@ from web3.contract.utils import (
86
82
  transact_with_contract_function,
87
83
  )
88
84
  from web3.exceptions import (
89
- ABIEventNotFound,
90
- ABIFunctionNotFound,
91
- MismatchedABI,
92
- NoABIEventsFound,
93
- NoABIFound,
94
- NoABIFunctionsFound,
95
85
  Web3AttributeError,
96
86
  Web3TypeError,
97
87
  Web3ValidationError,
@@ -103,12 +93,6 @@ from web3.types import (
103
93
  StateOverride,
104
94
  TxParams,
105
95
  )
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,
111
- )
112
96
 
113
97
  if TYPE_CHECKING:
114
98
  from ens import ENS # noqa: F401
@@ -119,9 +103,6 @@ class ContractEvent(BaseContractEvent):
119
103
  # mypy types
120
104
  w3: "Web3"
121
105
 
122
- def __call__(self, *args: Any, **kwargs: Any) -> "ContractEvent":
123
- return copy_contract_event(self, *args, **kwargs)
124
-
125
106
  @combomethod
126
107
  def get_logs(
127
108
  self,
@@ -255,162 +236,18 @@ class ContractEvent(BaseContractEvent):
255
236
  builder.address = self.address
256
237
  return builder
257
238
 
258
- @classmethod
259
- def factory(cls, class_name: str, **kwargs: Any) -> Self:
260
- return PropertyCheckingFactory(class_name, (cls,), kwargs)()
261
239
 
262
-
263
- class ContractEvents(BaseContractEvents):
240
+ class ContractEvents(BaseContractEvents[ContractEvent]):
264
241
  def __init__(
265
242
  self, abi: ABI, w3: "Web3", address: Optional[ChecksumAddress] = None
266
243
  ) -> None:
267
244
  super().__init__(abi, w3, ContractEvent, address)
268
245
 
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
-
305
246
 
306
247
  class ContractFunction(BaseContractFunction):
307
248
  # mypy types
308
249
  w3: "Web3"
309
250
 
310
- def __call__(self, *args: Any, **kwargs: Any) -> "ContractFunction":
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 = 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
- )
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
251
  def call(
415
252
  self,
416
253
  transaction: Optional[TxParams] = None,
@@ -555,7 +392,7 @@ class ContractFunction(BaseContractFunction):
555
392
  return cast(ContractFunction, NonExistentReceiveFunction())
556
393
 
557
394
 
558
- class ContractFunctions(BaseContractFunctions):
395
+ class ContractFunctions(BaseContractFunctions[ContractFunction]):
559
396
  def __init__(
560
397
  self,
561
398
  abi: ABI,
@@ -565,46 +402,6 @@ class ContractFunctions(BaseContractFunctions):
565
402
  ) -> None:
566
403
  super().__init__(abi, w3, ContractFunction, address, decode_tuples)
567
404
 
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
-
575
- def __getattr__(self, function_name: str) -> "ContractFunction":
576
- if super().__getattribute__("abi") is None:
577
- raise NoABIFound(
578
- "There is no ABI found for this contract.",
579
- )
580
- elif "_functions" not in self.__dict__ or len(self._functions) == 0:
581
- raise NoABIFunctionsFound(
582
- "The abi for this contract contains no function definitions. ",
583
- "Are you sure you provided the correct contract abi?",
584
- )
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
- ]:
589
- raise ABIFunctionNotFound(
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
597
- )
598
- else:
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)
607
-
608
405
 
609
406
  class Contract(BaseContract):
610
407
  # mypy types
web3/datastructures.py CHANGED
@@ -17,6 +17,7 @@ from typing import (
17
17
  Tuple,
18
18
  TypeVar,
19
19
  Union,
20
+ ValuesView,
20
21
  cast,
21
22
  )
22
23
 
@@ -337,3 +338,6 @@ class NamedElementOnion(Mapping[TKey, TValue]):
337
338
  # This leads to typing issues, so it's better to use
338
339
  # ``as_tuple_of_middleware()`` to achieve the same result.
339
340
  return iter(self._reversed_middleware()) # type: ignore
341
+
342
+ def values(self) -> ValuesView[TValue]:
343
+ return ValuesView(self._queue)
web3/eth/async_eth.py CHANGED
@@ -7,6 +7,7 @@ from typing import (
7
7
  Dict,
8
8
  List,
9
9
  Optional,
10
+ Sequence,
10
11
  Tuple,
11
12
  Type,
12
13
  Union,
@@ -89,6 +90,8 @@ from web3.types import (
89
90
  LogsSubscriptionArg,
90
91
  Nonce,
91
92
  SignedTx,
93
+ SimulateV1Payload,
94
+ SimulateV1Result,
92
95
  StateOverride,
93
96
  SubscriptionType,
94
97
  SyncStatus,
@@ -288,6 +291,22 @@ class AsyncEth(BaseEth):
288
291
 
289
292
  raise TooManyRequests("Too many CCIP read redirects")
290
293
 
294
+ # eth_simulateV1
295
+
296
+ _simulateV1: Method[
297
+ Callable[
298
+ [SimulateV1Payload, BlockIdentifier],
299
+ Awaitable[Sequence[SimulateV1Result]],
300
+ ]
301
+ ] = Method(RPC.eth_simulateV1)
302
+
303
+ async def simulate_v1(
304
+ self,
305
+ payload: SimulateV1Payload,
306
+ block_identifier: BlockIdentifier,
307
+ ) -> Sequence[SimulateV1Result]:
308
+ return await self._simulateV1(payload, block_identifier)
309
+
291
310
  # eth_createAccessList
292
311
 
293
312
  _create_access_list: Method[
web3/eth/eth.py CHANGED
@@ -85,6 +85,8 @@ from web3.types import (
85
85
  MerkleProof,
86
86
  Nonce,
87
87
  SignedTx,
88
+ SimulateV1Payload,
89
+ SimulateV1Result,
88
90
  StateOverride,
89
91
  SyncStatus,
90
92
  TxData,
@@ -270,6 +272,19 @@ class Eth(BaseEth):
270
272
 
271
273
  raise TooManyRequests("Too many CCIP read redirects")
272
274
 
275
+ # eth_simulateV1
276
+
277
+ _simulateV1: Method[
278
+ Callable[[SimulateV1Payload, BlockIdentifier], Sequence[SimulateV1Result]]
279
+ ] = Method(RPC.eth_simulateV1)
280
+
281
+ def simulate_v1(
282
+ self,
283
+ payload: SimulateV1Payload,
284
+ block_identifier: BlockIdentifier,
285
+ ) -> Sequence[SimulateV1Result]:
286
+ return self._simulateV1(payload, block_identifier)
287
+
273
288
  # eth_createAccessList
274
289
 
275
290
  _create_access_list: Method[
@@ -158,7 +158,7 @@ def _compute_gas_price(
158
158
 
159
159
  :param probabilities: An iterable of `Probability` named-tuples
160
160
  sorted in reverse order.
161
- :param desired_probability: An floating point representation of the desired
161
+ :param desired_probability: A floating point representation of the desired
162
162
  probability. (e.g. ``85% -> 0.85``)
163
163
  """
164
164
  first = probabilities[0]
web3/manager.py CHANGED
@@ -31,17 +31,19 @@ from web3._utils.caching import (
31
31
  from web3._utils.compat import (
32
32
  Self,
33
33
  )
34
+ from web3._utils.formatters import (
35
+ apply_null_result_formatters,
36
+ )
37
+ from web3._utils.validation import (
38
+ raise_error_for_batch_response,
39
+ validate_rpc_response_and_raise_if_error,
40
+ )
34
41
  from web3.datastructures import (
35
42
  NamedElementOnion,
36
43
  )
37
44
  from web3.exceptions import (
38
- BadResponseFormat,
39
- MethodUnavailable,
40
45
  ProviderConnectionError,
41
- RequestTimedOut,
42
46
  TaskNotRunning,
43
- TransactionNotFound,
44
- Web3RPCError,
45
47
  Web3TypeError,
46
48
  )
47
49
  from web3.method import (
@@ -94,176 +96,6 @@ if TYPE_CHECKING:
94
96
 
95
97
 
96
98
  NULL_RESPONSES = [None, HexBytes("0x"), "0x"]
97
- KNOWN_REQUEST_TIMEOUT_MESSAGING = {
98
- # Note: It's important to be very explicit here and not too broad. We don't want
99
- # to accidentally catch a message that is not for a request timeout. In the worst
100
- # case, we raise something more generic like `Web3RPCError`. JSON-RPC unfortunately
101
- # has not standardized error codes for request timeouts.
102
- "request timed out", # go-ethereum
103
- }
104
- METHOD_NOT_FOUND = -32601
105
-
106
-
107
- def _raise_bad_response_format(response: RPCResponse, error: str = "") -> None:
108
- message = "The response was in an unexpected format and unable to be parsed."
109
- raw_response = f"The raw response is: {response}"
110
-
111
- if error is not None and error != "":
112
- error = error[:-1] if error.endswith(".") else error
113
- message = f"{message} {error}. {raw_response}"
114
- else:
115
- message = f"{message} {raw_response}"
116
-
117
- raise BadResponseFormat(message)
118
-
119
-
120
- def apply_error_formatters(
121
- error_formatters: Callable[..., Any],
122
- response: RPCResponse,
123
- ) -> RPCResponse:
124
- if error_formatters:
125
- formatted_resp = pipe(response, error_formatters)
126
- return formatted_resp
127
- else:
128
- return response
129
-
130
-
131
- def apply_null_result_formatters(
132
- null_result_formatters: Callable[..., Any],
133
- response: RPCResponse,
134
- params: Optional[Any] = None,
135
- ) -> RPCResponse:
136
- if null_result_formatters:
137
- formatted_resp = pipe(params, null_result_formatters)
138
- return formatted_resp
139
- else:
140
- return response
141
-
142
-
143
- def _validate_subscription_fields(response: RPCResponse) -> None:
144
- params = response["params"]
145
- subscription = params["subscription"]
146
- if not isinstance(subscription, str) and not len(subscription) == 34:
147
- _raise_bad_response_format(
148
- response, "eth_subscription 'params' must include a 'subscription' field."
149
- )
150
-
151
-
152
- def _validate_response(
153
- response: RPCResponse,
154
- error_formatters: Optional[Callable[..., Any]],
155
- is_subscription_response: bool = False,
156
- logger: Optional[logging.Logger] = None,
157
- params: Optional[Any] = None,
158
- ) -> None:
159
- if "jsonrpc" not in response or response["jsonrpc"] != "2.0":
160
- _raise_bad_response_format(
161
- response, 'The "jsonrpc" field must be present with a value of "2.0".'
162
- )
163
-
164
- response_id = response.get("id")
165
- if "id" in response:
166
- int_error_msg = (
167
- '"id" must be an integer or a string representation of an integer.'
168
- )
169
- if response_id is None and "error" in response:
170
- # errors can sometimes have null `id`, according to the JSON-RPC spec
171
- pass
172
- elif not isinstance(response_id, (str, int)):
173
- _raise_bad_response_format(response, int_error_msg)
174
- elif isinstance(response_id, str):
175
- try:
176
- int(response_id)
177
- except ValueError:
178
- _raise_bad_response_format(response, int_error_msg)
179
- elif is_subscription_response:
180
- # if `id` is not present, this must be a subscription response
181
- _validate_subscription_fields(response)
182
- else:
183
- _raise_bad_response_format(
184
- response,
185
- 'Response must include an "id" field or be formatted as an '
186
- "`eth_subscription` response.",
187
- )
188
-
189
- if all(key in response for key in {"error", "result"}):
190
- _raise_bad_response_format(
191
- response, 'Response cannot include both "error" and "result".'
192
- )
193
- elif (
194
- not any(key in response for key in {"error", "result"})
195
- and not is_subscription_response
196
- ):
197
- _raise_bad_response_format(
198
- response, 'Response must include either "error" or "result".'
199
- )
200
- elif "error" in response:
201
- web3_rpc_error: Optional[Web3RPCError] = None
202
- error = response["error"]
203
-
204
- # raise the error when the value is a string
205
- if error is None or not isinstance(error, dict):
206
- _raise_bad_response_format(
207
- response,
208
- 'response["error"] must be a valid object as defined by the '
209
- "JSON-RPC 2.0 specification.",
210
- )
211
-
212
- # errors must include a message
213
- error_message = error.get("message")
214
- if not isinstance(error_message, str):
215
- _raise_bad_response_format(
216
- response, 'error["message"] is required and must be a string value.'
217
- )
218
- elif error_message == "transaction not found":
219
- transaction_hash = params[0]
220
- web3_rpc_error = TransactionNotFound(
221
- repr(error),
222
- rpc_response=response,
223
- user_message=(f"Transaction with hash {transaction_hash!r} not found."),
224
- )
225
-
226
- # errors must include an integer code
227
- code = error.get("code")
228
- if not isinstance(code, int):
229
- _raise_bad_response_format(
230
- response, 'error["code"] is required and must be an integer value.'
231
- )
232
- elif code == METHOD_NOT_FOUND:
233
- web3_rpc_error = MethodUnavailable(
234
- repr(error),
235
- rpc_response=response,
236
- user_message=(
237
- "This method is not available. Check your node provider or your "
238
- "client's API docs to see what methods are supported and / or "
239
- "currently enabled."
240
- ),
241
- )
242
- elif any(
243
- # parse specific timeout messages
244
- timeout_str in error_message.lower()
245
- for timeout_str in KNOWN_REQUEST_TIMEOUT_MESSAGING
246
- ):
247
- web3_rpc_error = RequestTimedOut(
248
- repr(error),
249
- rpc_response=response,
250
- user_message=(
251
- "The request timed out. Check the connection to your node and "
252
- "try again."
253
- ),
254
- )
255
-
256
- if web3_rpc_error is None:
257
- # if no condition was met above, raise a more generic `Web3RPCError`
258
- web3_rpc_error = Web3RPCError(repr(error), rpc_response=response)
259
-
260
- response = apply_error_formatters(error_formatters, response)
261
- logger.debug(f"RPC error response: {response}")
262
-
263
- raise web3_rpc_error
264
-
265
- elif "result" not in response and not is_subscription_response:
266
- _raise_bad_response_format(response)
267
99
 
268
100
 
269
101
  class RequestManager:
@@ -363,7 +195,7 @@ class RequestManager:
363
195
  and response["params"].get("result") is not None
364
196
  )
365
197
 
366
- _validate_response(
198
+ validate_rpc_response_and_raise_if_error(
367
199
  response,
368
200
  error_formatters,
369
201
  is_subscription_response=is_subscription_response,
@@ -422,6 +254,8 @@ class RequestManager:
422
254
  """
423
255
  Context manager for making batch requests
424
256
  """
257
+ if isinstance(self.provider, AutoProvider):
258
+ self.provider = self.provider._get_active_provider(use_cache=True)
425
259
  if not isinstance(self.provider, (AsyncJSONBaseProvider, JSONBaseProvider)):
426
260
  raise Web3TypeError("Batch requests are not supported by this provider.")
427
261
  return RequestBatcher(self.w3)
@@ -436,17 +270,23 @@ class RequestManager:
436
270
  request_func = provider.batch_request_func(
437
271
  cast("Web3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
438
272
  )
439
- responses = request_func(
273
+ response = request_func(
440
274
  [
441
275
  (method, params)
442
276
  for (method, params), _response_formatters in requests_info
443
277
  ]
444
278
  )
445
- formatted_responses = [
446
- self._format_batched_response(info, resp)
447
- for info, resp in zip(requests_info, responses)
448
- ]
449
- return list(formatted_responses)
279
+
280
+ if isinstance(response, list):
281
+ # expected format
282
+ formatted_responses = [
283
+ self._format_batched_response(info, cast(RPCResponse, resp))
284
+ for info, resp in zip(requests_info, response)
285
+ ]
286
+ return list(formatted_responses)
287
+ else:
288
+ # expect a single response with an error
289
+ raise_error_for_batch_response(response, self.logger)
450
290
 
451
291
  async def _async_make_batch_request(
452
292
  self,
@@ -465,25 +305,31 @@ class RequestManager:
465
305
  # since we add items to the batch without awaiting, we unpack the coroutines
466
306
  # and await them all here
467
307
  unpacked_requests_info = await asyncio.gather(*requests_info)
468
- responses = await request_func(
308
+ response = await request_func(
469
309
  [
470
310
  (method, params)
471
311
  for (method, params), _response_formatters in unpacked_requests_info
472
312
  ]
473
313
  )
474
314
 
475
- if isinstance(self.provider, PersistentConnectionProvider):
476
- # call _process_response for each response in the batch
477
- return [
478
- cast(RPCResponse, await self._process_response(resp))
479
- for resp in responses
315
+ if isinstance(response, list):
316
+ # expected format
317
+ response = cast(List[RPCResponse], response)
318
+ if isinstance(self.provider, PersistentConnectionProvider):
319
+ # call _process_response for each response in the batch
320
+ return [
321
+ cast(RPCResponse, await self._process_response(resp))
322
+ for resp in response
323
+ ]
324
+
325
+ formatted_responses = [
326
+ self._format_batched_response(info, resp)
327
+ for info, resp in zip(unpacked_requests_info, response)
480
328
  ]
481
-
482
- formatted_responses = [
483
- self._format_batched_response(info, resp)
484
- for info, resp in zip(unpacked_requests_info, responses)
485
- ]
486
- return list(formatted_responses)
329
+ return list(formatted_responses)
330
+ else:
331
+ # expect a single response with an error
332
+ raise_error_for_batch_response(response, self.logger)
487
333
 
488
334
  def _format_batched_response(
489
335
  self,
@@ -491,6 +337,13 @@ class RequestManager:
491
337
  response: RPCResponse,
492
338
  ) -> RPCResponse:
493
339
  result_formatters, error_formatters, null_result_formatters = requests_info[1]
340
+ validate_rpc_response_and_raise_if_error(
341
+ response,
342
+ error_formatters,
343
+ is_subscription_response=False,
344
+ logger=self.logger,
345
+ params=requests_info[0][1],
346
+ )
494
347
  return apply_result_formatters(
495
348
  result_formatters,
496
349
  self.formatted_response(