web3 7.0.0b5__py3-none-any.whl → 7.0.0b6__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/batching.py +217 -0
- web3/_utils/caching.py +26 -2
- web3/_utils/compat/__init__.py +1 -0
- web3/_utils/events.py +2 -2
- web3/_utils/module_testing/eth_module.py +10 -10
- web3/_utils/module_testing/module_testing_utils.py +13 -0
- web3/_utils/module_testing/web3_module.py +438 -17
- web3/contract/utils.py +112 -4
- web3/eth/async_eth.py +5 -4
- web3/eth/eth.py +5 -4
- web3/exceptions.py +20 -0
- web3/gas_strategies/time_based.py +2 -2
- web3/main.py +21 -9
- web3/manager.py +113 -8
- web3/method.py +29 -9
- web3/middleware/base.py +43 -0
- web3/module.py +47 -7
- web3/providers/async_base.py +55 -23
- web3/providers/base.py +59 -26
- web3/providers/ipc.py +23 -8
- web3/providers/legacy_websocket.py +26 -1
- web3/providers/persistent/async_ipc.py +60 -76
- web3/providers/persistent/persistent.py +134 -10
- web3/providers/persistent/request_processor.py +98 -14
- web3/providers/persistent/websocket.py +43 -66
- web3/providers/rpc/async_rpc.py +19 -1
- web3/providers/rpc/rpc.py +19 -1
- web3/types.py +7 -1
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/LICENSE +1 -1
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/METADATA +31 -20
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/RECORD +33 -32
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/WHEEL +0 -0
- {web3-7.0.0b5.dist-info → web3-7.0.0b6.dist-info}/top_level.txt +0 -0
web3/_utils/batching.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
from copy import (
|
|
2
|
+
copy,
|
|
3
|
+
)
|
|
4
|
+
from types import (
|
|
5
|
+
TracebackType,
|
|
6
|
+
)
|
|
7
|
+
from typing import (
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Any,
|
|
10
|
+
Callable,
|
|
11
|
+
Coroutine,
|
|
12
|
+
Dict,
|
|
13
|
+
Generic,
|
|
14
|
+
List,
|
|
15
|
+
Sequence,
|
|
16
|
+
Tuple,
|
|
17
|
+
Type,
|
|
18
|
+
Union,
|
|
19
|
+
cast,
|
|
20
|
+
)
|
|
21
|
+
import warnings
|
|
22
|
+
|
|
23
|
+
from web3._utils.compat import (
|
|
24
|
+
Self,
|
|
25
|
+
)
|
|
26
|
+
from web3.contract.async_contract import (
|
|
27
|
+
AsyncContractFunction,
|
|
28
|
+
)
|
|
29
|
+
from web3.contract.contract import (
|
|
30
|
+
ContractFunction,
|
|
31
|
+
)
|
|
32
|
+
from web3.exceptions import (
|
|
33
|
+
Web3ValueError,
|
|
34
|
+
)
|
|
35
|
+
from web3.types import (
|
|
36
|
+
TFunc,
|
|
37
|
+
TReturn,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from web3 import ( # noqa: F401
|
|
42
|
+
AsyncWeb3,
|
|
43
|
+
Web3,
|
|
44
|
+
)
|
|
45
|
+
from web3.method import ( # noqa: F401
|
|
46
|
+
Method,
|
|
47
|
+
)
|
|
48
|
+
from web3.providers import ( # noqa: F401
|
|
49
|
+
PersistentConnectionProvider,
|
|
50
|
+
)
|
|
51
|
+
from web3.providers.async_base import ( # noqa: F401
|
|
52
|
+
AsyncJSONBaseProvider,
|
|
53
|
+
)
|
|
54
|
+
from web3.providers.base import ( # noqa: F401
|
|
55
|
+
JSONBaseProvider,
|
|
56
|
+
)
|
|
57
|
+
from web3.types import ( # noqa: F401
|
|
58
|
+
RPCEndpoint,
|
|
59
|
+
RPCResponse,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
BATCH_REQUEST_ID = "batch_request" # for use as the cache key for batch requests
|
|
64
|
+
|
|
65
|
+
BatchRequestInformation = Tuple[Tuple["RPCEndpoint", Any], Sequence[Any]]
|
|
66
|
+
RPC_METHODS_UNSUPPORTED_DURING_BATCH = {
|
|
67
|
+
"eth_subscribe",
|
|
68
|
+
"eth_unsubscribe",
|
|
69
|
+
"eth_sendRawTransaction",
|
|
70
|
+
"eth_sendTransaction",
|
|
71
|
+
"eth_signTransaction",
|
|
72
|
+
"eth_sign",
|
|
73
|
+
"eth_signTypedData",
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class RequestBatcher(Generic[TFunc]):
|
|
78
|
+
def __init__(self, web3: Union["AsyncWeb3", "Web3"]) -> None:
|
|
79
|
+
self.web3 = web3
|
|
80
|
+
self._requests_info: List[BatchRequestInformation] = []
|
|
81
|
+
self._async_requests_info: List[
|
|
82
|
+
Coroutine[Any, Any, BatchRequestInformation]
|
|
83
|
+
] = []
|
|
84
|
+
self._initialize_batching()
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def _provider(self) -> Union["JSONBaseProvider", "AsyncJSONBaseProvider"]:
|
|
88
|
+
return (
|
|
89
|
+
cast("AsyncJSONBaseProvider", self.web3.provider)
|
|
90
|
+
if self.web3.provider.is_async
|
|
91
|
+
else cast("JSONBaseProvider", self.web3.provider)
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def _validate_is_batching(self) -> None:
|
|
95
|
+
if not self._provider._is_batching:
|
|
96
|
+
raise Web3ValueError(
|
|
97
|
+
"Batch has already been executed or cancelled. Create a new batch to "
|
|
98
|
+
"issue batched requests."
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def _initialize_batching(self) -> None:
|
|
102
|
+
self._provider._is_batching = True
|
|
103
|
+
self.clear()
|
|
104
|
+
|
|
105
|
+
def _end_batching(self) -> None:
|
|
106
|
+
self.clear()
|
|
107
|
+
self._provider._is_batching = False
|
|
108
|
+
if self._provider.has_persistent_connection:
|
|
109
|
+
provider = cast("PersistentConnectionProvider", self._provider)
|
|
110
|
+
provider._batch_request_counter = None
|
|
111
|
+
|
|
112
|
+
def add(self, batch_payload: TReturn) -> None:
|
|
113
|
+
self._validate_is_batching()
|
|
114
|
+
|
|
115
|
+
if isinstance(batch_payload, (ContractFunction, AsyncContractFunction)):
|
|
116
|
+
batch_payload = batch_payload.call() # type: ignore
|
|
117
|
+
|
|
118
|
+
# When batching, we don't make a request. Instead, we will get the request
|
|
119
|
+
# information and store it in the `_requests_info` list. So we have to cast the
|
|
120
|
+
# apparent "request" into the BatchRequestInformation type.
|
|
121
|
+
if self._provider.is_async:
|
|
122
|
+
self._async_requests_info.append(
|
|
123
|
+
cast(Coroutine[Any, Any, BatchRequestInformation], batch_payload)
|
|
124
|
+
)
|
|
125
|
+
else:
|
|
126
|
+
self._requests_info.append(cast(BatchRequestInformation, batch_payload))
|
|
127
|
+
|
|
128
|
+
def add_mapping(
|
|
129
|
+
self,
|
|
130
|
+
batch_payload: Dict[
|
|
131
|
+
Union[
|
|
132
|
+
"Method[Callable[..., Any]]",
|
|
133
|
+
Callable[..., Any],
|
|
134
|
+
ContractFunction,
|
|
135
|
+
AsyncContractFunction,
|
|
136
|
+
],
|
|
137
|
+
List[Any],
|
|
138
|
+
],
|
|
139
|
+
) -> None:
|
|
140
|
+
self._validate_is_batching()
|
|
141
|
+
for method, params in batch_payload.items():
|
|
142
|
+
for param in params:
|
|
143
|
+
self.add(method(param))
|
|
144
|
+
|
|
145
|
+
def execute(self) -> List["RPCResponse"]:
|
|
146
|
+
self._validate_is_batching()
|
|
147
|
+
responses = self.web3.manager._make_batch_request(self._requests_info)
|
|
148
|
+
self._end_batching()
|
|
149
|
+
return responses
|
|
150
|
+
|
|
151
|
+
def clear(self) -> None:
|
|
152
|
+
self._requests_info = []
|
|
153
|
+
self._async_requests_info = []
|
|
154
|
+
if self._provider.has_persistent_connection:
|
|
155
|
+
provider = cast("PersistentConnectionProvider", self._provider)
|
|
156
|
+
provider._batch_request_counter = next(copy(provider.request_counter))
|
|
157
|
+
|
|
158
|
+
def cancel(self) -> None:
|
|
159
|
+
self._end_batching()
|
|
160
|
+
|
|
161
|
+
# -- context manager -- #
|
|
162
|
+
|
|
163
|
+
def __enter__(self) -> Self:
|
|
164
|
+
self._initialize_batching()
|
|
165
|
+
return self
|
|
166
|
+
|
|
167
|
+
def __exit__(
|
|
168
|
+
self,
|
|
169
|
+
exc_type: Type[BaseException],
|
|
170
|
+
exc_val: BaseException,
|
|
171
|
+
exc_tb: TracebackType,
|
|
172
|
+
) -> None:
|
|
173
|
+
self._end_batching()
|
|
174
|
+
|
|
175
|
+
# -- async -- #
|
|
176
|
+
|
|
177
|
+
async def async_execute(self) -> List["RPCResponse"]:
|
|
178
|
+
self._validate_is_batching()
|
|
179
|
+
responses = await self.web3.manager._async_make_batch_request(
|
|
180
|
+
self._async_requests_info
|
|
181
|
+
)
|
|
182
|
+
self._end_batching()
|
|
183
|
+
return responses
|
|
184
|
+
|
|
185
|
+
# -- async context manager -- #
|
|
186
|
+
|
|
187
|
+
async def __aenter__(self) -> Self:
|
|
188
|
+
self._initialize_batching()
|
|
189
|
+
return self
|
|
190
|
+
|
|
191
|
+
async def __aexit__(
|
|
192
|
+
self,
|
|
193
|
+
exc_type: Type[BaseException],
|
|
194
|
+
exc_val: BaseException,
|
|
195
|
+
exc_tb: TracebackType,
|
|
196
|
+
) -> None:
|
|
197
|
+
self._end_batching()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def sort_batch_response_by_response_ids(
|
|
201
|
+
responses: List["RPCResponse"],
|
|
202
|
+
) -> List["RPCResponse"]:
|
|
203
|
+
if all(response.get("id") is not None for response in responses):
|
|
204
|
+
# If all responses have an `id`, sort them by `id, since the JSON-RPC 2.0 spec
|
|
205
|
+
# doesn't guarantee order.
|
|
206
|
+
return sorted(responses, key=lambda response: response["id"])
|
|
207
|
+
else:
|
|
208
|
+
# If any response is missing an `id`, which should only happen on particular
|
|
209
|
+
# errors, return them in the order they were received and hope that the
|
|
210
|
+
# provider is returning them in order. Issue a warning.
|
|
211
|
+
warnings.warn(
|
|
212
|
+
"Received batch response with missing `id` for one or more responses. "
|
|
213
|
+
"Relying on provider to return these responses in order.",
|
|
214
|
+
RuntimeWarning,
|
|
215
|
+
stacklevel=2,
|
|
216
|
+
)
|
|
217
|
+
return responses
|
web3/_utils/caching.py
CHANGED
|
@@ -6,10 +6,13 @@ from typing import (
|
|
|
6
6
|
Any,
|
|
7
7
|
Callable,
|
|
8
8
|
Coroutine,
|
|
9
|
+
Dict,
|
|
9
10
|
List,
|
|
11
|
+
Set,
|
|
10
12
|
Tuple,
|
|
11
13
|
TypeVar,
|
|
12
14
|
Union,
|
|
15
|
+
cast,
|
|
13
16
|
)
|
|
14
17
|
|
|
15
18
|
from eth_utils import (
|
|
@@ -69,7 +72,11 @@ class RequestInformation:
|
|
|
69
72
|
self,
|
|
70
73
|
method: "RPCEndpoint",
|
|
71
74
|
params: Any,
|
|
72
|
-
response_formatters: Tuple[
|
|
75
|
+
response_formatters: Tuple[
|
|
76
|
+
Union[Dict[str, Callable[..., Any]], Callable[..., Any]],
|
|
77
|
+
Callable[..., Any],
|
|
78
|
+
Callable[..., Any],
|
|
79
|
+
],
|
|
73
80
|
subscription_id: str = None,
|
|
74
81
|
):
|
|
75
82
|
self.method = method
|
|
@@ -87,7 +94,24 @@ def is_cacheable_request(
|
|
|
87
94
|
return False
|
|
88
95
|
|
|
89
96
|
|
|
90
|
-
# -- request caching
|
|
97
|
+
# -- request caching -- #
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
CACHEABLE_REQUESTS = cast(
|
|
101
|
+
Set["RPCEndpoint"],
|
|
102
|
+
(
|
|
103
|
+
"eth_chainId",
|
|
104
|
+
"eth_getBlockByHash",
|
|
105
|
+
"eth_getBlockTransactionCountByHash",
|
|
106
|
+
"eth_getRawTransactionByHash",
|
|
107
|
+
"eth_getTransactionByBlockHashAndIndex",
|
|
108
|
+
"eth_getTransactionByHash",
|
|
109
|
+
"eth_getUncleByBlockHashAndIndex",
|
|
110
|
+
"eth_getUncleCountByBlockHash",
|
|
111
|
+
"net_version",
|
|
112
|
+
"web3_clientVersion",
|
|
113
|
+
),
|
|
114
|
+
)
|
|
91
115
|
|
|
92
116
|
|
|
93
117
|
def _should_cache_response(response: "RPCResponse") -> bool:
|
web3/_utils/compat/__init__.py
CHANGED
web3/_utils/events.py
CHANGED
|
@@ -444,8 +444,8 @@ class EventFilterBuilder(BaseEventFilterBuilder):
|
|
|
444
444
|
if not isinstance(w3, web3.Web3):
|
|
445
445
|
raise Web3ValueError(f"Invalid web3 argument: got: {w3!r}")
|
|
446
446
|
|
|
447
|
-
for arg in
|
|
448
|
-
arg._immutable = True
|
|
447
|
+
for arg in self.args.values():
|
|
448
|
+
arg._immutable = True
|
|
449
449
|
self._immutable = True
|
|
450
450
|
|
|
451
451
|
log_filter = cast("LogFilter", w3.eth.filter(self.filter_params))
|
|
@@ -220,7 +220,7 @@ class AsyncEthModuleTest:
|
|
|
220
220
|
txn_hash = await async_w3.eth.send_transaction(txn_params)
|
|
221
221
|
|
|
222
222
|
modified_txn_hash = await async_w3.eth.modify_transaction(
|
|
223
|
-
txn_hash, gasPrice=(cast(
|
|
223
|
+
txn_hash, gasPrice=(cast(Wei, txn_params["gasPrice"] * 2)), value=Wei(2)
|
|
224
224
|
)
|
|
225
225
|
modified_txn = await async_w3.eth.get_transaction(modified_txn_hash)
|
|
226
226
|
|
|
@@ -252,9 +252,9 @@ class AsyncEthModuleTest:
|
|
|
252
252
|
|
|
253
253
|
modified_txn_hash = await async_w3.eth.modify_transaction(
|
|
254
254
|
txn_hash,
|
|
255
|
-
value=2,
|
|
256
|
-
maxPriorityFeePerGas=(cast(Wei, txn_params["maxPriorityFeePerGas"]
|
|
257
|
-
maxFeePerGas=(cast(Wei, txn_params["maxFeePerGas"]
|
|
255
|
+
value=Wei(2),
|
|
256
|
+
maxPriorityFeePerGas=(cast(Wei, txn_params["maxPriorityFeePerGas"] * 2)),
|
|
257
|
+
maxFeePerGas=(cast(Wei, txn_params["maxFeePerGas"] * 2)),
|
|
258
258
|
)
|
|
259
259
|
modified_txn = await async_w3.eth.get_transaction(modified_txn_hash)
|
|
260
260
|
|
|
@@ -721,7 +721,7 @@ class AsyncEthModuleTest:
|
|
|
721
721
|
"gasPrice": 10**9,
|
|
722
722
|
}
|
|
723
723
|
signed = keyfile_account.sign_transaction(txn)
|
|
724
|
-
txn_hash = await async_w3.eth.send_raw_transaction(signed.
|
|
724
|
+
txn_hash = await async_w3.eth.send_raw_transaction(signed.raw_transaction)
|
|
725
725
|
assert txn_hash == HexBytes(signed.hash)
|
|
726
726
|
|
|
727
727
|
@pytest.mark.asyncio
|
|
@@ -3657,7 +3657,7 @@ class EthModuleTest:
|
|
|
3657
3657
|
txn_hash = w3.eth.send_transaction(txn_params)
|
|
3658
3658
|
|
|
3659
3659
|
modified_txn_hash = w3.eth.modify_transaction(
|
|
3660
|
-
txn_hash, gasPrice=(cast(
|
|
3660
|
+
txn_hash, gasPrice=(cast(Wei, txn_params["gasPrice"] * 2)), value=Wei(2)
|
|
3661
3661
|
)
|
|
3662
3662
|
modified_txn = w3.eth.get_transaction(modified_txn_hash)
|
|
3663
3663
|
|
|
@@ -3686,9 +3686,9 @@ class EthModuleTest:
|
|
|
3686
3686
|
|
|
3687
3687
|
modified_txn_hash = w3.eth.modify_transaction(
|
|
3688
3688
|
txn_hash,
|
|
3689
|
-
value=2,
|
|
3690
|
-
maxPriorityFeePerGas=(cast(Wei, txn_params["maxPriorityFeePerGas"]
|
|
3691
|
-
maxFeePerGas=(cast(Wei, txn_params["maxFeePerGas"]
|
|
3689
|
+
value=Wei(2),
|
|
3690
|
+
maxPriorityFeePerGas=(cast(Wei, txn_params["maxPriorityFeePerGas"] * 2)),
|
|
3691
|
+
maxFeePerGas=(cast(Wei, txn_params["maxFeePerGas"] * 2)),
|
|
3692
3692
|
)
|
|
3693
3693
|
modified_txn = w3.eth.get_transaction(modified_txn_hash)
|
|
3694
3694
|
|
|
@@ -3720,7 +3720,7 @@ class EthModuleTest:
|
|
|
3720
3720
|
"gasPrice": 10**9,
|
|
3721
3721
|
}
|
|
3722
3722
|
signed = keyfile_account.sign_transaction(txn)
|
|
3723
|
-
txn_hash = w3.eth.send_raw_transaction(signed.
|
|
3723
|
+
txn_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
|
|
3724
3724
|
assert txn_hash == HexBytes(signed.hash)
|
|
3725
3725
|
|
|
3726
3726
|
def test_eth_call(self, w3: "Web3", math_contract: "Contract") -> None:
|
|
@@ -177,6 +177,12 @@ class WebSocketMessageStreamMock:
|
|
|
177
177
|
self.messages = deque(messages) if messages else deque()
|
|
178
178
|
self.raise_exception = raise_exception
|
|
179
179
|
|
|
180
|
+
def __await__(self) -> Generator[Any, Any, "Self"]:
|
|
181
|
+
async def __async_init__() -> "Self":
|
|
182
|
+
return self
|
|
183
|
+
|
|
184
|
+
return __async_init__().__await__()
|
|
185
|
+
|
|
180
186
|
def __aiter__(self) -> "Self":
|
|
181
187
|
return self
|
|
182
188
|
|
|
@@ -189,6 +195,13 @@ class WebSocketMessageStreamMock:
|
|
|
189
195
|
|
|
190
196
|
return self.messages.popleft()
|
|
191
197
|
|
|
198
|
+
@staticmethod
|
|
199
|
+
async def pong() -> Literal[False]:
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
async def connect(self) -> None:
|
|
203
|
+
pass
|
|
204
|
+
|
|
192
205
|
async def send(self, data: bytes) -> None:
|
|
193
206
|
pass
|
|
194
207
|
|