web3 7.8.0__py3-none-any.whl → 7.10.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.
- web3/_utils/abi.py +3 -3
- web3/_utils/batching.py +1 -1
- web3/_utils/contract_sources/contract_data/ambiguous_function_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
- web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
- web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/event_contracts.py +7 -7
- web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
- web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
- web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
- web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
- web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
- web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
- web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
- web3/_utils/error_formatters_utils.py +1 -1
- web3/_utils/formatters.py +28 -0
- web3/_utils/method_formatters.py +121 -20
- web3/_utils/module_testing/eth_module.py +99 -2
- web3/_utils/module_testing/persistent_connection_provider.py +1 -1
- web3/_utils/rpc_abi.py +1 -0
- web3/_utils/validation.py +192 -1
- web3/eth/async_eth.py +19 -0
- web3/eth/eth.py +15 -0
- web3/gas_strategies/rpc.py +1 -1
- web3/gas_strategies/time_based.py +1 -1
- web3/manager.py +13 -204
- web3/providers/async_base.py +1 -1
- web3/providers/legacy_websocket.py +1 -3
- web3/providers/persistent/persistent.py +35 -5
- web3/providers/persistent/subscription_manager.py +7 -2
- web3/providers/persistent/websocket.py +12 -9
- web3/types.py +26 -2
- {web3-7.8.0.dist-info → web3-7.10.0.dist-info}/METADATA +14 -9
- {web3-7.8.0.dist-info → web3-7.10.0.dist-info}/RECORD +47 -47
- {web3-7.8.0.dist-info → web3-7.10.0.dist-info}/WHEEL +1 -1
- {web3-7.8.0.dist-info → web3-7.10.0.dist-info/licenses}/LICENSE +0 -0
- {web3-7.8.0.dist-info → web3-7.10.0.dist-info}/top_level.txt +0 -0
web3/_utils/rpc_abi.py
CHANGED
|
@@ -51,6 +51,7 @@ class RPC:
|
|
|
51
51
|
eth_blobBaseFee = RPCEndpoint("eth_blobBaseFee")
|
|
52
52
|
eth_blockNumber = RPCEndpoint("eth_blockNumber")
|
|
53
53
|
eth_call = RPCEndpoint("eth_call")
|
|
54
|
+
eth_simulateV1 = RPCEndpoint("eth_simulateV1")
|
|
54
55
|
eth_createAccessList = RPCEndpoint("eth_createAccessList")
|
|
55
56
|
eth_chainId = RPCEndpoint("eth_chainId")
|
|
56
57
|
eth_estimateGas = RPCEndpoint("eth_estimateGas")
|
web3/_utils/validation.py
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import itertools
|
|
2
|
+
import logging
|
|
2
3
|
from typing import (
|
|
3
4
|
Any,
|
|
5
|
+
Callable,
|
|
4
6
|
Dict,
|
|
7
|
+
NoReturn,
|
|
8
|
+
Optional,
|
|
5
9
|
)
|
|
6
10
|
|
|
7
11
|
from eth_typing import (
|
|
@@ -53,11 +57,22 @@ from web3._utils.abi import (
|
|
|
53
57
|
length_of_array_type,
|
|
54
58
|
sub_type_of_array_type,
|
|
55
59
|
)
|
|
60
|
+
from web3._utils.formatters import (
|
|
61
|
+
apply_error_formatters,
|
|
62
|
+
)
|
|
56
63
|
from web3.exceptions import (
|
|
64
|
+
BadResponseFormat,
|
|
57
65
|
InvalidAddress,
|
|
66
|
+
MethodUnavailable,
|
|
67
|
+
RequestTimedOut,
|
|
68
|
+
TransactionNotFound,
|
|
69
|
+
Web3RPCError,
|
|
58
70
|
Web3TypeError,
|
|
59
71
|
Web3ValueError,
|
|
60
72
|
)
|
|
73
|
+
from web3.types import (
|
|
74
|
+
RPCResponse,
|
|
75
|
+
)
|
|
61
76
|
|
|
62
77
|
|
|
63
78
|
def _prepare_selector_collision_msg(duplicates: Dict[HexStr, ABIFunction]) -> str:
|
|
@@ -116,7 +131,7 @@ def validate_abi_value(abi_type: TypeStr, value: Any) -> None:
|
|
|
116
131
|
)
|
|
117
132
|
if specified_length != len(value):
|
|
118
133
|
raise Web3TypeError(
|
|
119
|
-
"The following array length does not the length specified"
|
|
134
|
+
"The following array length does not match the length specified "
|
|
120
135
|
f"by the abi-type, {abi_type}: {value}"
|
|
121
136
|
)
|
|
122
137
|
|
|
@@ -211,3 +226,179 @@ def assert_one_val(*args: Any, **kwargs: Any) -> None:
|
|
|
211
226
|
"Exactly one of the passed values can be specified. "
|
|
212
227
|
f"Instead, values were: {args!r}, {kwargs!r}"
|
|
213
228
|
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# -- RPC Response Validation -- #
|
|
232
|
+
|
|
233
|
+
KNOWN_REQUEST_TIMEOUT_MESSAGING = {
|
|
234
|
+
# Note: It's important to be very explicit here and not too broad. We don't want
|
|
235
|
+
# to accidentally catch a message that is not for a request timeout. In the worst
|
|
236
|
+
# case, we raise something more generic like `Web3RPCError`. JSON-RPC unfortunately
|
|
237
|
+
# has not standardized error codes for request timeouts.
|
|
238
|
+
"request timed out", # go-ethereum
|
|
239
|
+
}
|
|
240
|
+
METHOD_NOT_FOUND = -32601
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _validate_subscription_fields(response: RPCResponse) -> None:
|
|
244
|
+
params = response["params"]
|
|
245
|
+
subscription = params["subscription"]
|
|
246
|
+
if not isinstance(subscription, str) and not len(subscription) == 34:
|
|
247
|
+
_raise_bad_response_format(
|
|
248
|
+
response, "eth_subscription 'params' must include a 'subscription' field."
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _raise_bad_response_format(response: RPCResponse, error: str = "") -> None:
|
|
253
|
+
message = "The response was in an unexpected format and unable to be parsed."
|
|
254
|
+
raw_response = f"The raw response is: {response}"
|
|
255
|
+
|
|
256
|
+
if error is not None and error != "":
|
|
257
|
+
error = error[:-1] if error.endswith(".") else error
|
|
258
|
+
message = f"{message} {error}. {raw_response}"
|
|
259
|
+
else:
|
|
260
|
+
message = f"{message} {raw_response}"
|
|
261
|
+
|
|
262
|
+
raise BadResponseFormat(message)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def raise_error_for_batch_response(
|
|
266
|
+
response: RPCResponse,
|
|
267
|
+
logger: Optional[logging.Logger] = None,
|
|
268
|
+
) -> NoReturn:
|
|
269
|
+
error = response.get("error")
|
|
270
|
+
if error is None:
|
|
271
|
+
_raise_bad_response_format(
|
|
272
|
+
response,
|
|
273
|
+
"Batch response must be formatted as a list of responses or "
|
|
274
|
+
"as a single JSON-RPC error response.",
|
|
275
|
+
)
|
|
276
|
+
validate_rpc_response_and_raise_if_error(
|
|
277
|
+
response,
|
|
278
|
+
None,
|
|
279
|
+
is_subscription_response=False,
|
|
280
|
+
logger=logger,
|
|
281
|
+
params=[],
|
|
282
|
+
)
|
|
283
|
+
# This should not be reached, but if it is, raise a generic `BadResponseFormat`
|
|
284
|
+
raise BadResponseFormat(
|
|
285
|
+
"Batch response was in an unexpected format and unable to be parsed."
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def validate_rpc_response_and_raise_if_error(
|
|
290
|
+
response: RPCResponse,
|
|
291
|
+
error_formatters: Optional[Callable[..., Any]],
|
|
292
|
+
is_subscription_response: bool = False,
|
|
293
|
+
logger: Optional[logging.Logger] = None,
|
|
294
|
+
params: Optional[Any] = None,
|
|
295
|
+
) -> None:
|
|
296
|
+
if "jsonrpc" not in response or response["jsonrpc"] != "2.0":
|
|
297
|
+
_raise_bad_response_format(
|
|
298
|
+
response, 'The "jsonrpc" field must be present with a value of "2.0".'
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
response_id = response.get("id")
|
|
302
|
+
if "id" in response:
|
|
303
|
+
int_error_msg = (
|
|
304
|
+
'"id" must be an integer or a string representation of an integer.'
|
|
305
|
+
)
|
|
306
|
+
if response_id is None and "error" in response:
|
|
307
|
+
# errors can sometimes have null `id`, according to the JSON-RPC spec
|
|
308
|
+
pass
|
|
309
|
+
elif not isinstance(response_id, (str, int)):
|
|
310
|
+
_raise_bad_response_format(response, int_error_msg)
|
|
311
|
+
elif isinstance(response_id, str):
|
|
312
|
+
try:
|
|
313
|
+
int(response_id)
|
|
314
|
+
except ValueError:
|
|
315
|
+
_raise_bad_response_format(response, int_error_msg)
|
|
316
|
+
elif is_subscription_response:
|
|
317
|
+
# if `id` is not present, this must be a subscription response
|
|
318
|
+
_validate_subscription_fields(response)
|
|
319
|
+
else:
|
|
320
|
+
_raise_bad_response_format(
|
|
321
|
+
response,
|
|
322
|
+
'Response must include an "id" field or be formatted as an '
|
|
323
|
+
"`eth_subscription` response.",
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
if all(key in response for key in {"error", "result"}):
|
|
327
|
+
_raise_bad_response_format(
|
|
328
|
+
response, 'Response cannot include both "error" and "result".'
|
|
329
|
+
)
|
|
330
|
+
elif (
|
|
331
|
+
not any(key in response for key in {"error", "result"})
|
|
332
|
+
and not is_subscription_response
|
|
333
|
+
):
|
|
334
|
+
_raise_bad_response_format(
|
|
335
|
+
response, 'Response must include either "error" or "result".'
|
|
336
|
+
)
|
|
337
|
+
elif "error" in response:
|
|
338
|
+
web3_rpc_error: Optional[Web3RPCError] = None
|
|
339
|
+
error = response["error"]
|
|
340
|
+
|
|
341
|
+
# raise the error when the value is a string
|
|
342
|
+
if error is None or not isinstance(error, dict):
|
|
343
|
+
_raise_bad_response_format(
|
|
344
|
+
response,
|
|
345
|
+
'response["error"] must be a valid object as defined by the '
|
|
346
|
+
"JSON-RPC 2.0 specification.",
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
# errors must include a message
|
|
350
|
+
error_message = error.get("message")
|
|
351
|
+
if not isinstance(error_message, str):
|
|
352
|
+
_raise_bad_response_format(
|
|
353
|
+
response, 'error["message"] is required and must be a string value.'
|
|
354
|
+
)
|
|
355
|
+
elif error_message == "transaction not found":
|
|
356
|
+
transaction_hash = params[0]
|
|
357
|
+
web3_rpc_error = TransactionNotFound(
|
|
358
|
+
repr(error),
|
|
359
|
+
rpc_response=response,
|
|
360
|
+
user_message=(f"Transaction with hash {transaction_hash!r} not found."),
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
# errors must include an integer code
|
|
364
|
+
code = error.get("code")
|
|
365
|
+
if not isinstance(code, int):
|
|
366
|
+
_raise_bad_response_format(
|
|
367
|
+
response, 'error["code"] is required and must be an integer value.'
|
|
368
|
+
)
|
|
369
|
+
elif code == METHOD_NOT_FOUND:
|
|
370
|
+
web3_rpc_error = MethodUnavailable(
|
|
371
|
+
repr(error),
|
|
372
|
+
rpc_response=response,
|
|
373
|
+
user_message=(
|
|
374
|
+
"This method is not available. Check your node provider or your "
|
|
375
|
+
"client's API docs to see what methods are supported and / or "
|
|
376
|
+
"currently enabled."
|
|
377
|
+
),
|
|
378
|
+
)
|
|
379
|
+
elif any(
|
|
380
|
+
# parse specific timeout messages
|
|
381
|
+
timeout_str in error_message.lower()
|
|
382
|
+
for timeout_str in KNOWN_REQUEST_TIMEOUT_MESSAGING
|
|
383
|
+
):
|
|
384
|
+
web3_rpc_error = RequestTimedOut(
|
|
385
|
+
repr(error),
|
|
386
|
+
rpc_response=response,
|
|
387
|
+
user_message=(
|
|
388
|
+
"The request timed out. Check the connection to your node and "
|
|
389
|
+
"try again."
|
|
390
|
+
),
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
if web3_rpc_error is None:
|
|
394
|
+
# if no condition was met above, raise a more generic `Web3RPCError`
|
|
395
|
+
web3_rpc_error = Web3RPCError(repr(error), rpc_response=response)
|
|
396
|
+
|
|
397
|
+
response = apply_error_formatters(error_formatters, response)
|
|
398
|
+
if logger is not None:
|
|
399
|
+
logger.debug(f"RPC error response: {response}")
|
|
400
|
+
|
|
401
|
+
raise web3_rpc_error
|
|
402
|
+
|
|
403
|
+
elif "result" not in response and not is_subscription_response:
|
|
404
|
+
_raise_bad_response_format(response)
|
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[
|
web3/gas_strategies/rpc.py
CHANGED
|
@@ -15,6 +15,6 @@ def rpc_gas_price_strategy(
|
|
|
15
15
|
w3: Web3, transaction_params: Optional[TxParams] = None
|
|
16
16
|
) -> Wei:
|
|
17
17
|
"""
|
|
18
|
-
A simple gas price strategy deriving
|
|
18
|
+
A simple gas price strategy deriving its value from the eth_gasPrice JSON-RPC call.
|
|
19
19
|
"""
|
|
20
20
|
return w3.eth.gas_price
|
|
@@ -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:
|
|
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
|
@@ -8,7 +8,6 @@ from typing import (
|
|
|
8
8
|
Coroutine,
|
|
9
9
|
Dict,
|
|
10
10
|
List,
|
|
11
|
-
NoReturn,
|
|
12
11
|
Optional,
|
|
13
12
|
Sequence,
|
|
14
13
|
Tuple,
|
|
@@ -32,17 +31,19 @@ from web3._utils.caching import (
|
|
|
32
31
|
from web3._utils.compat import (
|
|
33
32
|
Self,
|
|
34
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
|
+
)
|
|
35
41
|
from web3.datastructures import (
|
|
36
42
|
NamedElementOnion,
|
|
37
43
|
)
|
|
38
44
|
from web3.exceptions import (
|
|
39
|
-
BadResponseFormat,
|
|
40
|
-
MethodUnavailable,
|
|
41
45
|
ProviderConnectionError,
|
|
42
|
-
RequestTimedOut,
|
|
43
46
|
TaskNotRunning,
|
|
44
|
-
TransactionNotFound,
|
|
45
|
-
Web3RPCError,
|
|
46
47
|
Web3TypeError,
|
|
47
48
|
)
|
|
48
49
|
from web3.method import (
|
|
@@ -95,200 +96,6 @@ if TYPE_CHECKING:
|
|
|
95
96
|
|
|
96
97
|
|
|
97
98
|
NULL_RESPONSES = [None, HexBytes("0x"), "0x"]
|
|
98
|
-
KNOWN_REQUEST_TIMEOUT_MESSAGING = {
|
|
99
|
-
# Note: It's important to be very explicit here and not too broad. We don't want
|
|
100
|
-
# to accidentally catch a message that is not for a request timeout. In the worst
|
|
101
|
-
# case, we raise something more generic like `Web3RPCError`. JSON-RPC unfortunately
|
|
102
|
-
# has not standardized error codes for request timeouts.
|
|
103
|
-
"request timed out", # go-ethereum
|
|
104
|
-
}
|
|
105
|
-
METHOD_NOT_FOUND = -32601
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def _raise_bad_response_format(response: RPCResponse, error: str = "") -> None:
|
|
109
|
-
message = "The response was in an unexpected format and unable to be parsed."
|
|
110
|
-
raw_response = f"The raw response is: {response}"
|
|
111
|
-
|
|
112
|
-
if error is not None and error != "":
|
|
113
|
-
error = error[:-1] if error.endswith(".") else error
|
|
114
|
-
message = f"{message} {error}. {raw_response}"
|
|
115
|
-
else:
|
|
116
|
-
message = f"{message} {raw_response}"
|
|
117
|
-
|
|
118
|
-
raise BadResponseFormat(message)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def apply_error_formatters(
|
|
122
|
-
error_formatters: Callable[..., Any],
|
|
123
|
-
response: RPCResponse,
|
|
124
|
-
) -> RPCResponse:
|
|
125
|
-
if error_formatters:
|
|
126
|
-
formatted_resp = pipe(response, error_formatters)
|
|
127
|
-
return formatted_resp
|
|
128
|
-
else:
|
|
129
|
-
return response
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def apply_null_result_formatters(
|
|
133
|
-
null_result_formatters: Callable[..., Any],
|
|
134
|
-
response: RPCResponse,
|
|
135
|
-
params: Optional[Any] = None,
|
|
136
|
-
) -> RPCResponse:
|
|
137
|
-
if null_result_formatters:
|
|
138
|
-
formatted_resp = pipe(params, null_result_formatters)
|
|
139
|
-
return formatted_resp
|
|
140
|
-
else:
|
|
141
|
-
return response
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def _validate_subscription_fields(response: RPCResponse) -> None:
|
|
145
|
-
params = response["params"]
|
|
146
|
-
subscription = params["subscription"]
|
|
147
|
-
if not isinstance(subscription, str) and not len(subscription) == 34:
|
|
148
|
-
_raise_bad_response_format(
|
|
149
|
-
response, "eth_subscription 'params' must include a 'subscription' field."
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def _validate_response(
|
|
154
|
-
response: RPCResponse,
|
|
155
|
-
error_formatters: Optional[Callable[..., Any]],
|
|
156
|
-
is_subscription_response: bool = False,
|
|
157
|
-
logger: Optional[logging.Logger] = None,
|
|
158
|
-
params: Optional[Any] = None,
|
|
159
|
-
) -> None:
|
|
160
|
-
if "jsonrpc" not in response or response["jsonrpc"] != "2.0":
|
|
161
|
-
_raise_bad_response_format(
|
|
162
|
-
response, 'The "jsonrpc" field must be present with a value of "2.0".'
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
response_id = response.get("id")
|
|
166
|
-
if "id" in response:
|
|
167
|
-
int_error_msg = (
|
|
168
|
-
'"id" must be an integer or a string representation of an integer.'
|
|
169
|
-
)
|
|
170
|
-
if response_id is None and "error" in response:
|
|
171
|
-
# errors can sometimes have null `id`, according to the JSON-RPC spec
|
|
172
|
-
pass
|
|
173
|
-
elif not isinstance(response_id, (str, int)):
|
|
174
|
-
_raise_bad_response_format(response, int_error_msg)
|
|
175
|
-
elif isinstance(response_id, str):
|
|
176
|
-
try:
|
|
177
|
-
int(response_id)
|
|
178
|
-
except ValueError:
|
|
179
|
-
_raise_bad_response_format(response, int_error_msg)
|
|
180
|
-
elif is_subscription_response:
|
|
181
|
-
# if `id` is not present, this must be a subscription response
|
|
182
|
-
_validate_subscription_fields(response)
|
|
183
|
-
else:
|
|
184
|
-
_raise_bad_response_format(
|
|
185
|
-
response,
|
|
186
|
-
'Response must include an "id" field or be formatted as an '
|
|
187
|
-
"`eth_subscription` response.",
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
if all(key in response for key in {"error", "result"}):
|
|
191
|
-
_raise_bad_response_format(
|
|
192
|
-
response, 'Response cannot include both "error" and "result".'
|
|
193
|
-
)
|
|
194
|
-
elif (
|
|
195
|
-
not any(key in response for key in {"error", "result"})
|
|
196
|
-
and not is_subscription_response
|
|
197
|
-
):
|
|
198
|
-
_raise_bad_response_format(
|
|
199
|
-
response, 'Response must include either "error" or "result".'
|
|
200
|
-
)
|
|
201
|
-
elif "error" in response:
|
|
202
|
-
web3_rpc_error: Optional[Web3RPCError] = None
|
|
203
|
-
error = response["error"]
|
|
204
|
-
|
|
205
|
-
# raise the error when the value is a string
|
|
206
|
-
if error is None or not isinstance(error, dict):
|
|
207
|
-
_raise_bad_response_format(
|
|
208
|
-
response,
|
|
209
|
-
'response["error"] must be a valid object as defined by the '
|
|
210
|
-
"JSON-RPC 2.0 specification.",
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
# errors must include a message
|
|
214
|
-
error_message = error.get("message")
|
|
215
|
-
if not isinstance(error_message, str):
|
|
216
|
-
_raise_bad_response_format(
|
|
217
|
-
response, 'error["message"] is required and must be a string value.'
|
|
218
|
-
)
|
|
219
|
-
elif error_message == "transaction not found":
|
|
220
|
-
transaction_hash = params[0]
|
|
221
|
-
web3_rpc_error = TransactionNotFound(
|
|
222
|
-
repr(error),
|
|
223
|
-
rpc_response=response,
|
|
224
|
-
user_message=(f"Transaction with hash {transaction_hash!r} not found."),
|
|
225
|
-
)
|
|
226
|
-
|
|
227
|
-
# errors must include an integer code
|
|
228
|
-
code = error.get("code")
|
|
229
|
-
if not isinstance(code, int):
|
|
230
|
-
_raise_bad_response_format(
|
|
231
|
-
response, 'error["code"] is required and must be an integer value.'
|
|
232
|
-
)
|
|
233
|
-
elif code == METHOD_NOT_FOUND:
|
|
234
|
-
web3_rpc_error = MethodUnavailable(
|
|
235
|
-
repr(error),
|
|
236
|
-
rpc_response=response,
|
|
237
|
-
user_message=(
|
|
238
|
-
"This method is not available. Check your node provider or your "
|
|
239
|
-
"client's API docs to see what methods are supported and / or "
|
|
240
|
-
"currently enabled."
|
|
241
|
-
),
|
|
242
|
-
)
|
|
243
|
-
elif any(
|
|
244
|
-
# parse specific timeout messages
|
|
245
|
-
timeout_str in error_message.lower()
|
|
246
|
-
for timeout_str in KNOWN_REQUEST_TIMEOUT_MESSAGING
|
|
247
|
-
):
|
|
248
|
-
web3_rpc_error = RequestTimedOut(
|
|
249
|
-
repr(error),
|
|
250
|
-
rpc_response=response,
|
|
251
|
-
user_message=(
|
|
252
|
-
"The request timed out. Check the connection to your node and "
|
|
253
|
-
"try again."
|
|
254
|
-
),
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
if web3_rpc_error is None:
|
|
258
|
-
# if no condition was met above, raise a more generic `Web3RPCError`
|
|
259
|
-
web3_rpc_error = Web3RPCError(repr(error), rpc_response=response)
|
|
260
|
-
|
|
261
|
-
response = apply_error_formatters(error_formatters, response)
|
|
262
|
-
logger.debug(f"RPC error response: {response}")
|
|
263
|
-
|
|
264
|
-
raise web3_rpc_error
|
|
265
|
-
|
|
266
|
-
elif "result" not in response and not is_subscription_response:
|
|
267
|
-
_raise_bad_response_format(response)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
def _raise_error_for_batch_response(
|
|
271
|
-
response: RPCResponse,
|
|
272
|
-
logger: Optional[logging.Logger] = None,
|
|
273
|
-
) -> NoReturn:
|
|
274
|
-
error = response.get("error")
|
|
275
|
-
if error is None:
|
|
276
|
-
_raise_bad_response_format(
|
|
277
|
-
response,
|
|
278
|
-
"Batch response must be formatted as a list of responses or "
|
|
279
|
-
"as a single JSON-RPC error response.",
|
|
280
|
-
)
|
|
281
|
-
_validate_response(
|
|
282
|
-
response,
|
|
283
|
-
None,
|
|
284
|
-
is_subscription_response=False,
|
|
285
|
-
logger=logger,
|
|
286
|
-
params=[],
|
|
287
|
-
)
|
|
288
|
-
# This should not be reached, but if it is, raise a generic `BadResponseFormat`
|
|
289
|
-
raise BadResponseFormat(
|
|
290
|
-
"Batch response was in an unexpected format and unable to be parsed."
|
|
291
|
-
)
|
|
292
99
|
|
|
293
100
|
|
|
294
101
|
class RequestManager:
|
|
@@ -388,7 +195,7 @@ class RequestManager:
|
|
|
388
195
|
and response["params"].get("result") is not None
|
|
389
196
|
)
|
|
390
197
|
|
|
391
|
-
|
|
198
|
+
validate_rpc_response_and_raise_if_error(
|
|
392
199
|
response,
|
|
393
200
|
error_formatters,
|
|
394
201
|
is_subscription_response=is_subscription_response,
|
|
@@ -447,6 +254,8 @@ class RequestManager:
|
|
|
447
254
|
"""
|
|
448
255
|
Context manager for making batch requests
|
|
449
256
|
"""
|
|
257
|
+
if isinstance(self.provider, AutoProvider):
|
|
258
|
+
self.provider = self.provider._get_active_provider(use_cache=True)
|
|
450
259
|
if not isinstance(self.provider, (AsyncJSONBaseProvider, JSONBaseProvider)):
|
|
451
260
|
raise Web3TypeError("Batch requests are not supported by this provider.")
|
|
452
261
|
return RequestBatcher(self.w3)
|
|
@@ -477,7 +286,7 @@ class RequestManager:
|
|
|
477
286
|
return list(formatted_responses)
|
|
478
287
|
else:
|
|
479
288
|
# expect a single response with an error
|
|
480
|
-
|
|
289
|
+
raise_error_for_batch_response(response, self.logger)
|
|
481
290
|
|
|
482
291
|
async def _async_make_batch_request(
|
|
483
292
|
self,
|
|
@@ -520,7 +329,7 @@ class RequestManager:
|
|
|
520
329
|
return list(formatted_responses)
|
|
521
330
|
else:
|
|
522
331
|
# expect a single response with an error
|
|
523
|
-
|
|
332
|
+
raise_error_for_batch_response(response, self.logger)
|
|
524
333
|
|
|
525
334
|
def _format_batched_response(
|
|
526
335
|
self,
|
|
@@ -528,7 +337,7 @@ class RequestManager:
|
|
|
528
337
|
response: RPCResponse,
|
|
529
338
|
) -> RPCResponse:
|
|
530
339
|
result_formatters, error_formatters, null_result_formatters = requests_info[1]
|
|
531
|
-
|
|
340
|
+
validate_rpc_response_and_raise_if_error(
|
|
532
341
|
response,
|
|
533
342
|
error_formatters,
|
|
534
343
|
is_subscription_response=False,
|
web3/providers/async_base.py
CHANGED