web3 7.0.0b2__py3-none-any.whl → 7.7.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 (144) hide show
  1. ens/__init__.py +13 -2
  2. ens/_normalization.py +4 -4
  3. ens/async_ens.py +27 -15
  4. ens/base_ens.py +3 -1
  5. ens/contract_data.py +2 -2
  6. ens/ens.py +10 -7
  7. ens/exceptions.py +16 -29
  8. ens/specs/nf.json +1 -1
  9. ens/specs/normalization_spec.json +1 -1
  10. ens/utils.py +24 -32
  11. web3/__init__.py +23 -12
  12. web3/_utils/abi.py +157 -263
  13. web3/_utils/async_transactions.py +34 -20
  14. web3/_utils/batching.py +217 -0
  15. web3/_utils/blocks.py +6 -2
  16. web3/_utils/caching/__init__.py +12 -0
  17. web3/_utils/caching/caching_utils.py +433 -0
  18. web3/_utils/caching/request_caching_validation.py +287 -0
  19. web3/_utils/compat/__init__.py +2 -3
  20. web3/_utils/contract_sources/compile_contracts.py +1 -1
  21. web3/_utils/contract_sources/contract_data/ambiguous_function_contract.py +42 -0
  22. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  23. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  24. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  25. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  26. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  27. web3/_utils/contract_sources/contract_data/event_contracts.py +50 -5
  28. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  29. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  30. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  31. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  32. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  33. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  34. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  35. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  36. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  37. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  38. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  39. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  40. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  41. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  42. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  43. web3/_utils/contracts.py +172 -220
  44. web3/_utils/datatypes.py +5 -1
  45. web3/_utils/decorators.py +6 -1
  46. web3/_utils/empty.py +1 -1
  47. web3/_utils/encoding.py +16 -12
  48. web3/_utils/error_formatters_utils.py +5 -3
  49. web3/_utils/events.py +78 -72
  50. web3/_utils/fee_utils.py +1 -3
  51. web3/_utils/filters.py +24 -22
  52. web3/_utils/formatters.py +2 -2
  53. web3/_utils/http.py +8 -2
  54. web3/_utils/http_session_manager.py +314 -0
  55. web3/_utils/math.py +14 -15
  56. web3/_utils/method_formatters.py +161 -34
  57. web3/_utils/module.py +2 -1
  58. web3/_utils/module_testing/__init__.py +3 -2
  59. web3/_utils/module_testing/eth_module.py +736 -583
  60. web3/_utils/module_testing/go_ethereum_debug_module.py +128 -0
  61. web3/_utils/module_testing/module_testing_utils.py +81 -24
  62. web3/_utils/module_testing/persistent_connection_provider.py +702 -220
  63. web3/_utils/module_testing/utils.py +114 -33
  64. web3/_utils/module_testing/web3_module.py +438 -17
  65. web3/_utils/normalizers.py +13 -11
  66. web3/_utils/rpc_abi.py +10 -22
  67. web3/_utils/threads.py +8 -7
  68. web3/_utils/transactions.py +32 -25
  69. web3/_utils/type_conversion.py +5 -1
  70. web3/_utils/validation.py +20 -17
  71. web3/beacon/__init__.py +5 -0
  72. web3/beacon/api_endpoints.py +3 -0
  73. web3/beacon/async_beacon.py +29 -6
  74. web3/beacon/beacon.py +24 -6
  75. web3/contract/__init__.py +7 -0
  76. web3/contract/async_contract.py +285 -82
  77. web3/contract/base_contract.py +556 -258
  78. web3/contract/contract.py +295 -84
  79. web3/contract/utils.py +251 -55
  80. web3/datastructures.py +49 -34
  81. web3/eth/__init__.py +7 -0
  82. web3/eth/async_eth.py +89 -69
  83. web3/eth/base_eth.py +7 -3
  84. web3/eth/eth.py +43 -66
  85. web3/exceptions.py +158 -83
  86. web3/gas_strategies/time_based.py +8 -6
  87. web3/geth.py +53 -184
  88. web3/main.py +77 -17
  89. web3/manager.py +362 -95
  90. web3/method.py +43 -15
  91. web3/middleware/__init__.py +17 -0
  92. web3/middleware/attrdict.py +12 -22
  93. web3/middleware/base.py +55 -2
  94. web3/middleware/filter.py +45 -23
  95. web3/middleware/formatting.py +6 -3
  96. web3/middleware/names.py +4 -1
  97. web3/middleware/signing.py +15 -6
  98. web3/middleware/stalecheck.py +2 -1
  99. web3/module.py +61 -25
  100. web3/providers/__init__.py +21 -0
  101. web3/providers/async_base.py +87 -32
  102. web3/providers/base.py +77 -32
  103. web3/providers/eth_tester/__init__.py +5 -0
  104. web3/providers/eth_tester/defaults.py +2 -55
  105. web3/providers/eth_tester/main.py +41 -15
  106. web3/providers/eth_tester/middleware.py +16 -17
  107. web3/providers/ipc.py +41 -17
  108. web3/providers/legacy_websocket.py +26 -1
  109. web3/providers/persistent/__init__.py +7 -0
  110. web3/providers/persistent/async_ipc.py +61 -121
  111. web3/providers/persistent/persistent.py +323 -16
  112. web3/providers/persistent/persistent_connection.py +54 -5
  113. web3/providers/persistent/request_processor.py +136 -56
  114. web3/providers/persistent/subscription_container.py +56 -0
  115. web3/providers/persistent/subscription_manager.py +233 -0
  116. web3/providers/persistent/websocket.py +29 -92
  117. web3/providers/rpc/__init__.py +5 -0
  118. web3/providers/rpc/async_rpc.py +73 -18
  119. web3/providers/rpc/rpc.py +73 -30
  120. web3/providers/rpc/utils.py +1 -13
  121. web3/scripts/install_pre_releases.py +33 -0
  122. web3/scripts/parse_pygeth_version.py +16 -0
  123. web3/testing.py +4 -4
  124. web3/tracing.py +9 -5
  125. web3/types.py +141 -74
  126. web3/utils/__init__.py +64 -5
  127. web3/utils/abi.py +790 -10
  128. web3/utils/address.py +8 -0
  129. web3/utils/async_exception_handling.py +20 -11
  130. web3/utils/caching.py +34 -4
  131. web3/utils/exception_handling.py +9 -12
  132. web3/utils/subscriptions.py +285 -0
  133. {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/LICENSE +1 -1
  134. web3-7.7.0.dist-info/METADATA +130 -0
  135. web3-7.7.0.dist-info/RECORD +171 -0
  136. {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/WHEEL +1 -1
  137. web3/_utils/caching.py +0 -155
  138. web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
  139. web3/_utils/module_testing/go_ethereum_personal_module.py +0 -300
  140. web3/_utils/request.py +0 -265
  141. web3-7.0.0b2.dist-info/METADATA +0 -106
  142. web3-7.0.0b2.dist-info/RECORD +0 -163
  143. /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
  144. {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/top_level.txt +0 -0
@@ -4,8 +4,10 @@ from typing import (
4
4
  Callable,
5
5
  Coroutine,
6
6
  Dict,
7
+ Literal,
7
8
  Optional,
8
9
  Union,
10
+ cast,
9
11
  )
10
12
 
11
13
  from eth_abi import (
@@ -18,9 +20,6 @@ from eth_utils import (
18
20
  is_bytes,
19
21
  )
20
22
 
21
- from web3._utils.compat import (
22
- Literal,
23
- )
24
23
  from web3.providers import (
25
24
  BaseProvider,
26
25
  )
@@ -33,6 +32,9 @@ from web3.types import (
33
32
  RPCResponse,
34
33
  )
35
34
 
35
+ from ...exceptions import (
36
+ Web3TypeError,
37
+ )
36
38
  from ...middleware import (
37
39
  async_combine_middleware,
38
40
  combine_middleware,
@@ -58,6 +60,7 @@ if TYPE_CHECKING:
58
60
 
59
61
 
60
62
  class AsyncEthereumTesterProvider(AsyncBaseProvider):
63
+ _current_request_id = 0
61
64
  _middleware = (
62
65
  default_transaction_fields_middleware,
63
66
  ethereum_tester_middleware,
@@ -98,13 +101,22 @@ class AsyncEthereumTesterProvider(AsyncBaseProvider):
98
101
  return self._request_func_cache[-1]
99
102
 
100
103
  async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
101
- return _make_request(method, params, self.api_endpoints, self.ethereum_tester)
104
+ response = _make_request(
105
+ method,
106
+ params,
107
+ self.api_endpoints,
108
+ self.ethereum_tester,
109
+ repr(self._current_request_id),
110
+ )
111
+ self._current_request_id += 1
112
+ return response
102
113
 
103
114
  async def is_connected(self, show_traceback: bool = False) -> Literal[True]:
104
115
  return True
105
116
 
106
117
 
107
118
  class EthereumTesterProvider(BaseProvider):
119
+ _current_request_id = 0
108
120
  _middleware = (
109
121
  default_transaction_fields_middleware,
110
122
  ethereum_tester_middleware,
@@ -133,7 +145,7 @@ class EthereumTesterProvider(BaseProvider):
133
145
  elif isinstance(ethereum_tester, BaseChainBackend):
134
146
  self.ethereum_tester = EthereumTester(ethereum_tester)
135
147
  else:
136
- raise TypeError(
148
+ raise Web3TypeError(
137
149
  "Expected ethereum_tester to be of type `eth_tester.EthereumTester` or "
138
150
  "a subclass of `eth_tester.backends.base.BaseChainBackend`, "
139
151
  f"instead received {type(ethereum_tester)}. "
@@ -172,23 +184,32 @@ class EthereumTesterProvider(BaseProvider):
172
184
  return self._request_func_cache[-1]
173
185
 
174
186
  def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
175
- return _make_request(method, params, self.api_endpoints, self.ethereum_tester)
187
+ response = _make_request(
188
+ method,
189
+ params,
190
+ self.api_endpoints,
191
+ self.ethereum_tester,
192
+ repr(self._current_request_id),
193
+ )
194
+ self._current_request_id += 1
195
+ return response
176
196
 
177
197
  def is_connected(self, show_traceback: bool = False) -> Literal[True]:
178
198
  return True
179
199
 
180
200
 
181
- def _make_response(result: Any, message: str = "") -> RPCResponse:
201
+ def _make_response(result: Any, response_id: str, message: str = "") -> RPCResponse:
182
202
  if isinstance(result, Exception):
183
- return RPCResponse(
203
+ return cast(
204
+ RPCResponse,
184
205
  {
185
- "id": 1,
206
+ "id": response_id,
186
207
  "jsonrpc": "2.0",
187
- "error": RPCError({"code": -32601, "message": message}),
188
- }
208
+ "error": cast(RPCError, {"code": -32601, "message": message}),
209
+ },
189
210
  )
190
211
 
191
- return RPCResponse({"id": 1, "jsonrpc": "2.0", "result": result})
212
+ return cast(RPCResponse, {"id": response_id, "jsonrpc": "2.0", "result": result})
192
213
 
193
214
 
194
215
  def _make_request(
@@ -196,6 +217,7 @@ def _make_request(
196
217
  params: Any,
197
218
  api_endpoints: Dict[str, Dict[str, Any]],
198
219
  ethereum_tester_instance: "EthereumTester",
220
+ request_id: str,
199
221
  ) -> RPCResponse:
200
222
  # do not import eth_tester derivatives until runtime,
201
223
  # it is not a default dependency
@@ -208,11 +230,15 @@ def _make_request(
208
230
  try:
209
231
  delegator = api_endpoints[namespace][endpoint]
210
232
  except KeyError as e:
211
- return _make_response(e, f"Unknown RPC Endpoint: {method}")
233
+ return _make_response(e, request_id, message=f"Unknown RPC Endpoint: {method}")
212
234
  try:
213
235
  response = delegator(ethereum_tester_instance, params)
214
236
  except NotImplementedError as e:
215
- return _make_response(e, f"RPC Endpoint has not been implemented: {method}")
237
+ return _make_response(
238
+ e,
239
+ request_id,
240
+ message=f"RPC Endpoint has not been implemented: {method}",
241
+ )
216
242
  except TransactionFailed as e:
217
243
  first_arg = e.args[0]
218
244
  try:
@@ -229,4 +255,4 @@ def _make_request(
229
255
  reason = first_arg
230
256
  raise TransactionFailed(f"execution reverted: {reason}")
231
257
  else:
232
- return _make_response(response)
258
+ return _make_response(response, request_id)
@@ -59,7 +59,7 @@ if TYPE_CHECKING:
59
59
 
60
60
 
61
61
  def is_named_block(value: Any) -> bool:
62
- return value in {"latest", "earliest", "pending", "safe", "finalized"}
62
+ return value in {"latest", "earliest", "safe", "finalized"}
63
63
 
64
64
 
65
65
  def is_hexstr(value: Any) -> bool:
@@ -72,7 +72,9 @@ is_not_named_block = complement(is_named_block)
72
72
  # --- Request Mapping --- #
73
73
 
74
74
  TRANSACTION_REQUEST_KEY_MAPPING = {
75
+ "blobVersionedHashes": "blob_versioned_hashes",
75
76
  "gasPrice": "gas_price",
77
+ "maxFeePerBlobGas": "max_fee_per_blob_gas",
76
78
  "maxFeePerGas": "max_fee_per_gas",
77
79
  "maxPriorityFeePerGas": "max_priority_fee_per_gas",
78
80
  "accessList": "access_list",
@@ -90,7 +92,7 @@ TRANSACTION_REQUEST_FORMATTERS = {
90
92
  "maxFeePerGas": to_integer_if_hex,
91
93
  "maxPriorityFeePerGas": to_integer_if_hex,
92
94
  "accessList": apply_list_to_array_formatter(
93
- (apply_key_map({"storageKeys": "storage_keys"}))
95
+ apply_key_map({"storageKeys": "storage_keys"})
94
96
  ),
95
97
  }
96
98
  transaction_request_formatter = apply_formatters_to_dict(TRANSACTION_REQUEST_FORMATTERS)
@@ -108,14 +110,14 @@ filter_request_remapper = apply_key_map(FILTER_REQUEST_KEY_MAPPING)
108
110
 
109
111
 
110
112
  FILTER_REQUEST_FORMATTERS = {
111
- "fromBlock": to_integer_if_hex,
112
- "toBlock": to_integer_if_hex,
113
+ "from_block": to_integer_if_hex,
114
+ "to_block": to_integer_if_hex,
113
115
  }
114
116
  filter_request_formatter = apply_formatters_to_dict(FILTER_REQUEST_FORMATTERS)
115
117
 
116
118
  filter_request_transformer = compose(
117
- filter_request_remapper,
118
119
  filter_request_formatter,
120
+ filter_request_remapper,
119
121
  )
120
122
 
121
123
 
@@ -123,10 +125,12 @@ filter_request_transformer = compose(
123
125
 
124
126
  TRANSACTION_RESULT_KEY_MAPPING = {
125
127
  "access_list": "accessList",
128
+ "blob_versioned_hashes": "blobVersionedHashes",
126
129
  "block_hash": "blockHash",
127
130
  "block_number": "blockNumber",
128
131
  "chain_id": "chainId",
129
132
  "gas_price": "gasPrice",
133
+ "max_fee_per_blob_gas": "maxFeePerBlobGas",
130
134
  "max_fee_per_gas": "maxFeePerGas",
131
135
  "max_priority_fee_per_gas": "maxPriorityFeePerGas",
132
136
  "transaction_hash": "transactionHash",
@@ -164,6 +168,8 @@ RECEIPT_RESULT_KEY_MAPPING = {
164
168
  "effective_gas_price": "effectiveGasPrice",
165
169
  "transaction_hash": "transactionHash",
166
170
  "transaction_index": "transactionIndex",
171
+ "blob_gas_used": "blobGasUsed",
172
+ "blob_gas_price": "blobGasPrice",
167
173
  }
168
174
  receipt_result_remapper = apply_key_map(RECEIPT_RESULT_KEY_MAPPING)
169
175
 
@@ -186,6 +192,9 @@ BLOCK_RESULT_KEY_MAPPING = {
186
192
  # JSON-RPC spec still says miner
187
193
  "coinbase": "miner",
188
194
  "withdrawals_root": "withdrawalsRoot",
195
+ "parent_beacon_block_root": "parentBeaconBlockRoot",
196
+ "blob_gas_used": "blobGasUsed",
197
+ "excess_blob_gas": "excessBlobGas",
189
198
  }
190
199
  block_result_remapper = apply_key_map(BLOCK_RESULT_KEY_MAPPING)
191
200
 
@@ -276,11 +285,6 @@ request_formatters = {
276
285
  ),
277
286
  # EVM
278
287
  RPCEndpoint("evm_revert"): apply_formatters_to_args(hex_to_integer),
279
- # Personal
280
- RPCEndpoint("personal_sendTransaction"): apply_formatters_to_args(
281
- transaction_request_transformer,
282
- identity,
283
- ),
284
288
  }
285
289
 
286
290
  result_formatters: Optional[Dict[RPCEndpoint, Callable[..., Any]]] = {
@@ -330,9 +334,7 @@ result_formatters: Optional[Dict[RPCEndpoint, Callable[..., Any]]] = {
330
334
 
331
335
 
332
336
  def guess_from(w3: "Web3", _: TxParams) -> ChecksumAddress:
333
- if w3.eth.coinbase:
334
- return w3.eth.coinbase
335
- elif w3.eth.accounts and len(w3.eth.accounts) > 0:
337
+ if w3.eth.accounts and len(w3.eth.accounts) > 0:
336
338
  return w3.eth.accounts[0]
337
339
 
338
340
  return None
@@ -356,11 +358,8 @@ def fill_default(
356
358
  async def async_guess_from(
357
359
  async_w3: "AsyncWeb3", _: TxParams
358
360
  ) -> Optional[ChecksumAddress]:
359
- coinbase = await async_w3.eth.coinbase
360
361
  accounts = await async_w3.eth.accounts
361
- if coinbase is not None:
362
- return coinbase
363
- elif accounts is not None and len(accounts) > 0:
362
+ if accounts is not None and len(accounts) > 0:
364
363
  return accounts[0]
365
364
  return None
366
365
 
web3/providers/ipc.py CHANGED
@@ -14,8 +14,11 @@ from types import (
14
14
  )
15
15
  from typing import (
16
16
  Any,
17
+ List,
18
+ Tuple,
17
19
  Type,
18
20
  Union,
21
+ cast,
19
22
  )
20
23
 
21
24
  from web3._utils.threads import (
@@ -26,6 +29,16 @@ from web3.types import (
26
29
  RPCResponse,
27
30
  )
28
31
 
32
+ from .._utils.batching import (
33
+ sort_batch_response_by_response_ids,
34
+ )
35
+ from .._utils.caching import (
36
+ handle_request_caching,
37
+ )
38
+ from ..exceptions import (
39
+ Web3TypeError,
40
+ Web3ValueError,
41
+ )
29
42
  from .base import (
30
43
  JSONBaseProvider,
31
44
  )
@@ -96,28 +109,29 @@ def get_default_ipc_path() -> str:
96
109
  return r"\\.\pipe\geth.ipc"
97
110
 
98
111
  else:
99
- raise ValueError(
112
+ raise Web3ValueError(
100
113
  f"Unsupported platform '{sys.platform}'. Only darwin/linux/win32/"
101
114
  "freebsd are supported. You must specify the ipc_path"
102
115
  )
103
116
 
104
117
 
105
118
  def get_dev_ipc_path() -> str:
106
- if os.environ.get("WEB3_PROVIDER_URI", ""):
107
- return os.environ.get("WEB3_PROVIDER_URI")
119
+ web3_provider_uri = os.environ.get("WEB3_PROVIDER_URI", "")
120
+ if web3_provider_uri and "geth.ipc" in web3_provider_uri:
121
+ return web3_provider_uri
108
122
 
109
- elif sys.platform == "darwin":
110
- tmpdir = os.environ.get("TMPDIR", "")
123
+ elif sys.platform == "darwin" or sys.platform.startswith("linux"):
124
+ tmpdir = os.environ.get("TMPDIR", "/tmp")
111
125
  return os.path.expanduser(os.path.join(tmpdir, "geth.ipc"))
112
126
 
113
- elif sys.platform.startswith("linux") or sys.platform.startswith("freebsd"):
127
+ elif sys.platform.endswith("freebsd"):
114
128
  return os.path.expanduser(os.path.join("/tmp", "geth.ipc"))
115
129
 
116
130
  elif sys.platform == "win32":
117
131
  return r"\\.\pipe\geth.ipc"
118
132
 
119
133
  else:
120
- raise ValueError(
134
+ raise Web3ValueError(
121
135
  f"Unsupported platform '{sys.platform}'. Only darwin/linux/win32/"
122
136
  "freebsd are supported. You must specify the ipc_path"
123
137
  )
@@ -131,33 +145,27 @@ class IPCProvider(JSONBaseProvider):
131
145
  self,
132
146
  ipc_path: Union[str, Path] = None,
133
147
  timeout: int = 30,
134
- *args: Any,
135
148
  **kwargs: Any,
136
149
  ) -> None:
150
+ super().__init__(**kwargs)
137
151
  if ipc_path is None:
138
152
  self.ipc_path = get_default_ipc_path()
139
153
  elif isinstance(ipc_path, str) or isinstance(ipc_path, Path):
140
154
  self.ipc_path = str(Path(ipc_path).expanduser().resolve())
141
155
  else:
142
- raise TypeError("ipc_path must be of type string or pathlib.Path")
156
+ raise Web3TypeError("ipc_path must be of type string or pathlib.Path")
143
157
 
144
158
  self.timeout = timeout
145
159
  self._lock = threading.Lock()
146
160
  self._socket = PersistantSocket(self.ipc_path)
147
- super().__init__()
148
161
 
149
162
  def __str__(self) -> str:
150
163
  return f"<{self.__class__.__name__} {self.ipc_path}>"
151
164
 
152
- def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
153
- self.logger.debug(
154
- f"Making request IPC. Path: {self.ipc_path}, Method: {method}"
155
- )
156
- request = self.encode_rpc_request(method, params)
157
-
165
+ def _make_request(self, request: bytes) -> RPCResponse:
158
166
  with self._lock, self._socket as sock:
159
167
  try:
160
- sock.sendall(request)
168
+ sock.sendall(request + b"\n")
161
169
  except BrokenPipeError:
162
170
  # one extra attempt, then give up
163
171
  sock = self._socket.reset()
@@ -185,6 +193,22 @@ class IPCProvider(JSONBaseProvider):
185
193
  timeout.sleep(0)
186
194
  continue
187
195
 
196
+ @handle_request_caching
197
+ def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
198
+ self.logger.debug(
199
+ f"Making request IPC. Path: {self.ipc_path}, Method: {method}"
200
+ )
201
+ request = self.encode_rpc_request(method, params)
202
+ return self._make_request(request)
203
+
204
+ def make_batch_request(
205
+ self, requests: List[Tuple[RPCEndpoint, Any]]
206
+ ) -> List[RPCResponse]:
207
+ self.logger.debug(f"Making batch request IPC. Path: {self.ipc_path}")
208
+ request_data = self.encode_batch_rpc_request(requests)
209
+ response = cast(List[RPCResponse], self._make_request(request_data))
210
+ return sort_batch_response_by_response_ids(response)
211
+
188
212
 
189
213
  # A valid JSON RPC response can only end in } or ] http://www.jsonrpc.org/specification
190
214
  def has_valid_json_rpc_ending(raw_response: bytes) -> bool:
@@ -10,9 +10,12 @@ from types import (
10
10
  )
11
11
  from typing import (
12
12
  Any,
13
+ List,
13
14
  Optional,
15
+ Tuple,
14
16
  Type,
15
17
  Union,
18
+ cast,
16
19
  )
17
20
 
18
21
  from eth_typing import (
@@ -25,6 +28,12 @@ from websockets.legacy.client import (
25
28
  WebSocketClientProtocol,
26
29
  )
27
30
 
31
+ from web3._utils.batching import (
32
+ sort_batch_response_by_response_ids,
33
+ )
34
+ from web3._utils.caching import (
35
+ handle_request_caching,
36
+ )
28
37
  from web3.exceptions import (
29
38
  Web3ValidationError,
30
39
  )
@@ -91,7 +100,9 @@ class LegacyWebSocketProvider(JSONBaseProvider):
91
100
  endpoint_uri: Optional[Union[URI, str]] = None,
92
101
  websocket_kwargs: Optional[Any] = None,
93
102
  websocket_timeout: int = DEFAULT_WEBSOCKET_TIMEOUT,
103
+ **kwargs: Any,
94
104
  ) -> None:
105
+ super().__init__(**kwargs)
95
106
  self.endpoint_uri = URI(endpoint_uri)
96
107
  self.websocket_timeout = websocket_timeout
97
108
  if self.endpoint_uri is None:
@@ -110,7 +121,6 @@ class LegacyWebSocketProvider(JSONBaseProvider):
110
121
  f"in websocket_kwargs, found: {found_restricted_keys}"
111
122
  )
112
123
  self.conn = PersistentWebSocket(self.endpoint_uri, websocket_kwargs)
113
- super().__init__()
114
124
 
115
125
  def __str__(self) -> str:
116
126
  return f"WS connection {self.endpoint_uri}"
@@ -124,6 +134,7 @@ class LegacyWebSocketProvider(JSONBaseProvider):
124
134
  await asyncio.wait_for(conn.recv(), timeout=self.websocket_timeout)
125
135
  )
126
136
 
137
+ @handle_request_caching
127
138
  def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
128
139
  self.logger.debug(
129
140
  f"Making request WebSocket. URI: {self.endpoint_uri}, " f"Method: {method}"
@@ -133,3 +144,17 @@ class LegacyWebSocketProvider(JSONBaseProvider):
133
144
  self.coro_make_request(request_data), LegacyWebSocketProvider._loop
134
145
  )
135
146
  return future.result()
147
+
148
+ def make_batch_request(
149
+ self, requests: List[Tuple[RPCEndpoint, Any]]
150
+ ) -> List[RPCResponse]:
151
+ self.logger.debug(
152
+ f"Making batch request WebSocket. URI: {self.endpoint_uri}, "
153
+ f"Methods: {requests}"
154
+ )
155
+ request_data = self.encode_batch_rpc_request(requests)
156
+ future = asyncio.run_coroutine_threadsafe(
157
+ self.coro_make_request(request_data), LegacyWebSocketProvider._loop
158
+ )
159
+ response = cast(List[RPCResponse], future.result())
160
+ return sort_batch_response_by_response_ids(response)
@@ -13,3 +13,10 @@ from .async_ipc import (
13
13
  from .websocket import (
14
14
  WebSocketProvider,
15
15
  )
16
+
17
+ __all__ = [
18
+ "PersistentConnectionProvider",
19
+ "PersistentConnection",
20
+ "AsyncIPCProvider",
21
+ "WebSocketProvider",
22
+ ]