web3 7.0.0b3__py3-none-any.whl → 7.0.0b5__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.
- ens/_normalization.py +1 -3
- ens/async_ens.py +12 -9
- ens/contract_data.py +2 -2
- ens/ens.py +8 -5
- ens/exceptions.py +19 -27
- ens/specs/nf.json +1 -1
- ens/specs/normalization_spec.json +1 -1
- ens/utils.py +17 -10
- web3/__init__.py +2 -7
- web3/_utils/abi.py +30 -29
- web3/_utils/async_transactions.py +7 -4
- web3/_utils/blocks.py +6 -2
- web3/_utils/caching.py +7 -3
- web3/_utils/compat/__init__.py +0 -3
- web3/_utils/contract_sources/compile_contracts.py +1 -1
- web3/_utils/contracts.py +17 -17
- web3/_utils/datatypes.py +5 -1
- web3/_utils/decorators.py +6 -1
- web3/_utils/empty.py +1 -1
- web3/_utils/encoding.py +15 -10
- web3/_utils/error_formatters_utils.py +5 -3
- web3/_utils/events.py +38 -36
- web3/_utils/fee_utils.py +2 -4
- web3/_utils/filters.py +23 -18
- web3/_utils/formatters.py +2 -2
- web3/_utils/math.py +3 -2
- web3/_utils/method_formatters.py +24 -28
- web3/_utils/module.py +2 -1
- web3/_utils/module_testing/__init__.py +0 -3
- web3/_utils/module_testing/eth_module.py +494 -432
- web3/_utils/module_testing/module_testing_utils.py +1 -3
- web3/_utils/module_testing/utils.py +14 -7
- web3/_utils/normalizers.py +3 -3
- web3/_utils/request.py +4 -4
- web3/_utils/rpc_abi.py +5 -19
- web3/_utils/threads.py +8 -7
- web3/_utils/transactions.py +14 -12
- web3/_utils/type_conversion.py +5 -1
- web3/_utils/validation.py +12 -10
- web3/contract/async_contract.py +23 -18
- web3/contract/base_contract.py +69 -74
- web3/contract/contract.py +25 -19
- web3/contract/utils.py +11 -6
- web3/datastructures.py +22 -12
- web3/eth/async_eth.py +38 -32
- web3/eth/base_eth.py +7 -3
- web3/eth/eth.py +20 -15
- web3/exceptions.py +83 -78
- web3/gas_strategies/time_based.py +2 -4
- web3/geth.py +1 -191
- web3/main.py +6 -6
- web3/manager.py +128 -68
- web3/method.py +13 -5
- web3/middleware/base.py +4 -2
- web3/middleware/filter.py +45 -23
- web3/middleware/formatting.py +6 -3
- web3/middleware/names.py +4 -1
- web3/middleware/signing.py +8 -4
- web3/middleware/stalecheck.py +2 -1
- web3/providers/eth_tester/defaults.py +1 -49
- web3/providers/eth_tester/main.py +41 -15
- web3/providers/eth_tester/middleware.py +13 -9
- web3/providers/ipc.py +7 -3
- web3/providers/persistent/async_ipc.py +6 -7
- web3/providers/persistent/persistent.py +11 -1
- web3/providers/persistent/request_processor.py +7 -7
- web3/providers/persistent/websocket.py +3 -3
- web3/providers/rpc/async_rpc.py +24 -7
- web3/providers/rpc/rpc.py +30 -17
- web3/providers/rpc/utils.py +1 -10
- web3/testing.py +4 -4
- web3/tools/benchmark/main.py +13 -9
- web3/tools/benchmark/node.py +2 -8
- web3/tools/benchmark/utils.py +1 -1
- web3/tracing.py +9 -5
- web3/types.py +20 -23
- {web3-7.0.0b3.dist-info → web3-7.0.0b5.dist-info}/METADATA +13 -28
- {web3-7.0.0b3.dist-info → web3-7.0.0b5.dist-info}/RECORD +81 -82
- web3/_utils/module_testing/go_ethereum_personal_module.py +0 -300
- {web3-7.0.0b3.dist-info → web3-7.0.0b5.dist-info}/LICENSE +0 -0
- {web3-7.0.0b3.dist-info → web3-7.0.0b5.dist-info}/WHEEL +0 -0
- {web3-7.0.0b3.dist-info → web3-7.0.0b5.dist-info}/top_level.txt +0 -0
web3/manager.py
CHANGED
|
@@ -35,6 +35,8 @@ from web3.exceptions import (
|
|
|
35
35
|
BadResponseFormat,
|
|
36
36
|
MethodUnavailable,
|
|
37
37
|
ProviderConnectionError,
|
|
38
|
+
Web3RPCError,
|
|
39
|
+
Web3TypeError,
|
|
38
40
|
)
|
|
39
41
|
from web3.middleware import (
|
|
40
42
|
AttributeDictMiddleware,
|
|
@@ -85,6 +87,7 @@ def _raise_bad_response_format(response: RPCResponse, error: str = "") -> None:
|
|
|
85
87
|
raw_response = f"The raw response is: {response}"
|
|
86
88
|
|
|
87
89
|
if error is not None and error != "":
|
|
90
|
+
error = error[:-1] if error.endswith(".") else error
|
|
88
91
|
message = f"{message} {error}. {raw_response}"
|
|
89
92
|
else:
|
|
90
93
|
message = f"{message} {raw_response}"
|
|
@@ -115,6 +118,111 @@ def apply_null_result_formatters(
|
|
|
115
118
|
return response
|
|
116
119
|
|
|
117
120
|
|
|
121
|
+
def _validate_subscription_fields(response: RPCResponse) -> None:
|
|
122
|
+
params = response["params"]
|
|
123
|
+
subscription = params["subscription"]
|
|
124
|
+
if not isinstance(subscription, str) and not len(subscription) == 34:
|
|
125
|
+
_raise_bad_response_format(
|
|
126
|
+
response, "eth_subscription 'params' must include a 'subscription' field."
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _validate_response(
|
|
131
|
+
response: RPCResponse,
|
|
132
|
+
error_formatters: Optional[Callable[..., Any]],
|
|
133
|
+
is_subscription_response: bool = False,
|
|
134
|
+
logger: Optional[logging.Logger] = None,
|
|
135
|
+
) -> None:
|
|
136
|
+
if "jsonrpc" not in response or response["jsonrpc"] != "2.0":
|
|
137
|
+
_raise_bad_response_format(
|
|
138
|
+
response, 'The "jsonrpc" field must be present with a value of "2.0".'
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
response_id = response.get("id")
|
|
142
|
+
if "id" in response:
|
|
143
|
+
int_error_msg = (
|
|
144
|
+
'"id" must be an integer or a string representation of an integer.'
|
|
145
|
+
)
|
|
146
|
+
if response_id is None and "error" in response:
|
|
147
|
+
# errors can sometimes have null `id`, according to the JSON-RPC spec
|
|
148
|
+
pass
|
|
149
|
+
elif not isinstance(response_id, (str, int)):
|
|
150
|
+
_raise_bad_response_format(response, int_error_msg)
|
|
151
|
+
elif isinstance(response_id, str):
|
|
152
|
+
try:
|
|
153
|
+
int(response_id)
|
|
154
|
+
except ValueError:
|
|
155
|
+
_raise_bad_response_format(response, int_error_msg)
|
|
156
|
+
elif is_subscription_response:
|
|
157
|
+
# if `id` is not present, this must be a subscription response
|
|
158
|
+
_validate_subscription_fields(response)
|
|
159
|
+
else:
|
|
160
|
+
_raise_bad_response_format(
|
|
161
|
+
response,
|
|
162
|
+
'Response must include an "id" field or be formatted as an '
|
|
163
|
+
"`eth_subscription` response.",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if all(key in response for key in {"error", "result"}):
|
|
167
|
+
_raise_bad_response_format(
|
|
168
|
+
response, 'Response cannot include both "error" and "result".'
|
|
169
|
+
)
|
|
170
|
+
elif (
|
|
171
|
+
not any(key in response for key in {"error", "result"})
|
|
172
|
+
and not is_subscription_response
|
|
173
|
+
):
|
|
174
|
+
_raise_bad_response_format(
|
|
175
|
+
response, 'Response must include either "error" or "result".'
|
|
176
|
+
)
|
|
177
|
+
elif "error" in response:
|
|
178
|
+
error = response["error"]
|
|
179
|
+
|
|
180
|
+
# raise the error when the value is a string
|
|
181
|
+
if error is None or not isinstance(error, dict):
|
|
182
|
+
_raise_bad_response_format(
|
|
183
|
+
response,
|
|
184
|
+
'response["error"] must be a valid object as defined by the '
|
|
185
|
+
"JSON-RPC 2.0 specification.",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# errors must include an integer code
|
|
189
|
+
code = error.get("code")
|
|
190
|
+
if not isinstance(code, int):
|
|
191
|
+
_raise_bad_response_format(
|
|
192
|
+
response, 'error["code"] is required and must be an integer value.'
|
|
193
|
+
)
|
|
194
|
+
elif code == METHOD_NOT_FOUND:
|
|
195
|
+
exception = MethodUnavailable(
|
|
196
|
+
repr(error),
|
|
197
|
+
rpc_response=response,
|
|
198
|
+
user_message=(
|
|
199
|
+
"This method is not available. Check your node provider or your "
|
|
200
|
+
"client's API docs to see what methods are supported and / or "
|
|
201
|
+
"currently enabled."
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
logger.error(exception.user_message)
|
|
205
|
+
logger.debug(f"RPC error response: {response}")
|
|
206
|
+
raise exception
|
|
207
|
+
|
|
208
|
+
# errors must include a message
|
|
209
|
+
error_message = error.get("message")
|
|
210
|
+
if not isinstance(error_message, str):
|
|
211
|
+
_raise_bad_response_format(
|
|
212
|
+
response, 'error["message"] is required and must be a string value.'
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
apply_error_formatters(error_formatters, response)
|
|
216
|
+
|
|
217
|
+
web3_rpc_error = Web3RPCError(repr(error), rpc_response=response)
|
|
218
|
+
logger.error(web3_rpc_error.user_message)
|
|
219
|
+
logger.debug(f"RPC error response: {response}")
|
|
220
|
+
raise web3_rpc_error
|
|
221
|
+
|
|
222
|
+
elif "result" not in response and not is_subscription_response:
|
|
223
|
+
_raise_bad_response_format(response)
|
|
224
|
+
|
|
225
|
+
|
|
118
226
|
class RequestManager:
|
|
119
227
|
logger = logging.getLogger("web3.manager.RequestManager")
|
|
120
228
|
|
|
@@ -201,90 +309,42 @@ class RequestManager:
|
|
|
201
309
|
#
|
|
202
310
|
# See also: https://www.jsonrpc.org/specification
|
|
203
311
|
#
|
|
204
|
-
@staticmethod
|
|
205
312
|
def formatted_response(
|
|
313
|
+
self,
|
|
206
314
|
response: RPCResponse,
|
|
207
315
|
params: Any,
|
|
208
316
|
error_formatters: Optional[Callable[..., Any]] = None,
|
|
209
317
|
null_result_formatters: Optional[Callable[..., Any]] = None,
|
|
210
318
|
) -> Any:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
)
|
|
319
|
+
is_subscription_response = (
|
|
320
|
+
response.get("method") == "eth_subscription"
|
|
321
|
+
and response.get("params") is not None
|
|
322
|
+
and response["params"].get("subscription") is not None
|
|
323
|
+
and response["params"].get("result") is not None
|
|
324
|
+
)
|
|
216
325
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if response_id is None and "error" not in response:
|
|
224
|
-
_raise_bad_response_format(
|
|
225
|
-
response, '"id" must be None when an error is present'
|
|
226
|
-
)
|
|
227
|
-
elif not isinstance(response_id, (str, int, type(None))):
|
|
228
|
-
_raise_bad_response_format(response, '"id" must be a string or integer')
|
|
229
|
-
|
|
230
|
-
# Response may not include both "error" and "result"
|
|
231
|
-
if "error" in response and "result" in response:
|
|
232
|
-
_raise_bad_response_format(
|
|
233
|
-
response, 'Response cannot include both "error" and "result"'
|
|
234
|
-
)
|
|
326
|
+
_validate_response(
|
|
327
|
+
response,
|
|
328
|
+
error_formatters,
|
|
329
|
+
is_subscription_response=is_subscription_response,
|
|
330
|
+
logger=self.logger,
|
|
331
|
+
)
|
|
235
332
|
|
|
236
|
-
#
|
|
237
|
-
|
|
238
|
-
error = response.get("error")
|
|
239
|
-
# Raise the error when the value is a string
|
|
240
|
-
if error is None or isinstance(error, str):
|
|
241
|
-
raise ValueError(error)
|
|
242
|
-
|
|
243
|
-
# Errors must include an integer code
|
|
244
|
-
code = error.get("code")
|
|
245
|
-
if not isinstance(code, int):
|
|
246
|
-
_raise_bad_response_format(response, "error['code'] must be an integer")
|
|
247
|
-
elif code == METHOD_NOT_FOUND:
|
|
248
|
-
raise MethodUnavailable(
|
|
249
|
-
error,
|
|
250
|
-
user_message="Check your node provider's API docs to see what "
|
|
251
|
-
"methods are supported",
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
# Errors must include a message
|
|
255
|
-
if not isinstance(error.get("message"), str):
|
|
256
|
-
_raise_bad_response_format(
|
|
257
|
-
response, "error['message'] must be a string"
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
apply_error_formatters(error_formatters, response)
|
|
261
|
-
|
|
262
|
-
raise ValueError(error)
|
|
263
|
-
|
|
264
|
-
# Format and validate results
|
|
265
|
-
elif "result" in response:
|
|
333
|
+
# format results
|
|
334
|
+
if "result" in response:
|
|
266
335
|
# Null values for result should apply null_result_formatters
|
|
267
336
|
# Skip when result not present in the response (fallback to False)
|
|
268
337
|
if response.get("result", False) in NULL_RESPONSES:
|
|
269
338
|
apply_null_result_formatters(null_result_formatters, response, params)
|
|
270
339
|
return response.get("result")
|
|
271
340
|
|
|
272
|
-
#
|
|
273
|
-
elif
|
|
274
|
-
response.get("method") == "eth_subscription"
|
|
275
|
-
and response.get("params") is not None
|
|
276
|
-
and response["params"].get("subscription") is not None
|
|
277
|
-
and response["params"].get("result") is not None
|
|
278
|
-
):
|
|
341
|
+
# response from eth_subscription includes response["params"]["result"]
|
|
342
|
+
elif is_subscription_response:
|
|
279
343
|
return {
|
|
280
344
|
"subscription": response["params"]["subscription"],
|
|
281
345
|
"result": response["params"]["result"],
|
|
282
346
|
}
|
|
283
347
|
|
|
284
|
-
# Any other response type raises BadResponseFormat
|
|
285
|
-
else:
|
|
286
|
-
_raise_bad_response_format(response)
|
|
287
|
-
|
|
288
348
|
def request_blocking(
|
|
289
349
|
self,
|
|
290
350
|
method: Union[RPCEndpoint, Callable[..., RPCEndpoint]],
|
|
@@ -323,8 +383,8 @@ class RequestManager:
|
|
|
323
383
|
cast("AsyncWeb3", self.w3), cast("MiddlewareOnion", self.middleware_onion)
|
|
324
384
|
)
|
|
325
385
|
self.logger.debug(
|
|
326
|
-
"Making request to open socket connection
|
|
327
|
-
f"
|
|
386
|
+
"Making request to open socket connection: "
|
|
387
|
+
f"{provider.get_endpoint_uri_or_ipc_path()}, method: {method}"
|
|
328
388
|
)
|
|
329
389
|
response = await request_func(method, params)
|
|
330
390
|
return await self._process_response(response)
|
|
@@ -337,7 +397,7 @@ class RequestManager:
|
|
|
337
397
|
|
|
338
398
|
async def _message_stream(self) -> AsyncGenerator[RPCResponse, None]:
|
|
339
399
|
if not isinstance(self._provider, PersistentConnectionProvider):
|
|
340
|
-
raise
|
|
400
|
+
raise Web3TypeError(
|
|
341
401
|
"Only providers that maintain an open, persistent connection "
|
|
342
402
|
"can listen to streams."
|
|
343
403
|
)
|
web3/method.py
CHANGED
|
@@ -32,7 +32,9 @@ from web3._utils.rpc_abi import (
|
|
|
32
32
|
RPC,
|
|
33
33
|
)
|
|
34
34
|
from web3.exceptions import (
|
|
35
|
+
Web3TypeError,
|
|
35
36
|
Web3ValidationError,
|
|
37
|
+
Web3ValueError,
|
|
36
38
|
)
|
|
37
39
|
from web3.types import (
|
|
38
40
|
RPCEndpoint,
|
|
@@ -66,7 +68,9 @@ def _set_mungers(
|
|
|
66
68
|
return (
|
|
67
69
|
mungers
|
|
68
70
|
if mungers
|
|
69
|
-
else [default_munger]
|
|
71
|
+
else [default_munger]
|
|
72
|
+
if is_property
|
|
73
|
+
else [default_root_munger]
|
|
70
74
|
)
|
|
71
75
|
|
|
72
76
|
|
|
@@ -84,7 +88,8 @@ TFunc = TypeVar("TFunc", bound=Callable[..., Any])
|
|
|
84
88
|
|
|
85
89
|
|
|
86
90
|
class Method(Generic[TFunc]):
|
|
87
|
-
"""
|
|
91
|
+
"""
|
|
92
|
+
Method object for web3 module methods
|
|
88
93
|
|
|
89
94
|
Calls to the Method go through these steps:
|
|
90
95
|
|
|
@@ -147,7 +152,7 @@ class Method(Generic[TFunc]):
|
|
|
147
152
|
self, obj: Optional["Module"] = None, obj_type: Optional[Type["Module"]] = None
|
|
148
153
|
) -> TFunc:
|
|
149
154
|
if obj is None:
|
|
150
|
-
raise
|
|
155
|
+
raise Web3TypeError(
|
|
151
156
|
"Direct calls to methods are not supported. "
|
|
152
157
|
"Methods must be called from an module instance, "
|
|
153
158
|
"usually attached to a web3 instance."
|
|
@@ -163,7 +168,7 @@ class Method(Generic[TFunc]):
|
|
|
163
168
|
return self.json_rpc_method
|
|
164
169
|
elif isinstance(self.json_rpc_method, (str,)):
|
|
165
170
|
return lambda *_: self.json_rpc_method
|
|
166
|
-
raise
|
|
171
|
+
raise Web3ValueError(
|
|
167
172
|
"``json_rpc_method`` config invalid. May be a string or function"
|
|
168
173
|
)
|
|
169
174
|
|
|
@@ -175,7 +180,9 @@ class Method(Generic[TFunc]):
|
|
|
175
180
|
lambda args, munger: munger(module, *args, **kwargs), self.mungers, args
|
|
176
181
|
)
|
|
177
182
|
|
|
178
|
-
def process_params(
|
|
183
|
+
def process_params(
|
|
184
|
+
self, module: "Module", *args: Any, **kwargs: Any
|
|
185
|
+
) -> Tuple[
|
|
179
186
|
Tuple[Union[RPCEndpoint, Callable[..., RPCEndpoint]], Tuple[Any, ...]],
|
|
180
187
|
Tuple[
|
|
181
188
|
Union[TReturn, Dict[str, Callable[..., Any]]],
|
|
@@ -226,5 +233,6 @@ class DeprecatedMethod:
|
|
|
226
233
|
warnings.warn(
|
|
227
234
|
f"{self.old_name} is deprecated in favor of {self.new_name}",
|
|
228
235
|
category=DeprecationWarning,
|
|
236
|
+
stacklevel=2,
|
|
229
237
|
)
|
|
230
238
|
return self.method.__get__(obj, obj_type)
|
web3/middleware/base.py
CHANGED
|
@@ -48,7 +48,9 @@ class Web3Middleware:
|
|
|
48
48
|
def request_processor(self, method: "RPCEndpoint", params: Any) -> Any:
|
|
49
49
|
return method, params
|
|
50
50
|
|
|
51
|
-
def response_processor(
|
|
51
|
+
def response_processor(
|
|
52
|
+
self, method: "RPCEndpoint", response: "RPCResponse"
|
|
53
|
+
) -> "RPCResponse":
|
|
52
54
|
return response
|
|
53
55
|
|
|
54
56
|
# -- async -- #
|
|
@@ -76,7 +78,7 @@ class Web3Middleware:
|
|
|
76
78
|
self,
|
|
77
79
|
method: "RPCEndpoint",
|
|
78
80
|
response: "RPCResponse",
|
|
79
|
-
) ->
|
|
81
|
+
) -> "RPCResponse":
|
|
80
82
|
return response
|
|
81
83
|
|
|
82
84
|
|
web3/middleware/filter.py
CHANGED
|
@@ -42,6 +42,9 @@ from web3._utils.formatters import (
|
|
|
42
42
|
from web3._utils.rpc_abi import (
|
|
43
43
|
RPC,
|
|
44
44
|
)
|
|
45
|
+
from web3.exceptions import (
|
|
46
|
+
Web3TypeError,
|
|
47
|
+
)
|
|
45
48
|
from web3.middleware.base import (
|
|
46
49
|
Web3Middleware,
|
|
47
50
|
)
|
|
@@ -71,7 +74,8 @@ else:
|
|
|
71
74
|
|
|
72
75
|
|
|
73
76
|
def segment_count(start: int, stop: int, step: int = 5) -> Iterable[Tuple[int, int]]:
|
|
74
|
-
"""
|
|
77
|
+
"""
|
|
78
|
+
Creates a segment counting generator
|
|
75
79
|
|
|
76
80
|
The generator returns tuple pairs of integers
|
|
77
81
|
that correspond to segments in the provided range.
|
|
@@ -84,6 +88,7 @@ def segment_count(start: int, stop: int, step: int = 5) -> Iterable[Tuple[int, i
|
|
|
84
88
|
|
|
85
89
|
|
|
86
90
|
Example:
|
|
91
|
+
-------
|
|
87
92
|
|
|
88
93
|
>>> segment_counter = segment_count(start=0, stop=10, step=3)
|
|
89
94
|
>>> next(segment_counter)
|
|
@@ -94,6 +99,7 @@ def segment_count(start: int, stop: int, step: int = 5) -> Iterable[Tuple[int, i
|
|
|
94
99
|
(6, 9)
|
|
95
100
|
>>> next(segment_counter) # Remainder is also returned
|
|
96
101
|
(9, 10)
|
|
102
|
+
|
|
97
103
|
"""
|
|
98
104
|
return gen_bounded_segments(start, stop, step)
|
|
99
105
|
|
|
@@ -104,10 +110,9 @@ def gen_bounded_segments(start: int, stop: int, step: int) -> Iterable[Tuple[int
|
|
|
104
110
|
if start + step >= stop:
|
|
105
111
|
yield (start, stop)
|
|
106
112
|
return
|
|
107
|
-
|
|
113
|
+
yield from zip(
|
|
108
114
|
range(start, stop - step + 1, step), range(start + step, stop + 1, step)
|
|
109
|
-
)
|
|
110
|
-
yield segment
|
|
115
|
+
)
|
|
111
116
|
|
|
112
117
|
remainder = (stop - start) % step
|
|
113
118
|
# Handle the remainder
|
|
@@ -118,13 +123,14 @@ def gen_bounded_segments(start: int, stop: int, step: int) -> Iterable[Tuple[int
|
|
|
118
123
|
def block_ranges(
|
|
119
124
|
start_block: BlockNumber, last_block: Optional[BlockNumber], step: int = 5
|
|
120
125
|
) -> Iterable[Tuple[BlockNumber, BlockNumber]]:
|
|
121
|
-
"""
|
|
126
|
+
"""
|
|
127
|
+
Returns 2-tuple ranges describing ranges of block from start_block to last_block
|
|
122
128
|
|
|
123
129
|
Ranges do not overlap to facilitate use as ``toBlock``, ``fromBlock``
|
|
124
130
|
json-rpc arguments, which are both inclusive.
|
|
125
131
|
"""
|
|
126
132
|
if last_block is not None and start_block > last_block:
|
|
127
|
-
raise
|
|
133
|
+
raise Web3TypeError(
|
|
128
134
|
"Incompatible start and stop arguments.",
|
|
129
135
|
"Start must be less than or equal to stop.",
|
|
130
136
|
)
|
|
@@ -138,7 +144,8 @@ def block_ranges(
|
|
|
138
144
|
def iter_latest_block(
|
|
139
145
|
w3: "Web3", to_block: Optional[Union[BlockNumber, LatestBlockParam]] = None
|
|
140
146
|
) -> Iterable[BlockNumber]:
|
|
141
|
-
"""
|
|
147
|
+
"""
|
|
148
|
+
Returns a generator that dispenses the latest block, if
|
|
142
149
|
any new blocks have been mined since last iteration.
|
|
143
150
|
|
|
144
151
|
If there are no new blocks or the latest block is greater than
|
|
@@ -160,8 +167,7 @@ def iter_latest_block(
|
|
|
160
167
|
|
|
161
168
|
while True:
|
|
162
169
|
latest_block = w3.eth.block_number
|
|
163
|
-
|
|
164
|
-
if is_bounded_range and latest_block > to_block: # type: ignore
|
|
170
|
+
if is_bounded_range and latest_block > to_block:
|
|
165
171
|
yield None
|
|
166
172
|
# No new blocks since last iteration.
|
|
167
173
|
if _last is not None and _last == latest_block:
|
|
@@ -176,7 +182,8 @@ def iter_latest_block_ranges(
|
|
|
176
182
|
from_block: BlockNumber,
|
|
177
183
|
to_block: Optional[Union[BlockNumber, LatestBlockParam]] = None,
|
|
178
184
|
) -> Iterable[Tuple[Optional[BlockNumber], Optional[BlockNumber]]]:
|
|
179
|
-
"""
|
|
185
|
+
"""
|
|
186
|
+
Returns an iterator unloading ranges of available blocks
|
|
180
187
|
|
|
181
188
|
starting from `fromBlock` to the latest mined block,
|
|
182
189
|
until reaching toBlock. e.g.:
|
|
@@ -212,7 +219,8 @@ def get_logs_multipart(
|
|
|
212
219
|
topics: List[Optional[Union[_Hash32, List[_Hash32]]]],
|
|
213
220
|
max_blocks: int,
|
|
214
221
|
) -> Iterable[List[LogReceipt]]:
|
|
215
|
-
"""
|
|
222
|
+
"""
|
|
223
|
+
Used to break up requests to ``eth_getLogs``
|
|
216
224
|
|
|
217
225
|
The getLog request is partitioned into multiple calls of the max number of blocks
|
|
218
226
|
``max_blocks``.
|
|
@@ -247,9 +255,8 @@ class RequestLogs:
|
|
|
247
255
|
if from_block is None or from_block == "latest":
|
|
248
256
|
self._from_block = BlockNumber(w3.eth.block_number + 1)
|
|
249
257
|
elif is_string(from_block) and is_hex(from_block):
|
|
250
|
-
self._from_block = BlockNumber(hex_to_integer(from_block))
|
|
258
|
+
self._from_block = BlockNumber(hex_to_integer(from_block))
|
|
251
259
|
else:
|
|
252
|
-
# cast b/c LatestBlockParam is handled above
|
|
253
260
|
self._from_block = from_block
|
|
254
261
|
self._to_block = to_block
|
|
255
262
|
self.filter_changes = self._get_filter_changes()
|
|
@@ -265,7 +272,7 @@ class RequestLogs:
|
|
|
265
272
|
elif self._to_block == "latest":
|
|
266
273
|
to_block = self.w3.eth.block_number
|
|
267
274
|
elif is_string(self._to_block) and is_hex(self._to_block):
|
|
268
|
-
to_block = BlockNumber(hex_to_integer(self._to_block))
|
|
275
|
+
to_block = BlockNumber(hex_to_integer(self._to_block))
|
|
269
276
|
else:
|
|
270
277
|
to_block = self._to_block
|
|
271
278
|
|
|
@@ -352,7 +359,8 @@ def block_hashes_in_range(
|
|
|
352
359
|
async def async_iter_latest_block(
|
|
353
360
|
w3: "AsyncWeb3", to_block: Optional[Union[BlockNumber, LatestBlockParam]] = None
|
|
354
361
|
) -> AsyncIterable[BlockNumber]:
|
|
355
|
-
"""
|
|
362
|
+
"""
|
|
363
|
+
Returns a generator that dispenses the latest block, if
|
|
356
364
|
any new blocks have been mined since last iteration.
|
|
357
365
|
|
|
358
366
|
If there are no new blocks or the latest block is greater than
|
|
@@ -390,7 +398,8 @@ async def async_iter_latest_block_ranges(
|
|
|
390
398
|
from_block: BlockNumber,
|
|
391
399
|
to_block: Optional[Union[BlockNumber, LatestBlockParam]] = None,
|
|
392
400
|
) -> AsyncIterable[Tuple[Optional[BlockNumber], Optional[BlockNumber]]]:
|
|
393
|
-
"""
|
|
401
|
+
"""
|
|
402
|
+
Returns an iterator unloading ranges of available blocks
|
|
394
403
|
|
|
395
404
|
starting from `from_block` to the latest mined block,
|
|
396
405
|
until reaching to_block. e.g.:
|
|
@@ -423,7 +432,8 @@ async def async_get_logs_multipart(
|
|
|
423
432
|
topics: List[Optional[Union[_Hash32, List[_Hash32]]]],
|
|
424
433
|
max_blocks: int,
|
|
425
434
|
) -> AsyncIterable[List[LogReceipt]]:
|
|
426
|
-
"""
|
|
435
|
+
"""
|
|
436
|
+
Used to break up requests to ``eth_getLogs``
|
|
427
437
|
|
|
428
438
|
The getLog request is partitioned into multiple calls of the max number of blocks
|
|
429
439
|
``max_blocks``.
|
|
@@ -579,6 +589,10 @@ SyncFilter = Union[RequestLogs, RequestBlocks]
|
|
|
579
589
|
AsyncFilter = Union[AsyncRequestLogs, AsyncRequestBlocks]
|
|
580
590
|
|
|
581
591
|
|
|
592
|
+
def _simulate_rpc_response_with_result(filter_id: str) -> "RPCResponse":
|
|
593
|
+
return {"jsonrpc": "2.0", "id": -1, "result": filter_id}
|
|
594
|
+
|
|
595
|
+
|
|
582
596
|
class LocalFilterMiddleware(Web3Middleware):
|
|
583
597
|
def __init__(self, w3: Union["Web3", "AsyncWeb3"]):
|
|
584
598
|
self.filters: Dict[str, SyncFilter] = {}
|
|
@@ -605,7 +619,7 @@ class LocalFilterMiddleware(Web3Middleware):
|
|
|
605
619
|
raise NotImplementedError(method)
|
|
606
620
|
|
|
607
621
|
self.filters[filter_id] = _filter
|
|
608
|
-
return
|
|
622
|
+
return _simulate_rpc_response_with_result(filter_id)
|
|
609
623
|
|
|
610
624
|
elif method in FILTER_CHANGES_METHODS:
|
|
611
625
|
_filter_id = params[0]
|
|
@@ -616,12 +630,16 @@ class LocalFilterMiddleware(Web3Middleware):
|
|
|
616
630
|
|
|
617
631
|
_filter = self.filters[_filter_id]
|
|
618
632
|
if method == RPC.eth_getFilterChanges:
|
|
619
|
-
return
|
|
633
|
+
return _simulate_rpc_response_with_result(
|
|
634
|
+
next(_filter.filter_changes) # type: ignore
|
|
635
|
+
)
|
|
620
636
|
|
|
621
637
|
elif method == RPC.eth_getFilterLogs:
|
|
622
638
|
# type ignored b/c logic prevents RequestBlocks which
|
|
623
639
|
# doesn't implement get_logs
|
|
624
|
-
return
|
|
640
|
+
return _simulate_rpc_response_with_result(
|
|
641
|
+
_filter.get_logs() # type: ignore
|
|
642
|
+
)
|
|
625
643
|
else:
|
|
626
644
|
raise NotImplementedError(method)
|
|
627
645
|
else:
|
|
@@ -653,7 +671,7 @@ class LocalFilterMiddleware(Web3Middleware):
|
|
|
653
671
|
raise NotImplementedError(method)
|
|
654
672
|
|
|
655
673
|
self.async_filters[filter_id] = _filter
|
|
656
|
-
return
|
|
674
|
+
return _simulate_rpc_response_with_result(filter_id)
|
|
657
675
|
|
|
658
676
|
elif method in FILTER_CHANGES_METHODS:
|
|
659
677
|
_filter_id = params[0]
|
|
@@ -664,12 +682,16 @@ class LocalFilterMiddleware(Web3Middleware):
|
|
|
664
682
|
|
|
665
683
|
_filter = self.async_filters[_filter_id]
|
|
666
684
|
if method == RPC.eth_getFilterChanges:
|
|
667
|
-
return
|
|
685
|
+
return _simulate_rpc_response_with_result(
|
|
686
|
+
await _filter.filter_changes.__anext__() # type: ignore
|
|
687
|
+
)
|
|
668
688
|
|
|
669
689
|
elif method == RPC.eth_getFilterLogs:
|
|
670
690
|
# type ignored b/c logic prevents RequestBlocks which
|
|
671
691
|
# doesn't implement get_logs
|
|
672
|
-
return
|
|
692
|
+
return _simulate_rpc_response_with_result(
|
|
693
|
+
await _filter.get_logs() # type: ignore
|
|
694
|
+
)
|
|
673
695
|
else:
|
|
674
696
|
raise NotImplementedError(method)
|
|
675
697
|
else:
|
web3/middleware/formatting.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import (
|
|
|
3
3
|
Any,
|
|
4
4
|
Callable,
|
|
5
5
|
Coroutine,
|
|
6
|
+
Literal,
|
|
6
7
|
Optional,
|
|
7
8
|
Union,
|
|
8
9
|
cast,
|
|
@@ -14,6 +15,9 @@ from eth_utils.toolz import (
|
|
|
14
15
|
merge,
|
|
15
16
|
)
|
|
16
17
|
|
|
18
|
+
from web3.exceptions import (
|
|
19
|
+
Web3ValueError,
|
|
20
|
+
)
|
|
17
21
|
from web3.middleware.base import (
|
|
18
22
|
Web3MiddlewareBuilder,
|
|
19
23
|
)
|
|
@@ -21,7 +25,6 @@ from web3.types import (
|
|
|
21
25
|
EthSubscriptionParams,
|
|
22
26
|
Formatters,
|
|
23
27
|
FormattersDict,
|
|
24
|
-
Literal,
|
|
25
28
|
RPCEndpoint,
|
|
26
29
|
RPCResponse,
|
|
27
30
|
)
|
|
@@ -118,7 +121,7 @@ class FormattingMiddlewareBuilder(Web3MiddlewareBuilder):
|
|
|
118
121
|
if (
|
|
119
122
|
sync_formatters_builder is None and async_formatters_builder is not None
|
|
120
123
|
) or (sync_formatters_builder is not None and async_formatters_builder is None):
|
|
121
|
-
raise
|
|
124
|
+
raise Web3ValueError(
|
|
122
125
|
"Must specify both sync_formatters_builder and async_formatters_builder"
|
|
123
126
|
)
|
|
124
127
|
|
|
@@ -128,7 +131,7 @@ class FormattingMiddlewareBuilder(Web3MiddlewareBuilder):
|
|
|
128
131
|
or result_formatters is not None
|
|
129
132
|
or error_formatters is not None
|
|
130
133
|
):
|
|
131
|
-
raise
|
|
134
|
+
raise Web3ValueError(
|
|
132
135
|
"Cannot specify formatters_builder and formatters at the same time"
|
|
133
136
|
)
|
|
134
137
|
|
web3/middleware/names.py
CHANGED
|
@@ -31,6 +31,9 @@ from .._utils.abi import (
|
|
|
31
31
|
from .._utils.formatters import (
|
|
32
32
|
recursive_map,
|
|
33
33
|
)
|
|
34
|
+
from ..exceptions import (
|
|
35
|
+
Web3TypeError,
|
|
36
|
+
)
|
|
34
37
|
from .base import (
|
|
35
38
|
Web3Middleware,
|
|
36
39
|
)
|
|
@@ -92,7 +95,7 @@ async def async_apply_ens_to_address_conversion(
|
|
|
92
95
|
return (formatted_params_dict, *params[1:])
|
|
93
96
|
|
|
94
97
|
else:
|
|
95
|
-
raise
|
|
98
|
+
raise Web3TypeError(
|
|
96
99
|
f"ABI definitions must be a list or dictionary, "
|
|
97
100
|
f"got {abi_types_for_method!r}"
|
|
98
101
|
)
|
web3/middleware/signing.py
CHANGED
|
@@ -55,6 +55,9 @@ from web3._utils.transactions import (
|
|
|
55
55
|
fill_nonce,
|
|
56
56
|
fill_transaction_defaults,
|
|
57
57
|
)
|
|
58
|
+
from web3.exceptions import (
|
|
59
|
+
Web3TypeError,
|
|
60
|
+
)
|
|
58
61
|
from web3.middleware.base import (
|
|
59
62
|
Web3MiddlewareBuilder,
|
|
60
63
|
)
|
|
@@ -108,7 +111,7 @@ def gen_normalized_accounts(
|
|
|
108
111
|
|
|
109
112
|
@singledispatch
|
|
110
113
|
def to_account(val: Any) -> LocalAccount:
|
|
111
|
-
raise
|
|
114
|
+
raise Web3TypeError(
|
|
112
115
|
"key must be one of the types: "
|
|
113
116
|
"eth_keys.datatype.PrivateKey, eth_account.signers.local.LocalAccount, "
|
|
114
117
|
"or raw private key as a hex string or byte string. "
|
|
@@ -132,7 +135,8 @@ to_account.register(bytes, private_key_to_account)
|
|
|
132
135
|
|
|
133
136
|
|
|
134
137
|
def format_transaction(transaction: TxParams) -> TxParams:
|
|
135
|
-
"""
|
|
138
|
+
"""
|
|
139
|
+
Format transaction so that it can be used correctly in the signing middleware.
|
|
136
140
|
|
|
137
141
|
Converts bytes to hex strings and other types that can be passed to
|
|
138
142
|
the underlying layers. Also has the effect of normalizing 'from' for
|
|
@@ -178,7 +182,7 @@ class SignAndSendRawMiddlewareBuilder(Web3MiddlewareBuilder):
|
|
|
178
182
|
return method, params
|
|
179
183
|
else:
|
|
180
184
|
account = self._accounts[to_checksum_address(tx_from)]
|
|
181
|
-
raw_tx = account.sign_transaction(filled_transaction).
|
|
185
|
+
raw_tx = account.sign_transaction(filled_transaction).raw_transaction
|
|
182
186
|
|
|
183
187
|
return (
|
|
184
188
|
RPCEndpoint("eth_sendRawTransaction"),
|
|
@@ -207,7 +211,7 @@ class SignAndSendRawMiddlewareBuilder(Web3MiddlewareBuilder):
|
|
|
207
211
|
return method, params
|
|
208
212
|
else:
|
|
209
213
|
account = self._accounts[to_checksum_address(tx_from)]
|
|
210
|
-
raw_tx = account.sign_transaction(filled_transaction).
|
|
214
|
+
raw_tx = account.sign_transaction(filled_transaction).raw_transaction
|
|
211
215
|
|
|
212
216
|
return (
|
|
213
217
|
RPCEndpoint("eth_sendRawTransaction"),
|
web3/middleware/stalecheck.py
CHANGED
|
@@ -16,6 +16,7 @@ from toolz import (
|
|
|
16
16
|
|
|
17
17
|
from web3.exceptions import (
|
|
18
18
|
StaleBlockchain,
|
|
19
|
+
Web3ValueError,
|
|
19
20
|
)
|
|
20
21
|
from web3.middleware.base import (
|
|
21
22
|
Web3Middleware,
|
|
@@ -54,7 +55,7 @@ class StalecheckMiddlewareBuilder(Web3MiddlewareBuilder):
|
|
|
54
55
|
skip_stalecheck_for_methods: Collection[str] = SKIP_STALECHECK_FOR_METHODS,
|
|
55
56
|
) -> Web3Middleware:
|
|
56
57
|
if allowable_delay <= 0:
|
|
57
|
-
raise
|
|
58
|
+
raise Web3ValueError(
|
|
58
59
|
"You must set a positive allowable_delay in seconds for this middleware"
|
|
59
60
|
)
|
|
60
61
|
middleware = StalecheckMiddlewareBuilder(w3)
|