web3 7.3.1__py3-none-any.whl → 7.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- web3/_utils/caching/caching_utils.py +125 -24
- web3/_utils/caching/request_caching_validation.py +204 -47
- web3/_utils/contract_sources/contract_data/offchain_lookup.py +2 -2
- web3/_utils/module_testing/eth_module.py +3 -3
- web3/_utils/module_testing/module_testing_utils.py +3 -3
- web3/_utils/module_testing/utils.py +11 -3
- web3/providers/async_base.py +7 -4
- web3/providers/base.py +7 -4
- web3/providers/ipc.py +4 -0
- web3/utils/async_exception_handling.py +2 -5
- web3/utils/exception_handling.py +2 -7
- {web3-7.3.1.dist-info → web3-7.4.0.dist-info}/METADATA +1 -1
- {web3-7.3.1.dist-info → web3-7.4.0.dist-info}/RECORD +16 -16
- {web3-7.3.1.dist-info → web3-7.4.0.dist-info}/WHEEL +1 -1
- {web3-7.3.1.dist-info → web3-7.4.0.dist-info}/LICENSE +0 -0
- {web3-7.3.1.dist-info → web3-7.4.0.dist-info}/top_level.txt +0 -0
|
@@ -16,6 +16,9 @@ from typing import (
|
|
|
16
16
|
Union,
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
+
from eth_typing import (
|
|
20
|
+
ChainId,
|
|
21
|
+
)
|
|
19
22
|
from eth_utils import (
|
|
20
23
|
is_boolean,
|
|
21
24
|
is_bytes,
|
|
@@ -34,12 +37,15 @@ from web3._utils.caching import (
|
|
|
34
37
|
from web3._utils.caching.request_caching_validation import (
|
|
35
38
|
UNCACHEABLE_BLOCK_IDS,
|
|
36
39
|
always_cache_request,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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,
|
|
43
49
|
)
|
|
44
50
|
from web3._utils.rpc_abi import (
|
|
45
51
|
RPC,
|
|
@@ -47,6 +53,12 @@ from web3._utils.rpc_abi import (
|
|
|
47
53
|
from web3.exceptions import (
|
|
48
54
|
Web3TypeError,
|
|
49
55
|
)
|
|
56
|
+
from web3.types import (
|
|
57
|
+
RPCEndpoint,
|
|
58
|
+
)
|
|
59
|
+
from web3.utils import (
|
|
60
|
+
RequestCacheValidationThreshold,
|
|
61
|
+
)
|
|
50
62
|
|
|
51
63
|
if TYPE_CHECKING:
|
|
52
64
|
from web3.providers import ( # noqa: F401
|
|
@@ -56,7 +68,6 @@ if TYPE_CHECKING:
|
|
|
56
68
|
from web3.types import ( # noqa: F401
|
|
57
69
|
AsyncMakeRequestFn,
|
|
58
70
|
MakeRequestFn,
|
|
59
|
-
RPCEndpoint,
|
|
60
71
|
RPCResponse,
|
|
61
72
|
)
|
|
62
73
|
|
|
@@ -84,7 +95,7 @@ def generate_cache_key(value: Any) -> str:
|
|
|
84
95
|
class RequestInformation:
|
|
85
96
|
def __init__(
|
|
86
97
|
self,
|
|
87
|
-
method:
|
|
98
|
+
method: RPCEndpoint,
|
|
88
99
|
params: Any,
|
|
89
100
|
response_formatters: Tuple[
|
|
90
101
|
Union[Dict[str, Callable[..., Any]], Callable[..., Any]],
|
|
@@ -100,9 +111,31 @@ class RequestInformation:
|
|
|
100
111
|
self.middleware_response_processors: List[Callable[..., Any]] = []
|
|
101
112
|
|
|
102
113
|
|
|
114
|
+
DEFAULT_VALIDATION_THRESHOLD = 60 * 60 # 1 hour
|
|
115
|
+
|
|
116
|
+
CHAIN_VALIDATION_THRESHOLD_DEFAULTS: Dict[
|
|
117
|
+
int, Union[RequestCacheValidationThreshold, int]
|
|
118
|
+
] = {
|
|
119
|
+
# Suggested safe values as defaults for each chain. Users can configure a different
|
|
120
|
+
# value if desired.
|
|
121
|
+
ChainId.ETH.value: RequestCacheValidationThreshold.FINALIZED,
|
|
122
|
+
ChainId.ARB1.value: 7 * 24 * 60 * 60, # 7 days
|
|
123
|
+
ChainId.ZKSYNC.value: 60 * 60, # 1 hour
|
|
124
|
+
ChainId.OETH.value: 3 * 60, # 3 minutes
|
|
125
|
+
ChainId.MATIC.value: 30 * 60, # 30 minutes
|
|
126
|
+
ChainId.ZKEVM.value: 60 * 60, # 1 hour
|
|
127
|
+
ChainId.BASE.value: 7 * 24 * 60 * 60, # 7 days
|
|
128
|
+
ChainId.SCR.value: 60 * 60, # 1 hour
|
|
129
|
+
ChainId.GNO.value: 5 * 60, # 5 minutes
|
|
130
|
+
ChainId.AVAX.value: 2 * 60, # 2 minutes
|
|
131
|
+
ChainId.BNB.value: 2 * 60, # 2 minutes
|
|
132
|
+
ChainId.FTM.value: 60, # 1 minute
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
103
136
|
def is_cacheable_request(
|
|
104
137
|
provider: Union[ASYNC_PROVIDER_TYPE, SYNC_PROVIDER_TYPE],
|
|
105
|
-
method:
|
|
138
|
+
method: RPCEndpoint,
|
|
106
139
|
params: Any,
|
|
107
140
|
) -> bool:
|
|
108
141
|
if not (provider.cache_allowed_requests and method in provider.cacheable_requests):
|
|
@@ -128,7 +161,7 @@ BLOCKNUM_IN_PARAMS = {
|
|
|
128
161
|
RPC.eth_getUncleByBlockNumberAndIndex,
|
|
129
162
|
RPC.eth_getUncleCountByBlockNumber,
|
|
130
163
|
}
|
|
131
|
-
|
|
164
|
+
BLOCK_IN_RESULT = {
|
|
132
165
|
RPC.eth_getBlockByHash,
|
|
133
166
|
RPC.eth_getTransactionByHash,
|
|
134
167
|
RPC.eth_getTransactionByBlockNumberAndIndex,
|
|
@@ -142,25 +175,58 @@ BLOCKHASH_IN_PARAMS = {
|
|
|
142
175
|
}
|
|
143
176
|
|
|
144
177
|
INTERNAL_VALIDATION_MAP: Dict[
|
|
145
|
-
|
|
178
|
+
RPCEndpoint,
|
|
179
|
+
Callable[
|
|
180
|
+
[SYNC_PROVIDER_TYPE, Sequence[Any], Dict[str, Any]],
|
|
181
|
+
bool,
|
|
182
|
+
],
|
|
146
183
|
] = {
|
|
147
184
|
**{endpoint: always_cache_request for endpoint in ALWAYS_CACHE},
|
|
148
|
-
**{endpoint:
|
|
149
|
-
**{endpoint:
|
|
150
|
-
**{endpoint:
|
|
185
|
+
**{endpoint: validate_from_block_id_in_params for endpoint in BLOCKNUM_IN_PARAMS},
|
|
186
|
+
**{endpoint: validate_from_blocknum_in_result for endpoint in BLOCK_IN_RESULT},
|
|
187
|
+
**{endpoint: validate_from_blockhash_in_params for endpoint in BLOCKHASH_IN_PARAMS},
|
|
151
188
|
}
|
|
152
189
|
CACHEABLE_REQUESTS = tuple(INTERNAL_VALIDATION_MAP.keys())
|
|
153
190
|
|
|
154
191
|
|
|
192
|
+
def set_threshold_if_empty(provider: SYNC_PROVIDER_TYPE) -> None:
|
|
193
|
+
current_threshold = provider.request_cache_validation_threshold
|
|
194
|
+
|
|
195
|
+
if current_threshold is empty or isinstance(
|
|
196
|
+
current_threshold, RequestCacheValidationThreshold
|
|
197
|
+
):
|
|
198
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
199
|
+
try:
|
|
200
|
+
# turn off momentarily to avoid recursion
|
|
201
|
+
provider.cache_allowed_requests = False
|
|
202
|
+
chain_id_result = provider.make_request(RPCEndpoint("eth_chainId"), [])[
|
|
203
|
+
"result"
|
|
204
|
+
]
|
|
205
|
+
chain_id = int(chain_id_result, 16)
|
|
206
|
+
|
|
207
|
+
if current_threshold is empty:
|
|
208
|
+
provider.request_cache_validation_threshold = (
|
|
209
|
+
CHAIN_VALIDATION_THRESHOLD_DEFAULTS.get(
|
|
210
|
+
chain_id, DEFAULT_VALIDATION_THRESHOLD
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
except Exception:
|
|
214
|
+
provider.request_cache_validation_threshold = DEFAULT_VALIDATION_THRESHOLD
|
|
215
|
+
finally:
|
|
216
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
217
|
+
|
|
218
|
+
|
|
155
219
|
def _should_cache_response(
|
|
156
220
|
provider: SYNC_PROVIDER_TYPE,
|
|
157
|
-
method:
|
|
221
|
+
method: RPCEndpoint,
|
|
158
222
|
params: Sequence[Any],
|
|
159
223
|
response: "RPCResponse",
|
|
160
224
|
) -> bool:
|
|
161
225
|
result = response.get("result", None)
|
|
162
226
|
if "error" in response or is_null(result):
|
|
163
227
|
return False
|
|
228
|
+
|
|
229
|
+
set_threshold_if_empty(provider)
|
|
164
230
|
if (
|
|
165
231
|
method in INTERNAL_VALIDATION_MAP
|
|
166
232
|
and provider.request_cache_validation_threshold is not None
|
|
@@ -170,10 +236,10 @@ def _should_cache_response(
|
|
|
170
236
|
|
|
171
237
|
|
|
172
238
|
def handle_request_caching(
|
|
173
|
-
func: Callable[[SYNC_PROVIDER_TYPE,
|
|
239
|
+
func: Callable[[SYNC_PROVIDER_TYPE, RPCEndpoint, Any], "RPCResponse"]
|
|
174
240
|
) -> Callable[..., "RPCResponse"]:
|
|
175
241
|
def wrapper(
|
|
176
|
-
provider: SYNC_PROVIDER_TYPE, method:
|
|
242
|
+
provider: SYNC_PROVIDER_TYPE, method: RPCEndpoint, params: Any
|
|
177
243
|
) -> "RPCResponse":
|
|
178
244
|
if is_cacheable_request(provider, method, params):
|
|
179
245
|
request_cache = provider._request_cache
|
|
@@ -204,25 +270,60 @@ ASYNC_VALIDATOR_TYPE = Callable[
|
|
|
204
270
|
Union[bool, Coroutine[Any, Any, bool]],
|
|
205
271
|
]
|
|
206
272
|
|
|
207
|
-
ASYNC_INTERNAL_VALIDATION_MAP: Dict[
|
|
273
|
+
ASYNC_INTERNAL_VALIDATION_MAP: Dict[RPCEndpoint, ASYNC_VALIDATOR_TYPE] = {
|
|
208
274
|
**{endpoint: always_cache_request for endpoint in ALWAYS_CACHE},
|
|
209
|
-
**{endpoint: async_validate_blocknum_in_params for endpoint in BLOCKNUM_IN_PARAMS},
|
|
210
|
-
**{endpoint: async_validate_blocknum_in_result for endpoint in BLOCKNUM_IN_RESULT},
|
|
211
275
|
**{
|
|
212
|
-
endpoint:
|
|
276
|
+
endpoint: async_validate_from_block_id_in_params
|
|
277
|
+
for endpoint in BLOCKNUM_IN_PARAMS
|
|
278
|
+
},
|
|
279
|
+
**{
|
|
280
|
+
endpoint: async_validate_from_blocknum_in_result for endpoint in BLOCK_IN_RESULT
|
|
281
|
+
},
|
|
282
|
+
**{
|
|
283
|
+
endpoint: async_validate_from_blockhash_in_params
|
|
284
|
+
for endpoint in BLOCKHASH_IN_PARAMS
|
|
213
285
|
},
|
|
214
286
|
}
|
|
215
287
|
|
|
216
288
|
|
|
289
|
+
async def async_set_threshold_if_empty(provider: ASYNC_PROVIDER_TYPE) -> None:
|
|
290
|
+
current_threshold = provider.request_cache_validation_threshold
|
|
291
|
+
|
|
292
|
+
if current_threshold is empty or isinstance(
|
|
293
|
+
current_threshold, RequestCacheValidationThreshold
|
|
294
|
+
):
|
|
295
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
296
|
+
try:
|
|
297
|
+
# turn off momentarily to avoid recursion
|
|
298
|
+
provider.cache_allowed_requests = False
|
|
299
|
+
chain_id_result = await provider.make_request(
|
|
300
|
+
RPCEndpoint("eth_chainId"), []
|
|
301
|
+
)
|
|
302
|
+
chain_id = int(chain_id_result["result"], 16)
|
|
303
|
+
|
|
304
|
+
if current_threshold is empty:
|
|
305
|
+
provider.request_cache_validation_threshold = (
|
|
306
|
+
CHAIN_VALIDATION_THRESHOLD_DEFAULTS.get(
|
|
307
|
+
chain_id, DEFAULT_VALIDATION_THRESHOLD
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
except Exception:
|
|
311
|
+
provider.request_cache_validation_threshold = DEFAULT_VALIDATION_THRESHOLD
|
|
312
|
+
finally:
|
|
313
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
314
|
+
|
|
315
|
+
|
|
217
316
|
async def _async_should_cache_response(
|
|
218
317
|
provider: ASYNC_PROVIDER_TYPE,
|
|
219
|
-
method:
|
|
318
|
+
method: RPCEndpoint,
|
|
220
319
|
params: Sequence[Any],
|
|
221
320
|
response: "RPCResponse",
|
|
222
321
|
) -> bool:
|
|
223
322
|
result = response.get("result", None)
|
|
224
323
|
if "error" in response or is_null(result):
|
|
225
324
|
return False
|
|
325
|
+
|
|
326
|
+
await async_set_threshold_if_empty(provider)
|
|
226
327
|
if (
|
|
227
328
|
method in ASYNC_INTERNAL_VALIDATION_MAP
|
|
228
329
|
and provider.request_cache_validation_threshold is not None
|
|
@@ -238,11 +339,11 @@ async def _async_should_cache_response(
|
|
|
238
339
|
|
|
239
340
|
def async_handle_request_caching(
|
|
240
341
|
func: Callable[
|
|
241
|
-
[ASYNC_PROVIDER_TYPE,
|
|
342
|
+
[ASYNC_PROVIDER_TYPE, RPCEndpoint, Any], Coroutine[Any, Any, "RPCResponse"]
|
|
242
343
|
],
|
|
243
344
|
) -> Callable[..., Coroutine[Any, Any, "RPCResponse"]]:
|
|
244
345
|
async def wrapper(
|
|
245
|
-
provider: ASYNC_PROVIDER_TYPE, method:
|
|
346
|
+
provider: ASYNC_PROVIDER_TYPE, method: RPCEndpoint, params: Any
|
|
246
347
|
) -> "RPCResponse":
|
|
247
348
|
if is_cacheable_request(provider, method, params):
|
|
248
349
|
request_cache = provider._request_cache
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import time
|
|
1
2
|
from typing import (
|
|
2
3
|
TYPE_CHECKING,
|
|
3
4
|
Any,
|
|
@@ -7,8 +8,11 @@ from typing import (
|
|
|
7
8
|
Union,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
|
-
from
|
|
11
|
-
|
|
11
|
+
from web3.types import (
|
|
12
|
+
RPCEndpoint,
|
|
13
|
+
)
|
|
14
|
+
from web3.utils import (
|
|
15
|
+
RequestCacheValidationThreshold,
|
|
12
16
|
)
|
|
13
17
|
|
|
14
18
|
if TYPE_CHECKING:
|
|
@@ -31,99 +35,252 @@ def _error_log(
|
|
|
31
35
|
)
|
|
32
36
|
|
|
33
37
|
|
|
34
|
-
def
|
|
38
|
+
def always_cache_request(*_args: Any, **_kwargs: Any) -> bool:
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def is_beyond_validation_threshold(
|
|
43
|
+
provider: SYNC_PROVIDER_TYPE,
|
|
44
|
+
blocknum: int = None,
|
|
45
|
+
block_timestamp: int = None,
|
|
46
|
+
) -> bool:
|
|
47
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
35
48
|
try:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
threshold = provider.request_cache_validation_threshold
|
|
50
|
+
|
|
51
|
+
# turn off caching to prevent recursion
|
|
52
|
+
provider.cache_allowed_requests = False
|
|
53
|
+
if isinstance(threshold, RequestCacheValidationThreshold):
|
|
54
|
+
# if mainnet and threshold is "finalized" or "safe"
|
|
55
|
+
threshold_block = provider.make_request(
|
|
56
|
+
RPCEndpoint("eth_getBlockByNumber"), [threshold.value, False]
|
|
57
|
+
)["result"]
|
|
58
|
+
# we should have a `blocknum` to compare against
|
|
59
|
+
return blocknum <= int(threshold_block["number"], 16)
|
|
60
|
+
elif isinstance(threshold, int):
|
|
61
|
+
if not block_timestamp:
|
|
62
|
+
# if validating via `blocknum` from params, we need to get the timestamp
|
|
63
|
+
# for the block with `blocknum`.
|
|
64
|
+
block = provider.make_request(
|
|
65
|
+
RPCEndpoint("eth_getBlockByNumber"), [hex(blocknum), False]
|
|
66
|
+
)["result"]
|
|
67
|
+
block_timestamp = int(block["timestamp"], 16)
|
|
68
|
+
|
|
69
|
+
# if validating via `block_timestamp` from result, we should have a
|
|
70
|
+
# `block_timestamp` to compare against
|
|
71
|
+
return block_timestamp <= time.time() - threshold
|
|
72
|
+
else:
|
|
73
|
+
provider.logger.error(
|
|
74
|
+
"Invalid request_cache_validation_threshold value. This should not "
|
|
75
|
+
f"have happened. Request not cached.\n threshold: {threshold}"
|
|
76
|
+
)
|
|
77
|
+
return False
|
|
40
78
|
except Exception as e:
|
|
41
79
|
_error_log(provider, e)
|
|
42
80
|
return False
|
|
81
|
+
finally:
|
|
82
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
43
83
|
|
|
44
84
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def validate_blocknum_in_params(
|
|
50
|
-
provider: SYNC_PROVIDER_TYPE, params: Sequence[Any], _result: Dict[str, Any]
|
|
85
|
+
def validate_from_block_id_in_params(
|
|
86
|
+
provider: SYNC_PROVIDER_TYPE,
|
|
87
|
+
params: Sequence[Any],
|
|
88
|
+
_result: Dict[str, Any],
|
|
51
89
|
) -> bool:
|
|
52
90
|
block_id = params[0]
|
|
53
91
|
if block_id == "earliest":
|
|
54
92
|
# `earliest` should always be cacheable
|
|
55
93
|
return True
|
|
56
|
-
|
|
57
|
-
|
|
94
|
+
|
|
95
|
+
blocknum = int(block_id, 16)
|
|
96
|
+
return is_beyond_validation_threshold(provider, blocknum=blocknum)
|
|
58
97
|
|
|
59
98
|
|
|
60
|
-
def
|
|
61
|
-
provider: SYNC_PROVIDER_TYPE,
|
|
99
|
+
def validate_from_blocknum_in_result(
|
|
100
|
+
provider: SYNC_PROVIDER_TYPE,
|
|
101
|
+
_params: Sequence[Any],
|
|
102
|
+
result: Dict[str, Any],
|
|
62
103
|
) -> bool:
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
104
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
105
|
+
try:
|
|
106
|
+
# turn off caching to prevent recursion
|
|
107
|
+
provider.cache_allowed_requests = False
|
|
66
108
|
|
|
109
|
+
# transaction results
|
|
110
|
+
if "blockNumber" in result:
|
|
111
|
+
blocknum = result.get("blockNumber")
|
|
112
|
+
# make an extra call to get the block values
|
|
113
|
+
block = provider.make_request(
|
|
114
|
+
RPCEndpoint("eth_getBlockByNumber"), [blocknum, False]
|
|
115
|
+
)["result"]
|
|
116
|
+
return is_beyond_validation_threshold(
|
|
117
|
+
provider,
|
|
118
|
+
blocknum=int(blocknum, 16),
|
|
119
|
+
block_timestamp=int(block["timestamp"], 16),
|
|
120
|
+
)
|
|
121
|
+
elif "number" in result:
|
|
122
|
+
return is_beyond_validation_threshold(
|
|
123
|
+
provider,
|
|
124
|
+
blocknum=int(result["number"], 16),
|
|
125
|
+
block_timestamp=int(result["timestamp"], 16),
|
|
126
|
+
)
|
|
127
|
+
else:
|
|
128
|
+
provider.logger.error(
|
|
129
|
+
"Could not find block number in result. This should not have happened. "
|
|
130
|
+
f"Request not cached.\n result: {result}",
|
|
131
|
+
)
|
|
132
|
+
return False
|
|
133
|
+
except Exception as e:
|
|
134
|
+
_error_log(provider, e)
|
|
135
|
+
return False
|
|
136
|
+
finally:
|
|
137
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
67
138
|
|
|
68
|
-
|
|
69
|
-
|
|
139
|
+
|
|
140
|
+
def validate_from_blockhash_in_params(
|
|
141
|
+
provider: SYNC_PROVIDER_TYPE,
|
|
142
|
+
params: Sequence[Any],
|
|
143
|
+
_result: Dict[str, Any],
|
|
70
144
|
) -> bool:
|
|
145
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
71
146
|
try:
|
|
147
|
+
# turn off caching to prevent recursion
|
|
148
|
+
provider.cache_allowed_requests = False
|
|
149
|
+
|
|
72
150
|
# make an extra call to get the block number from the hash
|
|
73
|
-
|
|
151
|
+
block = provider.make_request(
|
|
152
|
+
RPCEndpoint("eth_getBlockByHash"), [params[0], False]
|
|
153
|
+
)["result"]
|
|
154
|
+
return is_beyond_validation_threshold(
|
|
155
|
+
provider,
|
|
156
|
+
blocknum=int(block["number"], 16),
|
|
157
|
+
block_timestamp=int(block["timestamp"], 16),
|
|
158
|
+
)
|
|
74
159
|
except Exception as e:
|
|
75
160
|
_error_log(provider, e)
|
|
76
161
|
return False
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return is_beyond_validation_threshold(provider, blocknum)
|
|
162
|
+
finally:
|
|
163
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
80
164
|
|
|
81
165
|
|
|
82
166
|
# -- async -- #
|
|
83
167
|
|
|
84
168
|
|
|
85
169
|
async def async_is_beyond_validation_threshold(
|
|
86
|
-
provider: ASYNC_PROVIDER_TYPE,
|
|
170
|
+
provider: ASYNC_PROVIDER_TYPE,
|
|
171
|
+
blocknum: int = None,
|
|
172
|
+
block_timestamp: int = None,
|
|
87
173
|
) -> bool:
|
|
174
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
88
175
|
try:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
|
|
176
|
+
threshold = provider.request_cache_validation_threshold
|
|
177
|
+
|
|
178
|
+
# turn off caching to prevent recursion
|
|
179
|
+
provider.cache_allowed_requests = False
|
|
180
|
+
if isinstance(threshold, RequestCacheValidationThreshold):
|
|
181
|
+
# if mainnet and threshold is "finalized" or "safe"
|
|
182
|
+
threshold_block = await provider.make_request(
|
|
183
|
+
RPCEndpoint("eth_getBlockByNumber"), [threshold.value, False]
|
|
184
|
+
)
|
|
185
|
+
# we should have a `blocknum` to compare against
|
|
186
|
+
return blocknum <= int(threshold_block["result"]["number"], 16)
|
|
187
|
+
elif isinstance(threshold, int):
|
|
188
|
+
if not block_timestamp:
|
|
189
|
+
block = await provider.make_request(
|
|
190
|
+
RPCEndpoint("eth_getBlockByNumber"), [hex(blocknum), False]
|
|
191
|
+
)
|
|
192
|
+
block_timestamp = int(block["result"]["timestamp"], 16)
|
|
193
|
+
|
|
194
|
+
# if validating via `block_timestamp` from result, we should have a
|
|
195
|
+
# `block_timestamp` to compare against
|
|
196
|
+
return block_timestamp <= time.time() - threshold
|
|
197
|
+
else:
|
|
198
|
+
provider.logger.error(
|
|
199
|
+
"Invalid request_cache_validation_threshold value. This should not "
|
|
200
|
+
f"have happened. Request not cached.\n threshold: {threshold}"
|
|
201
|
+
)
|
|
202
|
+
return False
|
|
95
203
|
except Exception as e:
|
|
96
204
|
_error_log(provider, e)
|
|
97
205
|
return False
|
|
206
|
+
finally:
|
|
207
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
98
208
|
|
|
99
209
|
|
|
100
|
-
async def
|
|
101
|
-
provider: ASYNC_PROVIDER_TYPE,
|
|
210
|
+
async def async_validate_from_block_id_in_params(
|
|
211
|
+
provider: ASYNC_PROVIDER_TYPE,
|
|
212
|
+
params: Sequence[Any],
|
|
213
|
+
_result: Dict[str, Any],
|
|
102
214
|
) -> bool:
|
|
103
215
|
block_id = params[0]
|
|
104
216
|
if block_id == "earliest":
|
|
105
217
|
# `earliest` should always be cacheable
|
|
106
218
|
return True
|
|
107
|
-
|
|
108
|
-
|
|
219
|
+
|
|
220
|
+
blocknum = int(block_id, 16)
|
|
221
|
+
return await async_is_beyond_validation_threshold(provider, blocknum=blocknum)
|
|
109
222
|
|
|
110
223
|
|
|
111
|
-
async def
|
|
112
|
-
provider: ASYNC_PROVIDER_TYPE,
|
|
224
|
+
async def async_validate_from_blocknum_in_result(
|
|
225
|
+
provider: ASYNC_PROVIDER_TYPE,
|
|
226
|
+
_params: Sequence[Any],
|
|
227
|
+
result: Dict[str, Any],
|
|
113
228
|
) -> bool:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
229
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
230
|
+
try:
|
|
231
|
+
# turn off caching to prevent recursion
|
|
232
|
+
provider.cache_allowed_requests = False
|
|
233
|
+
|
|
234
|
+
# transaction results
|
|
235
|
+
if "blockNumber" in result:
|
|
236
|
+
blocknum = result.get("blockNumber")
|
|
237
|
+
# make an extra call to get the block values
|
|
238
|
+
block = await provider.make_request(
|
|
239
|
+
RPCEndpoint("eth_getBlockByNumber"), [blocknum, False]
|
|
240
|
+
)
|
|
241
|
+
return await async_is_beyond_validation_threshold(
|
|
242
|
+
provider,
|
|
243
|
+
blocknum=int(blocknum, 16),
|
|
244
|
+
block_timestamp=int(block["result"]["timestamp"], 16),
|
|
245
|
+
)
|
|
246
|
+
elif "number" in result:
|
|
247
|
+
return await async_is_beyond_validation_threshold(
|
|
248
|
+
provider,
|
|
249
|
+
blocknum=int(result["number"], 16),
|
|
250
|
+
block_timestamp=int(result["timestamp"], 16),
|
|
251
|
+
)
|
|
252
|
+
else:
|
|
253
|
+
provider.logger.error(
|
|
254
|
+
"Could not find block number in result. This should not have happened. "
|
|
255
|
+
f"Request not cached.\n result: {result}",
|
|
256
|
+
)
|
|
257
|
+
return False
|
|
258
|
+
except Exception as e:
|
|
259
|
+
_error_log(provider, e)
|
|
260
|
+
return False
|
|
261
|
+
finally:
|
|
262
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
117
263
|
|
|
118
264
|
|
|
119
|
-
async def
|
|
265
|
+
async def async_validate_from_blockhash_in_params(
|
|
120
266
|
provider: ASYNC_PROVIDER_TYPE, params: Sequence[Any], _result: Dict[str, Any]
|
|
121
267
|
) -> bool:
|
|
268
|
+
cache_allowed_requests = provider.cache_allowed_requests
|
|
122
269
|
try:
|
|
123
|
-
|
|
270
|
+
# turn off caching to prevent recursion
|
|
271
|
+
provider.cache_allowed_requests = False
|
|
272
|
+
|
|
273
|
+
# make an extra call to get the block number from the hash
|
|
274
|
+
response = await provider.make_request(
|
|
275
|
+
RPCEndpoint("eth_getBlockByHash"), [params[0], False]
|
|
276
|
+
)
|
|
277
|
+
return await async_is_beyond_validation_threshold(
|
|
278
|
+
provider,
|
|
279
|
+
blocknum=int(response["result"]["number"], 16),
|
|
280
|
+
block_timestamp=int(response["result"]["timestamp"], 16),
|
|
281
|
+
)
|
|
124
282
|
except Exception as e:
|
|
125
283
|
_error_log(provider, e)
|
|
126
284
|
return False
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return await async_is_beyond_validation_threshold(provider, blocknum)
|
|
285
|
+
finally:
|
|
286
|
+
provider.cache_allowed_requests = cache_allowed_requests
|
|
@@ -4,8 +4,8 @@ Compiled with Solidity v0.8.27.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
# source: web3/_utils/contract_sources/OffchainLookup.sol:OffchainLookup
|
|
7
|
-
OFFCHAIN_LOOKUP_BYTECODE = "
|
|
8
|
-
OFFCHAIN_LOOKUP_RUNTIME = "
|
|
7
|
+
OFFCHAIN_LOOKUP_BYTECODE = "0x608060405260405180604001604052806040518060600160405280602c815260200161105e602c913981526020016040518060400160405280601781526020017f68747470733a2f2f776562332e70792f676174657761790000000000000000008152508152505f906002610075929190610087565b50348015610081575f5ffd5b50610465565b828054828255905f5260205f209081019282156100cd579160200282015b828111156100cc5782518290816100bc9190610396565b50916020019190600101906100a5565b5b5090506100da91906100de565b5090565b5b808211156100fd575f81816100f49190610101565b506001016100df565b5090565b50805461010d906101bd565b5f825580601f1061011e575061013b565b601f0160209004905f5260205f209081019061013a919061013e565b5b50565b5b80821115610155575f815f90555060010161013f565b5090565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806101d457607f821691505b6020821081036101e7576101e6610190565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f600883026102497fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8261020e565b610253868361020e565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f61029761029261028d8461026b565b610274565b61026b565b9050919050565b5f819050919050565b6102b08361027d565b6102c46102bc8261029e565b84845461021a565b825550505050565b5f5f905090565b6102db6102cc565b6102e68184846102a7565b505050565b5b81811015610309576102fe5f826102d3565b6001810190506102ec565b5050565b601f82111561034e5761031f816101ed565b610328846101ff565b81016020851015610337578190505b61034b610343856101ff565b8301826102eb565b50505b505050565b5f82821c905092915050565b5f61036e5f1984600802610353565b1980831691505092915050565b5f610386838361035f565b9150826002028217905092915050565b61039f82610159565b67ffffffffffffffff8111156103b8576103b7610163565b5b6103c282546101bd565b6103cd82828561030d565b5f60209050601f8311600181146103fe575f84156103ec578287015190505b6103f6858261037b565b86555061045d565b601f19841661040c866101ed565b5f5b828110156104335784890151825560018201915060208501945060208101905061040e565b86831015610450578489015161044c601f89168261035f565b8355505b6001600288020188555050505b505050505050565b610bec806104725f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c806309a3c01b146100435780636337ed5814610061578063da96d05a14610091575b5f5ffd5b61004b6100c1565b60405161005891906103f2565b60405180910390f35b61007b60048036038101906100769190610484565b610110565b60405161008891906103f2565b60405180910390f35b6100ab60048036038101906100a691906104cf565b6101fc565b6040516100b891906103f2565b60405180910390f35b606080305f826309a3c01b60e01b846040517f556f1830000000000000000000000000000000000000000000000000000000008152600401610107959493929190610783565b60405180910390fd5b60605f83838101906101229190610911565b90507fd9bdd1345ca2a00d0c1413137c1b2b1d0a35e5b0e11508f3b3eff856286af07581604051602001610156919061099c565b60405160208183030381529060405280519060200120146101ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101a390610a0c565b60405180910390fd5b305f858563da96d05a60e01b88886040517f556f18300000000000000000000000000000000000000000000000000000000081526004016101f39796959493929190610a56565b60405180910390fd5b60605f858581019061020e9190610911565b90507faed76f463930323372899e36460e078e5292aac45f645bbe567be6fca83ede1081604051602001610242919061099c565b6040516020818303038152906040528051906020012014610298576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161028f90610b30565b60405180910390fd5b5f84848101906102a89190610911565b90507fd9bdd1345ca2a00d0c1413137c1b2b1d0a35e5b0e11508f3b3eff856286af075816040516020016102dc919061099c565b6040516020818303038152906040528051906020012014610332576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161032990610b98565b60405180910390fd5b86868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f8201169050808301925050505050505092505050949350505050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6103c482610382565b6103ce818561038c565b93506103de81856020860161039c565b6103e7816103aa565b840191505092915050565b5f6020820190508181035f83015261040a81846103ba565b905092915050565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5f83601f84011261044457610443610423565b5b8235905067ffffffffffffffff81111561046157610460610427565b5b60208301915083600182028301111561047d5761047c61042b565b5b9250929050565b5f5f6020838503121561049a5761049961041b565b5b5f83013567ffffffffffffffff8111156104b7576104b661041f565b5b6104c38582860161042f565b92509250509250929050565b5f5f5f5f604085870312156104e7576104e661041b565b5b5f85013567ffffffffffffffff8111156105045761050361041f565b5b6105108782880161042f565b9450945050602085013567ffffffffffffffff8111156105335761053261041f565b5b61053f8782880161042f565b925092505092959194509250565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105768261054d565b9050919050565b6105868161056c565b82525050565b5f81549050919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806105fc57607f821691505b60208210810361060f5761060e6105b8565b5b50919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b5f8154610643816105e5565b61064d8186610615565b9450600182165f8114610667576001811461067d576106af565b60ff1983168652811515602002860193506106af565b61068685610625565b5f5b838110156106a757815481890152600182019150602081019050610688565b808801955050505b50505092915050565b5f6106c38383610637565b905092915050565b5f600182019050919050565b5f6106e18261058c565b6106eb8185610596565b9350836020820285016106fd856105a6565b805f5b858110156107375784840389528161071885826106b8565b9450610723836106cb565b925060208a01995050600181019050610700565b50829750879550505050505092915050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61077d81610749565b82525050565b5f60a0820190506107965f83018861057d565b81810360208301526107a881876106d7565b905081810360408301526107bc81866103ba565b90506107cb6060830185610774565b81810360808301526107dd81846103ba565b90509695505050505050565b5f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610823826103aa565b810181811067ffffffffffffffff82111715610842576108416107ed565b5b80604052505050565b5f610854610412565b9050610860828261081a565b919050565b5f67ffffffffffffffff82111561087f5761087e6107ed565b5b610888826103aa565b9050602081019050919050565b828183375f83830152505050565b5f6108b56108b084610865565b61084b565b9050828152602081018484840111156108d1576108d06107e9565b5b6108dc848285610895565b509392505050565b5f82601f8301126108f8576108f7610423565b5b81356109088482602086016108a3565b91505092915050565b5f602082840312156109265761092561041b565b5b5f82013567ffffffffffffffff8111156109435761094261041f565b5b61094f848285016108e4565b91505092915050565b5f81519050919050565b5f81905092915050565b5f61097682610958565b6109808185610962565b935061099081856020860161039c565b80840191505092915050565b5f6109a7828461096c565b915081905092915050565b5f82825260208201905092915050565b7f7465737420646174612076616c69646174696f6e206661696c65642e000000005f82015250565b5f6109f6601c836109b2565b9150610a01826109c2565b602082019050919050565b5f6020820190508181035f830152610a23816109ea565b9050919050565b5f610a35838561038c565b9350610a42838584610895565b610a4b836103aa565b840190509392505050565b5f60a082019050610a695f83018a61057d565b8181036020830152610a7b81896106d7565b90508181036040830152610a90818789610a2a565b9050610a9f6060830186610774565b8181036080830152610ab2818486610a2a565b905098975050505050505050565b7f68747470207265717565737420726573756c742076616c69646174696f6e20665f8201527f61696c65642e0000000000000000000000000000000000000000000000000000602082015250565b5f610b1a6026836109b2565b9150610b2582610ac0565b604082019050919050565b5f6020820190508181035f830152610b4781610b0e565b9050919050565b7f6578747261446174612076616c69646174696f6e206661696c65642e000000005f82015250565b5f610b82601c836109b2565b9150610b8d82610b4e565b602082019050919050565b5f6020820190508181035f830152610baf81610b76565b905091905056fea264697066735822122057b6a2921a068efec1bdb94e9f03df697a3f59c9df2a9dad27bc3f325f5b629764736f6c634300081b003368747470733a2f2f776562332e70792f676174657761792f7b73656e6465727d2f7b646174617d2e6a736f6e" # noqa: E501
|
|
8
|
+
OFFCHAIN_LOOKUP_RUNTIME = "0x608060405234801561000f575f5ffd5b506004361061003f575f3560e01c806309a3c01b146100435780636337ed5814610061578063da96d05a14610091575b5f5ffd5b61004b6100c1565b60405161005891906103f2565b60405180910390f35b61007b60048036038101906100769190610484565b610110565b60405161008891906103f2565b60405180910390f35b6100ab60048036038101906100a691906104cf565b6101fc565b6040516100b891906103f2565b60405180910390f35b606080305f826309a3c01b60e01b846040517f556f1830000000000000000000000000000000000000000000000000000000008152600401610107959493929190610783565b60405180910390fd5b60605f83838101906101229190610911565b90507fd9bdd1345ca2a00d0c1413137c1b2b1d0a35e5b0e11508f3b3eff856286af07581604051602001610156919061099c565b60405160208183030381529060405280519060200120146101ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101a390610a0c565b60405180910390fd5b305f858563da96d05a60e01b88886040517f556f18300000000000000000000000000000000000000000000000000000000081526004016101f39796959493929190610a56565b60405180910390fd5b60605f858581019061020e9190610911565b90507faed76f463930323372899e36460e078e5292aac45f645bbe567be6fca83ede1081604051602001610242919061099c565b6040516020818303038152906040528051906020012014610298576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161028f90610b30565b60405180910390fd5b5f84848101906102a89190610911565b90507fd9bdd1345ca2a00d0c1413137c1b2b1d0a35e5b0e11508f3b3eff856286af075816040516020016102dc919061099c565b6040516020818303038152906040528051906020012014610332576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161032990610b98565b60405180910390fd5b86868080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f8201169050808301925050505050505092505050949350505050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6103c482610382565b6103ce818561038c565b93506103de81856020860161039c565b6103e7816103aa565b840191505092915050565b5f6020820190508181035f83015261040a81846103ba565b905092915050565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f5f83601f84011261044457610443610423565b5b8235905067ffffffffffffffff81111561046157610460610427565b5b60208301915083600182028301111561047d5761047c61042b565b5b9250929050565b5f5f6020838503121561049a5761049961041b565b5b5f83013567ffffffffffffffff8111156104b7576104b661041f565b5b6104c38582860161042f565b92509250509250929050565b5f5f5f5f604085870312156104e7576104e661041b565b5b5f85013567ffffffffffffffff8111156105045761050361041f565b5b6105108782880161042f565b9450945050602085013567ffffffffffffffff8111156105335761053261041f565b5b61053f8782880161042f565b925092505092959194509250565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105768261054d565b9050919050565b6105868161056c565b82525050565b5f81549050919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806105fc57607f821691505b60208210810361060f5761060e6105b8565b5b50919050565b5f82825260208201905092915050565b5f819050815f5260205f209050919050565b5f8154610643816105e5565b61064d8186610615565b9450600182165f8114610667576001811461067d576106af565b60ff1983168652811515602002860193506106af565b61068685610625565b5f5b838110156106a757815481890152600182019150602081019050610688565b808801955050505b50505092915050565b5f6106c38383610637565b905092915050565b5f600182019050919050565b5f6106e18261058c565b6106eb8185610596565b9350836020820285016106fd856105a6565b805f5b858110156107375784840389528161071885826106b8565b9450610723836106cb565b925060208a01995050600181019050610700565b50829750879550505050505092915050565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b61077d81610749565b82525050565b5f60a0820190506107965f83018861057d565b81810360208301526107a881876106d7565b905081810360408301526107bc81866103ba565b90506107cb6060830185610774565b81810360808301526107dd81846103ba565b90509695505050505050565b5f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610823826103aa565b810181811067ffffffffffffffff82111715610842576108416107ed565b5b80604052505050565b5f610854610412565b9050610860828261081a565b919050565b5f67ffffffffffffffff82111561087f5761087e6107ed565b5b610888826103aa565b9050602081019050919050565b828183375f83830152505050565b5f6108b56108b084610865565b61084b565b9050828152602081018484840111156108d1576108d06107e9565b5b6108dc848285610895565b509392505050565b5f82601f8301126108f8576108f7610423565b5b81356109088482602086016108a3565b91505092915050565b5f602082840312156109265761092561041b565b5b5f82013567ffffffffffffffff8111156109435761094261041f565b5b61094f848285016108e4565b91505092915050565b5f81519050919050565b5f81905092915050565b5f61097682610958565b6109808185610962565b935061099081856020860161039c565b80840191505092915050565b5f6109a7828461096c565b915081905092915050565b5f82825260208201905092915050565b7f7465737420646174612076616c69646174696f6e206661696c65642e000000005f82015250565b5f6109f6601c836109b2565b9150610a01826109c2565b602082019050919050565b5f6020820190508181035f830152610a23816109ea565b9050919050565b5f610a35838561038c565b9350610a42838584610895565b610a4b836103aa565b840190509392505050565b5f60a082019050610a695f83018a61057d565b8181036020830152610a7b81896106d7565b90508181036040830152610a90818789610a2a565b9050610a9f6060830186610774565b8181036080830152610ab2818486610a2a565b905098975050505050505050565b7f68747470207265717565737420726573756c742076616c69646174696f6e20665f8201527f61696c65642e0000000000000000000000000000000000000000000000000000602082015250565b5f610b1a6026836109b2565b9150610b2582610ac0565b604082019050919050565b5f6020820190508181035f830152610b4781610b0e565b9050919050565b7f6578747261446174612076616c69646174696f6e206661696c65642e000000005f82015250565b5f610b82601c836109b2565b9150610b8d82610b4e565b602082019050919050565b5f6020820190508181035f830152610baf81610b76565b905091905056fea264697066735822122057b6a2921a068efec1bdb94e9f03df697a3f59c9df2a9dad27bc3f325f5b629764736f6c634300081b0033" # noqa: E501
|
|
9
9
|
OFFCHAIN_LOOKUP_ABI = [
|
|
10
10
|
{
|
|
11
11
|
"inputs": [
|
|
@@ -113,7 +113,7 @@ UNKNOWN_HASH = HexStr(
|
|
|
113
113
|
# "test offchain lookup" as an abi-encoded string
|
|
114
114
|
OFFCHAIN_LOOKUP_TEST_DATA = "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001474657374206f6666636861696e206c6f6f6b7570000000000000000000000000" # noqa: E501
|
|
115
115
|
OFFCHAIN_LOOKUP_4BYTE_DATA = "0x556f1830"
|
|
116
|
-
OFFCHAIN_LOOKUP_RETURN_DATA = "
|
|
116
|
+
OFFCHAIN_LOOKUP_RETURN_DATA = "00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001a0da96d05a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002c68747470733a2f2f776562332e70792f676174657761792f7b73656e6465727d2f7b646174617d2e6a736f6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001768747470733a2f2f776562332e70792f6761746577617900000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001474657374206f6666636861696e206c6f6f6b757000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001474657374206f6666636861696e206c6f6f6b7570000000000000000000000000" # noqa: E501
|
|
117
117
|
# "web3py" as an abi-encoded string
|
|
118
118
|
WEB3PY_AS_HEXBYTES = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000067765623370790000000000000000000000000000000000000000000000000000" # noqa: E501
|
|
119
119
|
|
|
@@ -1628,7 +1628,7 @@ class AsyncEthModuleTest:
|
|
|
1628
1628
|
async_mock_offchain_lookup_request_response(
|
|
1629
1629
|
monkeypatch,
|
|
1630
1630
|
http_method="POST",
|
|
1631
|
-
mocked_request_url=
|
|
1631
|
+
mocked_request_url="https://web3.py/gateway",
|
|
1632
1632
|
mocked_status_code=200,
|
|
1633
1633
|
mocked_json_data=WEB3PY_AS_HEXBYTES,
|
|
1634
1634
|
sender=normalized_contract_address,
|
|
@@ -4091,7 +4091,7 @@ class EthModuleTest:
|
|
|
4091
4091
|
mock_offchain_lookup_request_response(
|
|
4092
4092
|
monkeypatch,
|
|
4093
4093
|
http_method="POST",
|
|
4094
|
-
mocked_request_url=
|
|
4094
|
+
mocked_request_url="https://web3.py/gateway",
|
|
4095
4095
|
mocked_status_code=200,
|
|
4096
4096
|
mocked_json_data=WEB3PY_AS_HEXBYTES,
|
|
4097
4097
|
sender=normalized_contract_address,
|
|
@@ -147,7 +147,7 @@ def mock_offchain_lookup_request_response(
|
|
|
147
147
|
if url_from_args == mocked_request_url:
|
|
148
148
|
assert kwargs["timeout"] == DEFAULT_HTTP_TIMEOUT
|
|
149
149
|
if http_method.upper() == "POST":
|
|
150
|
-
assert kwargs["
|
|
150
|
+
assert kwargs["json"] == {"data": calldata, "sender": sender}
|
|
151
151
|
return MockedResponse()
|
|
152
152
|
|
|
153
153
|
# else, make a normal request (no mocking)
|
|
@@ -196,8 +196,8 @@ def async_mock_offchain_lookup_request_response(
|
|
|
196
196
|
# mock response only to specified url while validating appropriate fields
|
|
197
197
|
if url_from_args == mocked_request_url:
|
|
198
198
|
assert kwargs["timeout"] == ClientTimeout(DEFAULT_HTTP_TIMEOUT)
|
|
199
|
-
if http_method.upper() == "
|
|
200
|
-
assert kwargs["
|
|
199
|
+
if http_method.upper() == "POST":
|
|
200
|
+
assert kwargs["json"] == {"data": calldata, "sender": sender}
|
|
201
201
|
return AsyncMockedResponse()
|
|
202
202
|
|
|
203
203
|
# else, make a normal request (no mocking)
|
|
@@ -35,6 +35,14 @@ class RequestMocker:
|
|
|
35
35
|
Context manager to mock requests made by a web3 instance. This is meant to be used
|
|
36
36
|
via a ``request_mocker`` fixture defined within the appropriate context.
|
|
37
37
|
|
|
38
|
+
************************************************************************************
|
|
39
|
+
Important: When mocking results, it's important to keep in mind the types that
|
|
40
|
+
clients return. For example, what we commonly translate to integers are returned
|
|
41
|
+
as hex strings in the RPC response and should be mocked as such for more
|
|
42
|
+
accurate testing.
|
|
43
|
+
************************************************************************************
|
|
44
|
+
|
|
45
|
+
|
|
38
46
|
Example:
|
|
39
47
|
-------
|
|
40
48
|
|
|
@@ -105,10 +113,9 @@ class RequestMocker:
|
|
|
105
113
|
|
|
106
114
|
return self
|
|
107
115
|
|
|
108
|
-
# define __exit__ with typing information
|
|
109
116
|
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
|
110
117
|
# mypy error: Cannot assign to a method
|
|
111
|
-
self.w3.provider.make_request = self._make_request # type: ignore[
|
|
118
|
+
self.w3.provider.make_request = self._make_request # type: ignore[assignment]
|
|
112
119
|
# reset request func cache to re-build request_func with original make_request
|
|
113
120
|
self.w3.provider._request_func_cache = (None, None)
|
|
114
121
|
|
|
@@ -167,6 +174,7 @@ class RequestMocker:
|
|
|
167
174
|
return mocked_response
|
|
168
175
|
|
|
169
176
|
# -- async -- #
|
|
177
|
+
|
|
170
178
|
async def __aenter__(self) -> "Self":
|
|
171
179
|
# mypy error: Cannot assign to a method
|
|
172
180
|
self.w3.provider.make_request = self._async_mock_request_handler # type: ignore[method-assign] # noqa: E501
|
|
@@ -176,7 +184,7 @@ class RequestMocker:
|
|
|
176
184
|
|
|
177
185
|
async def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
|
178
186
|
# mypy error: Cannot assign to a method
|
|
179
|
-
self.w3.provider.make_request = self._make_request # type: ignore[
|
|
187
|
+
self.w3.provider.make_request = self._make_request # type: ignore[assignment]
|
|
180
188
|
# reset request func cache to re-build request_func with original make_request
|
|
181
189
|
self.w3.provider._request_func_cache = (None, None)
|
|
182
190
|
|
web3/providers/async_base.py
CHANGED
|
@@ -10,6 +10,7 @@ from typing import (
|
|
|
10
10
|
Optional,
|
|
11
11
|
Set,
|
|
12
12
|
Tuple,
|
|
13
|
+
Union,
|
|
13
14
|
cast,
|
|
14
15
|
)
|
|
15
16
|
|
|
@@ -21,7 +22,10 @@ from eth_utils import (
|
|
|
21
22
|
|
|
22
23
|
from web3._utils.caching import (
|
|
23
24
|
CACHEABLE_REQUESTS,
|
|
24
|
-
|
|
25
|
+
)
|
|
26
|
+
from web3._utils.empty import (
|
|
27
|
+
Empty,
|
|
28
|
+
empty,
|
|
25
29
|
)
|
|
26
30
|
from web3._utils.encoding import (
|
|
27
31
|
FriendlyJsonSerde,
|
|
@@ -88,8 +92,8 @@ class AsyncBaseProvider:
|
|
|
88
92
|
cache_allowed_requests: bool = False,
|
|
89
93
|
cacheable_requests: Set[RPCEndpoint] = None,
|
|
90
94
|
request_cache_validation_threshold: Optional[
|
|
91
|
-
RequestCacheValidationThreshold
|
|
92
|
-
] =
|
|
95
|
+
Union[RequestCacheValidationThreshold, int, Empty]
|
|
96
|
+
] = empty,
|
|
93
97
|
) -> None:
|
|
94
98
|
self._request_cache = SimpleCache(1000)
|
|
95
99
|
self.cache_allowed_requests = cache_allowed_requests
|
|
@@ -132,7 +136,6 @@ class AsyncBaseProvider:
|
|
|
132
136
|
self._batch_request_func_cache = (middleware, accumulator_fn)
|
|
133
137
|
return self._batch_request_func_cache[-1]
|
|
134
138
|
|
|
135
|
-
@async_handle_request_caching
|
|
136
139
|
async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
137
140
|
raise NotImplementedError("Providers must implement this method")
|
|
138
141
|
|
web3/providers/base.py
CHANGED
|
@@ -9,6 +9,7 @@ from typing import (
|
|
|
9
9
|
Optional,
|
|
10
10
|
Set,
|
|
11
11
|
Tuple,
|
|
12
|
+
Union,
|
|
12
13
|
cast,
|
|
13
14
|
)
|
|
14
15
|
|
|
@@ -19,7 +20,10 @@ from eth_utils import (
|
|
|
19
20
|
|
|
20
21
|
from web3._utils.caching import (
|
|
21
22
|
CACHEABLE_REQUESTS,
|
|
22
|
-
|
|
23
|
+
)
|
|
24
|
+
from web3._utils.empty import (
|
|
25
|
+
Empty,
|
|
26
|
+
empty,
|
|
23
27
|
)
|
|
24
28
|
from web3._utils.encoding import (
|
|
25
29
|
FriendlyJsonSerde,
|
|
@@ -71,8 +75,8 @@ class BaseProvider:
|
|
|
71
75
|
cache_allowed_requests: bool = False,
|
|
72
76
|
cacheable_requests: Set[RPCEndpoint] = None,
|
|
73
77
|
request_cache_validation_threshold: Optional[
|
|
74
|
-
RequestCacheValidationThreshold
|
|
75
|
-
] =
|
|
78
|
+
Union[RequestCacheValidationThreshold, int, Empty]
|
|
79
|
+
] = empty,
|
|
76
80
|
) -> None:
|
|
77
81
|
self._request_cache = SimpleCache(1000)
|
|
78
82
|
self.cache_allowed_requests = cache_allowed_requests
|
|
@@ -104,7 +108,6 @@ class BaseProvider:
|
|
|
104
108
|
|
|
105
109
|
return self._request_func_cache[-1]
|
|
106
110
|
|
|
107
|
-
@handle_request_caching
|
|
108
111
|
def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
109
112
|
raise NotImplementedError("Providers must implement this method")
|
|
110
113
|
|
web3/providers/ipc.py
CHANGED
|
@@ -32,6 +32,9 @@ from web3.types import (
|
|
|
32
32
|
from .._utils.batching import (
|
|
33
33
|
sort_batch_response_by_response_ids,
|
|
34
34
|
)
|
|
35
|
+
from .._utils.caching import (
|
|
36
|
+
handle_request_caching,
|
|
37
|
+
)
|
|
35
38
|
from ..exceptions import (
|
|
36
39
|
Web3TypeError,
|
|
37
40
|
Web3ValueError,
|
|
@@ -189,6 +192,7 @@ class IPCProvider(JSONBaseProvider):
|
|
|
189
192
|
timeout.sleep(0)
|
|
190
193
|
continue
|
|
191
194
|
|
|
195
|
+
@handle_request_caching
|
|
192
196
|
def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
|
|
193
197
|
self.logger.debug(
|
|
194
198
|
f"Making request IPC. Path: {self.ipc_path}, Method: {method}"
|
|
@@ -56,15 +56,12 @@ async def async_handle_offchain_lookup(
|
|
|
56
56
|
response = await session.get(
|
|
57
57
|
formatted_url, timeout=ClientTimeout(DEFAULT_HTTP_TIMEOUT)
|
|
58
58
|
)
|
|
59
|
-
|
|
59
|
+
else:
|
|
60
60
|
response = await session.post(
|
|
61
61
|
formatted_url,
|
|
62
|
-
|
|
62
|
+
json={"data": formatted_data, "sender": formatted_sender},
|
|
63
63
|
timeout=ClientTimeout(DEFAULT_HTTP_TIMEOUT),
|
|
64
64
|
)
|
|
65
|
-
else:
|
|
66
|
-
await session.close()
|
|
67
|
-
raise Web3ValidationError("url not formatted properly.")
|
|
68
65
|
except Exception:
|
|
69
66
|
continue # try next url if timeout or issues making the request
|
|
70
67
|
|
web3/utils/exception_handling.py
CHANGED
|
@@ -51,17 +51,12 @@ def handle_offchain_lookup(
|
|
|
51
51
|
try:
|
|
52
52
|
if "{data}" in url and "{sender}" in url:
|
|
53
53
|
response = session.get(formatted_url, timeout=DEFAULT_HTTP_TIMEOUT)
|
|
54
|
-
|
|
54
|
+
else:
|
|
55
55
|
response = session.post(
|
|
56
56
|
formatted_url,
|
|
57
|
-
|
|
58
|
-
"data": formatted_data,
|
|
59
|
-
"sender": formatted_sender,
|
|
60
|
-
},
|
|
57
|
+
json={"data": formatted_data, "sender": formatted_sender},
|
|
61
58
|
timeout=DEFAULT_HTTP_TIMEOUT,
|
|
62
59
|
)
|
|
63
|
-
else:
|
|
64
|
-
raise Web3ValidationError("url not formatted properly.")
|
|
65
60
|
except Exception:
|
|
66
61
|
continue # try next url if timeout or issues making the request
|
|
67
62
|
|
|
@@ -59,8 +59,8 @@ web3/_utils/utility_methods.py,sha256=7VCpo5ysvPoGjFuDj5gT1Niva2l3BADUvNeJFlL3Yv
|
|
|
59
59
|
web3/_utils/validation.py,sha256=TYfkiOIT37zvRy1PqvP9lvbdfLmn1HvlBbHvrFifx1A,6351
|
|
60
60
|
web3/_utils/windows.py,sha256=IlFUtqYSbUUfFRx60zvEwpiZd080WpOrA4ojm4tmSEE,994
|
|
61
61
|
web3/_utils/caching/__init__.py,sha256=ri-5UGz5PPuYW9W1c2BX5lUJn1oZuvErbDz5NweiveA,284
|
|
62
|
-
web3/_utils/caching/caching_utils.py,sha256=
|
|
63
|
-
web3/_utils/caching/request_caching_validation.py,sha256=
|
|
62
|
+
web3/_utils/caching/caching_utils.py,sha256=Miqhx1Ne9FVlQK8xVKsMzglIanr6vO887Rm5FWB0KGI,11739
|
|
63
|
+
web3/_utils/caching/request_caching_validation.py,sha256=iKebggGM33toanGpfEO0Zx1sIWYfShazdnHnRXlddI4,9839
|
|
64
64
|
web3/_utils/compat/__init__.py,sha256=RUD0S8wzEv2a9o1UhJD0SIECjzatjJl7vc6RCM2d1Fs,571
|
|
65
65
|
web3/_utils/contract_sources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
66
|
web3/_utils/contract_sources/compile_contracts.py,sha256=C3eLY6gJ4xj9FunNwn4YPb9c8ElORkN8O4ddBa_kKqI,6486
|
|
@@ -76,7 +76,7 @@ web3/_utils/contract_sources/contract_data/extended_resolver.py,sha256=tY1mcWTI2
|
|
|
76
76
|
web3/_utils/contract_sources/contract_data/fallback_function_contract.py,sha256=1weuAAyUYH_loLlTiC-R3CsodsI95k5Dn5NJqGlI8qQ,1649
|
|
77
77
|
web3/_utils/contract_sources/contract_data/function_name_tester_contract.py,sha256=qQwJK54vprpBapkWqPfBI3YXpdfDtI9W73b04x19IkU,1894
|
|
78
78
|
web3/_utils/contract_sources/contract_data/math_contract.py,sha256=7KoMC9ppWjn0h1Rw5gb-oo65pul6Ar_KmY24_eYcPAs,7635
|
|
79
|
-
web3/_utils/contract_sources/contract_data/offchain_lookup.py,sha256=
|
|
79
|
+
web3/_utils/contract_sources/contract_data/offchain_lookup.py,sha256=3QuoZdV7DnXKeJdRRz08NwQQaVbHFgsEZ4fwTgX_GFQ,16424
|
|
80
80
|
web3/_utils/contract_sources/contract_data/offchain_resolver.py,sha256=O_Xl5rfnRXGaOy5TXK25Ch-r7JhA4n-jYAsQi-EPKGI,31829
|
|
81
81
|
web3/_utils/contract_sources/contract_data/panic_errors_contract.py,sha256=tOLQHKGuD0pgJC9lC_94DEbf0ERxYsxMeRgugMDI4Ic,15263
|
|
82
82
|
web3/_utils/contract_sources/contract_data/payable_tester.py,sha256=0LWImIizIiiX8c6x-A8NOsov_837RD8WUzfJ-RybaoA,1827
|
|
@@ -88,13 +88,13 @@ web3/_utils/contract_sources/contract_data/storage_contract.py,sha256=VrIyFZ6gPp
|
|
|
88
88
|
web3/_utils/contract_sources/contract_data/string_contract.py,sha256=wuNXF7O6Y_FP178b5YeBSZdBwrDGJdrv43GxtHs2AMA,11228
|
|
89
89
|
web3/_utils/contract_sources/contract_data/tuple_contracts.py,sha256=wy_blOQtNL6ipn-sxDGhi5MgUQW7s_8JW5DkcMeQdbk,23176
|
|
90
90
|
web3/_utils/module_testing/__init__.py,sha256=tPFAaX7xOR50CPTq24UeY-1CX1LQxxmEOPr0-tIRkoE,376
|
|
91
|
-
web3/_utils/module_testing/eth_module.py,sha256=
|
|
91
|
+
web3/_utils/module_testing/eth_module.py,sha256=Rhb6aq9G6pPVMfShPAAHoFIv_JeA1jvagCfjbLUxk78,187521
|
|
92
92
|
web3/_utils/module_testing/go_ethereum_admin_module.py,sha256=_c-6SyzZkfAJ-7ySXUpw9FEr4cg-ShRdAGSAHWanCtY,3406
|
|
93
93
|
web3/_utils/module_testing/go_ethereum_txpool_module.py,sha256=5f8XL8-2x3keyGRaITxMQYl9oQzjgqGn8zobB-j9BPs,1176
|
|
94
|
-
web3/_utils/module_testing/module_testing_utils.py,sha256=
|
|
94
|
+
web3/_utils/module_testing/module_testing_utils.py,sha256=cYkvrgrtJJ8Tsu61S1jQGfCOadc6GuYZOuADL_J91DE,7304
|
|
95
95
|
web3/_utils/module_testing/net_module.py,sha256=ifUTC-5fTcQbwpm0X09OdD5RSPnn00T8klFeYe8tTm4,1272
|
|
96
96
|
web3/_utils/module_testing/persistent_connection_provider.py,sha256=SPj5PKS9_1OncT2_Zx8x75Xj2hl22r4ciKqqsjxoS1w,17885
|
|
97
|
-
web3/_utils/module_testing/utils.py,sha256=
|
|
97
|
+
web3/_utils/module_testing/utils.py,sha256=uFHV36vfp1DFlweXxFZ2lsi8VjkxxDCqLpIJ0HDNFoU,10403
|
|
98
98
|
web3/_utils/module_testing/web3_module.py,sha256=7c6penGbHn381fPTYY6DsXKv56xGQpYkHljOsr2gbaw,25413
|
|
99
99
|
web3/auto/__init__.py,sha256=ZbzAiCZMdt_tCTTPvH6t8NCVNroKKkt7TSVBBNR74Is,44
|
|
100
100
|
web3/auto/gethdev.py,sha256=MuWD2gxv0xDv_SzPsp9mSkS1oG4P54xFK83qw9NvswA,438
|
|
@@ -128,10 +128,10 @@ web3/middleware/signing.py,sha256=1DOYxpmCra-Qq5r42237q3b54uDO-QHjMVMulxVpLVQ,58
|
|
|
128
128
|
web3/middleware/stalecheck.py,sha256=oWRA69BGIbNGjHSnUVOBnoxOYJZYjzRzlqqL5RRlnzk,2680
|
|
129
129
|
web3/middleware/validation.py,sha256=QxActrJk_zsXXiwpadP2MUjZBS5E50OJOtUwVrm9XVo,4280
|
|
130
130
|
web3/providers/__init__.py,sha256=YkcSzE9AubvSp-UfvJjyCrdepvziysbqeq2LT0ImDoc,936
|
|
131
|
-
web3/providers/async_base.py,sha256=
|
|
131
|
+
web3/providers/async_base.py,sha256=6UON8Ocsif93uqPWIdtubzwg6JpCYHf2JLao-F19LL4,7383
|
|
132
132
|
web3/providers/auto.py,sha256=Zx3CHKoRkmiw3Jte2BLNPiJAFd8rDXNGfA3XtxZvHgc,3465
|
|
133
|
-
web3/providers/base.py,sha256=
|
|
134
|
-
web3/providers/ipc.py,sha256=
|
|
133
|
+
web3/providers/base.py,sha256=QtBsOCdYeIBF1d3MzQJG0Vo5LduHgAbdiNDcXs9ZQyo,6425
|
|
134
|
+
web3/providers/ipc.py,sha256=3kO7GE8IxVqwpqgfLdomn-L_SeYG48Qs94FsvSdG-tI,6444
|
|
135
135
|
web3/providers/legacy_websocket.py,sha256=6mFEQVlVoc71AyDvPGGt3KQudQKdMxoHvokg6Fb4VOw,4767
|
|
136
136
|
web3/providers/eth_tester/__init__.py,sha256=UggyBQdeAyjy1awATW1933jkJcpqqaUYUQEFAQnA2o0,163
|
|
137
137
|
web3/providers/eth_tester/defaults.py,sha256=9aPe0x2C5wahdGI_rZjiGR3Fe2LbMjKWH9o6eZPuY-Q,12577
|
|
@@ -156,11 +156,11 @@ web3/scripts/release/test_package.py,sha256=DH0AryllcF4zfpWSd0NLPSQGHNoC-Qng5WYY
|
|
|
156
156
|
web3/utils/__init__.py,sha256=zwf8bM-Dl2_L9u_cR5K22_krl0vx4QBAANoBssmbDlU,1857
|
|
157
157
|
web3/utils/abi.py,sha256=iSBaCW41UxB1q1UHDPwJ1QJbdiuYmTWNu8m6E28DqlY,18672
|
|
158
158
|
web3/utils/address.py,sha256=KC_IpEbixSCuMhaW6V2QCyyJTYKYGS9c8QtI9_aH7zQ,967
|
|
159
|
-
web3/utils/async_exception_handling.py,sha256=
|
|
159
|
+
web3/utils/async_exception_handling.py,sha256=OoKbLNwWcY9dxLCbOfxcQPSB1OxWraNqcw8V0ZX-JaQ,3173
|
|
160
160
|
web3/utils/caching.py,sha256=Rkt5IN8A7YBZYXlCRaWa64FiDhTXsxzTGGQdGBB4Nzw,2514
|
|
161
|
-
web3/utils/exception_handling.py,sha256=
|
|
162
|
-
web3-7.
|
|
163
|
-
web3-7.
|
|
164
|
-
web3-7.
|
|
165
|
-
web3-7.
|
|
166
|
-
web3-7.
|
|
161
|
+
web3/utils/exception_handling.py,sha256=n-MtO5LNzJDVzHTzO6olzfb2_qEVtVRvink0ixswg-Y,2917
|
|
162
|
+
web3-7.4.0.dist-info/LICENSE,sha256=ScEyLx1vWrB0ybKiZKKTXm5QhVksHCEUtTp4lwYV45I,1095
|
|
163
|
+
web3-7.4.0.dist-info/METADATA,sha256=UorW1bAPlnfCwiyOsBE-3cOVYoetp_PC32ABTvpCijE,5317
|
|
164
|
+
web3-7.4.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
|
165
|
+
web3-7.4.0.dist-info/top_level.txt,sha256=iwupuJh7wgypXrpk_awszyri3TahRr5vxSphNyvt1bU,9
|
|
166
|
+
web3-7.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|