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
@@ -0,0 +1,433 @@
1
+ from asyncio import (
2
+ iscoroutinefunction,
3
+ )
4
+ import collections
5
+ import hashlib
6
+ import threading
7
+ from typing import (
8
+ TYPE_CHECKING,
9
+ Any,
10
+ Callable,
11
+ Coroutine,
12
+ Dict,
13
+ List,
14
+ Sequence,
15
+ Tuple,
16
+ Union,
17
+ )
18
+
19
+ from eth_typing import (
20
+ ChainId,
21
+ )
22
+ from eth_utils import (
23
+ is_boolean,
24
+ is_bytes,
25
+ is_dict,
26
+ is_list_like,
27
+ is_null,
28
+ is_number,
29
+ is_text,
30
+ to_bytes,
31
+ )
32
+
33
+ from web3._utils.caching import (
34
+ ASYNC_PROVIDER_TYPE,
35
+ SYNC_PROVIDER_TYPE,
36
+ )
37
+ from web3._utils.caching.request_caching_validation import (
38
+ UNCACHEABLE_BLOCK_IDS,
39
+ always_cache_request,
40
+ async_validate_from_block_id_in_params,
41
+ async_validate_from_blockhash_in_params,
42
+ async_validate_from_blocknum_in_result,
43
+ validate_from_block_id_in_params,
44
+ validate_from_blockhash_in_params,
45
+ validate_from_blocknum_in_result,
46
+ )
47
+ from web3._utils.empty import (
48
+ empty,
49
+ )
50
+ from web3._utils.rpc_abi import (
51
+ RPC,
52
+ )
53
+ from web3.exceptions import (
54
+ Web3TypeError,
55
+ )
56
+ from web3.types import (
57
+ RPCEndpoint,
58
+ )
59
+ from web3.utils import (
60
+ RequestCacheValidationThreshold,
61
+ )
62
+
63
+ if TYPE_CHECKING:
64
+ from web3.providers import ( # noqa: F401
65
+ AsyncBaseProvider,
66
+ BaseProvider,
67
+ PersistentConnectionProvider,
68
+ )
69
+ from web3.types import ( # noqa: F401
70
+ AsyncMakeRequestFn,
71
+ MakeRequestFn,
72
+ RPCRequest,
73
+ RPCResponse,
74
+ )
75
+
76
+
77
+ def generate_cache_key(value: Any) -> str:
78
+ """
79
+ Generates a cache key for the *args and **kwargs
80
+ """
81
+ if is_bytes(value):
82
+ return hashlib.md5(value).hexdigest()
83
+ elif is_text(value):
84
+ return generate_cache_key(to_bytes(text=value))
85
+ elif is_boolean(value) or is_null(value) or is_number(value):
86
+ return generate_cache_key(repr(value))
87
+ elif is_dict(value):
88
+ return generate_cache_key((key, value[key]) for key in sorted(value.keys()))
89
+ elif is_list_like(value) or isinstance(value, collections.abc.Generator):
90
+ return generate_cache_key("".join(generate_cache_key(item) for item in value))
91
+ else:
92
+ raise Web3TypeError(
93
+ f"Cannot generate cache key for value {value} of type {type(value)}"
94
+ )
95
+
96
+
97
+ class RequestInformation:
98
+ def __init__(
99
+ self,
100
+ method: RPCEndpoint,
101
+ params: Any,
102
+ response_formatters: Tuple[
103
+ Union[Dict[str, Callable[..., Any]], Callable[..., Any]],
104
+ Callable[..., Any],
105
+ Callable[..., Any],
106
+ ],
107
+ subscription_id: str = None,
108
+ ):
109
+ self.method = method
110
+ self.params = params
111
+ self.response_formatters = response_formatters
112
+ self.subscription_id = subscription_id
113
+ self.middleware_response_processors: List[Callable[..., Any]] = []
114
+
115
+
116
+ DEFAULT_VALIDATION_THRESHOLD = 60 * 60 # 1 hour
117
+
118
+ CHAIN_VALIDATION_THRESHOLD_DEFAULTS: Dict[
119
+ int, Union[RequestCacheValidationThreshold, int]
120
+ ] = {
121
+ # Suggested safe values as defaults for each chain. Users can configure a different
122
+ # value if desired.
123
+ ChainId.ETH.value: RequestCacheValidationThreshold.FINALIZED,
124
+ ChainId.ARB1.value: 7 * 24 * 60 * 60, # 7 days
125
+ ChainId.ZKSYNC.value: 60 * 60, # 1 hour
126
+ ChainId.OETH.value: 3 * 60, # 3 minutes
127
+ ChainId.MATIC.value: 30 * 60, # 30 minutes
128
+ ChainId.ZKEVM.value: 60 * 60, # 1 hour
129
+ ChainId.BASE.value: 7 * 24 * 60 * 60, # 7 days
130
+ ChainId.SCR.value: 60 * 60, # 1 hour
131
+ ChainId.GNO.value: 5 * 60, # 5 minutes
132
+ ChainId.AVAX.value: 2 * 60, # 2 minutes
133
+ ChainId.BNB.value: 2 * 60, # 2 minutes
134
+ ChainId.FTM.value: 60, # 1 minute
135
+ }
136
+
137
+
138
+ def is_cacheable_request(
139
+ provider: Union[ASYNC_PROVIDER_TYPE, SYNC_PROVIDER_TYPE],
140
+ method: RPCEndpoint,
141
+ params: Any,
142
+ ) -> bool:
143
+ if not (provider.cache_allowed_requests and method in provider.cacheable_requests):
144
+ return False
145
+ elif method in BLOCKNUM_IN_PARAMS:
146
+ block_id = params[0]
147
+ if block_id in UNCACHEABLE_BLOCK_IDS:
148
+ return False
149
+ return True
150
+
151
+
152
+ # -- request caching -- #
153
+
154
+ ALWAYS_CACHE = {
155
+ RPC.eth_chainId,
156
+ RPC.web3_clientVersion,
157
+ RPC.net_version,
158
+ }
159
+ BLOCKNUM_IN_PARAMS = {
160
+ RPC.eth_getBlockByNumber,
161
+ RPC.eth_getRawTransactionByBlockNumberAndIndex,
162
+ RPC.eth_getBlockTransactionCountByNumber,
163
+ RPC.eth_getUncleByBlockNumberAndIndex,
164
+ RPC.eth_getUncleCountByBlockNumber,
165
+ }
166
+ BLOCK_IN_RESULT = {
167
+ RPC.eth_getBlockByHash,
168
+ RPC.eth_getTransactionByHash,
169
+ RPC.eth_getTransactionByBlockNumberAndIndex,
170
+ RPC.eth_getTransactionByBlockHashAndIndex,
171
+ RPC.eth_getBlockTransactionCountByHash,
172
+ }
173
+ BLOCKHASH_IN_PARAMS = {
174
+ RPC.eth_getRawTransactionByBlockHashAndIndex,
175
+ RPC.eth_getUncleByBlockHashAndIndex,
176
+ RPC.eth_getUncleCountByBlockHash,
177
+ }
178
+
179
+ INTERNAL_VALIDATION_MAP: Dict[
180
+ RPCEndpoint,
181
+ Callable[
182
+ [SYNC_PROVIDER_TYPE, Sequence[Any], Dict[str, Any]],
183
+ bool,
184
+ ],
185
+ ] = {
186
+ **{endpoint: always_cache_request for endpoint in ALWAYS_CACHE},
187
+ **{endpoint: validate_from_block_id_in_params for endpoint in BLOCKNUM_IN_PARAMS},
188
+ **{endpoint: validate_from_blocknum_in_result for endpoint in BLOCK_IN_RESULT},
189
+ **{endpoint: validate_from_blockhash_in_params for endpoint in BLOCKHASH_IN_PARAMS},
190
+ }
191
+ CACHEABLE_REQUESTS = tuple(INTERNAL_VALIDATION_MAP.keys())
192
+
193
+
194
+ def set_threshold_if_empty(provider: SYNC_PROVIDER_TYPE) -> None:
195
+ current_threshold = provider.request_cache_validation_threshold
196
+
197
+ if current_threshold is empty or isinstance(
198
+ current_threshold, RequestCacheValidationThreshold
199
+ ):
200
+ cache_allowed_requests = provider.cache_allowed_requests
201
+ try:
202
+ # turn off momentarily to avoid recursion
203
+ provider.cache_allowed_requests = False
204
+ chain_id_result = provider.make_request(RPCEndpoint("eth_chainId"), [])[
205
+ "result"
206
+ ]
207
+ chain_id = int(chain_id_result, 16)
208
+
209
+ if current_threshold is empty:
210
+ provider.request_cache_validation_threshold = (
211
+ CHAIN_VALIDATION_THRESHOLD_DEFAULTS.get(
212
+ chain_id, DEFAULT_VALIDATION_THRESHOLD
213
+ )
214
+ )
215
+ except Exception:
216
+ provider.request_cache_validation_threshold = DEFAULT_VALIDATION_THRESHOLD
217
+ finally:
218
+ provider.cache_allowed_requests = cache_allowed_requests
219
+
220
+
221
+ def _should_cache_response(
222
+ provider: SYNC_PROVIDER_TYPE,
223
+ method: RPCEndpoint,
224
+ params: Sequence[Any],
225
+ response: "RPCResponse",
226
+ ) -> bool:
227
+ result = response.get("result", None)
228
+ if "error" in response or is_null(result):
229
+ return False
230
+
231
+ set_threshold_if_empty(provider)
232
+ if (
233
+ method in INTERNAL_VALIDATION_MAP
234
+ and provider.request_cache_validation_threshold is not None
235
+ ):
236
+ return INTERNAL_VALIDATION_MAP[method](provider, params, result)
237
+ return True
238
+
239
+
240
+ def handle_request_caching(
241
+ func: Callable[[SYNC_PROVIDER_TYPE, RPCEndpoint, Any], "RPCResponse"]
242
+ ) -> Callable[..., "RPCResponse"]:
243
+ def wrapper(
244
+ provider: SYNC_PROVIDER_TYPE, method: RPCEndpoint, params: Any
245
+ ) -> "RPCResponse":
246
+ if is_cacheable_request(provider, method, params):
247
+ request_cache = provider._request_cache
248
+ cache_key = generate_cache_key(
249
+ f"{threading.get_ident()}:{(method, params)}"
250
+ )
251
+ cache_result = request_cache.get_cache_entry(cache_key)
252
+ if cache_result is not None:
253
+ return cache_result
254
+ else:
255
+ response = func(provider, method, params)
256
+ if _should_cache_response(provider, method, params, response):
257
+ with provider._request_cache_lock:
258
+ request_cache.cache(cache_key, response)
259
+ return response
260
+ else:
261
+ return func(provider, method, params)
262
+
263
+ # save a reference to the decorator on the wrapped function
264
+ wrapper._decorator = handle_request_caching # type: ignore
265
+ return wrapper
266
+
267
+
268
+ # -- async -- #
269
+
270
+ ASYNC_VALIDATOR_TYPE = Callable[
271
+ ["AsyncBaseProvider", Sequence[Any], Dict[str, Any]],
272
+ Union[bool, Coroutine[Any, Any, bool]],
273
+ ]
274
+
275
+ ASYNC_INTERNAL_VALIDATION_MAP: Dict[RPCEndpoint, ASYNC_VALIDATOR_TYPE] = {
276
+ **{endpoint: always_cache_request for endpoint in ALWAYS_CACHE},
277
+ **{
278
+ endpoint: async_validate_from_block_id_in_params
279
+ for endpoint in BLOCKNUM_IN_PARAMS
280
+ },
281
+ **{
282
+ endpoint: async_validate_from_blocknum_in_result for endpoint in BLOCK_IN_RESULT
283
+ },
284
+ **{
285
+ endpoint: async_validate_from_blockhash_in_params
286
+ for endpoint in BLOCKHASH_IN_PARAMS
287
+ },
288
+ }
289
+
290
+
291
+ async def async_set_threshold_if_empty(provider: ASYNC_PROVIDER_TYPE) -> None:
292
+ current_threshold = provider.request_cache_validation_threshold
293
+
294
+ if current_threshold is empty or isinstance(
295
+ current_threshold, RequestCacheValidationThreshold
296
+ ):
297
+ cache_allowed_requests = provider.cache_allowed_requests
298
+ try:
299
+ # turn off momentarily to avoid recursion
300
+ provider.cache_allowed_requests = False
301
+ chain_id_result = await provider.make_request(
302
+ RPCEndpoint("eth_chainId"), []
303
+ )
304
+ chain_id = int(chain_id_result["result"], 16)
305
+
306
+ if current_threshold is empty:
307
+ provider.request_cache_validation_threshold = (
308
+ CHAIN_VALIDATION_THRESHOLD_DEFAULTS.get(
309
+ chain_id, DEFAULT_VALIDATION_THRESHOLD
310
+ )
311
+ )
312
+ except Exception:
313
+ provider.request_cache_validation_threshold = DEFAULT_VALIDATION_THRESHOLD
314
+ finally:
315
+ provider.cache_allowed_requests = cache_allowed_requests
316
+
317
+
318
+ async def _async_should_cache_response(
319
+ provider: ASYNC_PROVIDER_TYPE,
320
+ method: RPCEndpoint,
321
+ params: Sequence[Any],
322
+ response: "RPCResponse",
323
+ ) -> bool:
324
+ result = response.get("result", None)
325
+ if "error" in response or is_null(result):
326
+ return False
327
+
328
+ await async_set_threshold_if_empty(provider)
329
+ if (
330
+ method in ASYNC_INTERNAL_VALIDATION_MAP
331
+ and provider.request_cache_validation_threshold is not None
332
+ ):
333
+ cache_validator = ASYNC_INTERNAL_VALIDATION_MAP[method]
334
+ return (
335
+ await cache_validator(provider, params, result)
336
+ if iscoroutinefunction(cache_validator)
337
+ else cache_validator(provider, params, result)
338
+ )
339
+ return True
340
+
341
+
342
+ def async_handle_request_caching(
343
+ func: Callable[
344
+ [ASYNC_PROVIDER_TYPE, RPCEndpoint, Any], Coroutine[Any, Any, "RPCResponse"]
345
+ ],
346
+ ) -> Callable[..., Coroutine[Any, Any, "RPCResponse"]]:
347
+ async def wrapper(
348
+ provider: ASYNC_PROVIDER_TYPE, method: RPCEndpoint, params: Any
349
+ ) -> "RPCResponse":
350
+ if is_cacheable_request(provider, method, params):
351
+ request_cache = provider._request_cache
352
+ cache_key = generate_cache_key(
353
+ f"{threading.get_ident()}:{(method, params)}"
354
+ )
355
+ cache_result = request_cache.get_cache_entry(cache_key)
356
+ if cache_result is not None:
357
+ return cache_result
358
+ else:
359
+ response = await func(provider, method, params)
360
+ if await _async_should_cache_response(
361
+ provider, method, params, response
362
+ ):
363
+ async with provider._request_cache_lock:
364
+ request_cache.cache(cache_key, response)
365
+ return response
366
+ else:
367
+ return await func(provider, method, params)
368
+
369
+ # save a reference to the decorator on the wrapped function
370
+ wrapper._decorator = async_handle_request_caching # type: ignore
371
+ return wrapper
372
+
373
+
374
+ def async_handle_send_caching(
375
+ func: Callable[
376
+ [ASYNC_PROVIDER_TYPE, RPCEndpoint, Any],
377
+ Coroutine[Any, Any, "RPCRequest"],
378
+ ],
379
+ ) -> Callable[..., Coroutine[Any, Any, "RPCRequest"]]:
380
+ async def wrapper(
381
+ provider: ASYNC_PROVIDER_TYPE, method: RPCEndpoint, params: Any
382
+ ) -> "RPCRequest":
383
+ if is_cacheable_request(provider, method, params):
384
+ request_cache = provider._request_cache
385
+ cache_key = generate_cache_key(
386
+ f"{threading.get_ident()}:{(method, params)}"
387
+ )
388
+ cached_response = request_cache.get_cache_entry(cache_key)
389
+ if cached_response is not None:
390
+ # The request data isn't used, this just prevents a cached request from
391
+ # being sent - return an empty request object
392
+ return {"id": -1, "method": RPCEndpoint(""), "params": []}
393
+ return await func(provider, method, params)
394
+
395
+ # save a reference to the decorator on the wrapped function
396
+ wrapper._decorator = async_handle_send_caching # type: ignore
397
+ return wrapper
398
+
399
+
400
+ def async_handle_recv_caching(
401
+ func: Callable[
402
+ ["PersistentConnectionProvider", "RPCRequest"],
403
+ Coroutine[Any, Any, "RPCResponse"],
404
+ ]
405
+ ) -> Callable[..., Coroutine[Any, Any, "RPCResponse"]]:
406
+ async def wrapper(
407
+ provider: "PersistentConnectionProvider",
408
+ rpc_request: "RPCRequest",
409
+ ) -> "RPCResponse":
410
+ method = rpc_request["method"]
411
+ params = rpc_request["params"]
412
+ if is_cacheable_request(provider, method, params):
413
+ request_cache = provider._request_cache
414
+ cache_key = generate_cache_key(
415
+ f"{threading.get_ident()}:{(method, params)}"
416
+ )
417
+ cache_result = request_cache.get_cache_entry(cache_key)
418
+ if cache_result is not None:
419
+ return cache_result
420
+ else:
421
+ response = await func(provider, rpc_request)
422
+ if await _async_should_cache_response(
423
+ provider, method, params, response
424
+ ):
425
+ async with provider._request_cache_lock:
426
+ request_cache.cache(cache_key, response)
427
+ return response
428
+ else:
429
+ return await func(provider, rpc_request)
430
+
431
+ # save a reference to the decorator on the wrapped function
432
+ wrapper._decorator = async_handle_recv_caching # type: ignore
433
+ return wrapper