solver-multirpc 3.1.4__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.
- solver_multirpc-3.1.4.dist-info/METADATA +241 -0
- solver_multirpc-3.1.4.dist-info/RECORD +19 -0
- solver_multirpc-3.1.4.dist-info/WHEEL +4 -0
- src/__init__.py +0 -0
- src/multirpc/__init__.py +1 -0
- src/multirpc/async_multi_rpc_interface.py +125 -0
- src/multirpc/base_multi_rpc_interface.py +640 -0
- src/multirpc/constants.py +52 -0
- src/multirpc/exceptions.py +77 -0
- src/multirpc/gas_estimation.py +151 -0
- src/multirpc/sync_multi_rpc_interface.py +135 -0
- src/multirpc/tx_trace.py +153 -0
- src/multirpc/utils.py +265 -0
- src/tests/__init__.py +0 -0
- src/tests/abi.json +34 -0
- src/tests/constants.py +78 -0
- src/tests/contract.sol +16 -0
- src/tests/test.py +138 -0
- src/tests/test_settings.py.example +7 -0
src/multirpc/utils.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import enum
|
|
3
|
+
import json
|
|
4
|
+
import time
|
|
5
|
+
import traceback
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from functools import reduce, wraps
|
|
8
|
+
from threading import Thread
|
|
9
|
+
from typing import Any, Dict, List, Tuple, Union
|
|
10
|
+
|
|
11
|
+
import aiohttp.client_exceptions
|
|
12
|
+
from aiohttp import ClientTimeout
|
|
13
|
+
from eth_typing import URI
|
|
14
|
+
from web3 import AsyncHTTPProvider, AsyncWeb3, Web3, WebSocketProvider
|
|
15
|
+
from web3._utils.http import DEFAULT_HTTP_TIMEOUT
|
|
16
|
+
from web3._utils.http_session_manager import HTTPSessionManager
|
|
17
|
+
from web3.middleware import ExtraDataToPOAMiddleware
|
|
18
|
+
|
|
19
|
+
from .constants import MaxRPCInEachBracket, MultiRPCLogger
|
|
20
|
+
from .exceptions import AtLastProvideOneValidRPCInEachBracket, MaximumRPCInEachBracketReached
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_span_proper_label_from_provider(endpoint_uri):
|
|
24
|
+
return endpoint_uri.split("//")[-1].replace(".", "__").replace("/", "__")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ReturnableThread(Thread):
|
|
28
|
+
def __init__(self, target, args=(), kwargs=None):
|
|
29
|
+
super().__init__(target=target, args=args, kwargs=kwargs)
|
|
30
|
+
self.target = target
|
|
31
|
+
self.args = args
|
|
32
|
+
self.kwargs = kwargs if kwargs is not None else {}
|
|
33
|
+
self.result = None
|
|
34
|
+
self._exception = None
|
|
35
|
+
|
|
36
|
+
def run(self) -> None:
|
|
37
|
+
try:
|
|
38
|
+
self.result = self.target(*self.args, **self.kwargs)
|
|
39
|
+
except Exception as e:
|
|
40
|
+
self._exception = e
|
|
41
|
+
traceback.print_exc()
|
|
42
|
+
|
|
43
|
+
def join(self, *args, **kwargs):
|
|
44
|
+
super().join(*args, **kwargs)
|
|
45
|
+
if self._exception:
|
|
46
|
+
raise self._exception
|
|
47
|
+
return self.result
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def thread_safe(func):
|
|
51
|
+
@wraps(func)
|
|
52
|
+
def wrapper(*args, **kwargs):
|
|
53
|
+
event_loop = asyncio._get_running_loop()
|
|
54
|
+
if event_loop is None:
|
|
55
|
+
return func(*args, **kwargs)
|
|
56
|
+
t = ReturnableThread(target=func, args=args, kwargs=kwargs)
|
|
57
|
+
t.start()
|
|
58
|
+
return t.join()
|
|
59
|
+
|
|
60
|
+
return wrapper
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ResultEvent(asyncio.Event):
|
|
64
|
+
def __init__(self):
|
|
65
|
+
super().__init__()
|
|
66
|
+
self.result_ = None
|
|
67
|
+
|
|
68
|
+
def set_result(self, result):
|
|
69
|
+
self.result_ = result
|
|
70
|
+
|
|
71
|
+
def get_result(self):
|
|
72
|
+
return self.result_
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_unix_time():
|
|
76
|
+
return int(time.time() * 1000)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class TxPriority(enum.Enum):
|
|
80
|
+
Low = "low"
|
|
81
|
+
Medium = "medium"
|
|
82
|
+
High = "high"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ContractFunctionType:
|
|
86
|
+
View = "view"
|
|
87
|
+
Transaction = "transaction"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class NestedDict:
|
|
91
|
+
def __init__(self, data: Dict = None):
|
|
92
|
+
if data is None:
|
|
93
|
+
data = dict()
|
|
94
|
+
self.data = data
|
|
95
|
+
|
|
96
|
+
def __getitem__(self, keys: Union[Tuple[any], any]):
|
|
97
|
+
if not isinstance(keys, tuple):
|
|
98
|
+
keys = (keys,)
|
|
99
|
+
result = self.data
|
|
100
|
+
for key in keys:
|
|
101
|
+
result = result[key]
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
def __setitem__(self, keys: Union[Tuple[any], any], value) -> None:
|
|
105
|
+
if not isinstance(keys, tuple):
|
|
106
|
+
keys = (keys,)
|
|
107
|
+
current_dict = self.data
|
|
108
|
+
for key in keys[:-1]:
|
|
109
|
+
if not isinstance(current_dict.get(key), dict):
|
|
110
|
+
current_dict[key] = {}
|
|
111
|
+
current_dict = current_dict[key]
|
|
112
|
+
current_dict[keys[-1]] = value
|
|
113
|
+
|
|
114
|
+
def get(self, keys, default=None):
|
|
115
|
+
if not isinstance(keys, tuple):
|
|
116
|
+
keys = (keys,)
|
|
117
|
+
current_dict = self.data
|
|
118
|
+
for key in keys:
|
|
119
|
+
try:
|
|
120
|
+
current_dict = current_dict[key]
|
|
121
|
+
except KeyError:
|
|
122
|
+
return default
|
|
123
|
+
return current_dict
|
|
124
|
+
|
|
125
|
+
def items(self):
|
|
126
|
+
def get_items_recursive(data, current_keys=()):
|
|
127
|
+
for key, value in data.items():
|
|
128
|
+
if isinstance(value, dict):
|
|
129
|
+
yield from get_items_recursive(value, current_keys + (key,))
|
|
130
|
+
else:
|
|
131
|
+
yield current_keys + (key,), value
|
|
132
|
+
|
|
133
|
+
return get_items_recursive(self.data)
|
|
134
|
+
|
|
135
|
+
def __str__(self):
|
|
136
|
+
return str(self.data)
|
|
137
|
+
|
|
138
|
+
def __repr__(self):
|
|
139
|
+
return json.dumps(self.data, indent=1)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class MultiRpcHTTPSessionManager(HTTPSessionManager):
|
|
143
|
+
"""
|
|
144
|
+
This class extends the default HTTPSessionManager used by Web3 to ensure that
|
|
145
|
+
the aiohttp ClientSession is always closed—even in the case of a failure or
|
|
146
|
+
cancellation. By placing session closure inside a 'finally' block in both
|
|
147
|
+
'async_make_post_request' and 'async_json_make_get_request', we guarantee
|
|
148
|
+
proper cleanup of network connections and resources, preventing potential
|
|
149
|
+
resource leaks or connection pooling issues if an exception is raised during
|
|
150
|
+
the request or the task is cancelled.
|
|
151
|
+
|
|
152
|
+
NOTE: It's based on web3==7.7.0 . If you update web3 check if it's compatible.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
async def async_make_post_request(
|
|
156
|
+
self, endpoint_uri: URI, data: Union[bytes, Dict[str, Any]], **kwargs: Any
|
|
157
|
+
) -> bytes:
|
|
158
|
+
kwargs.setdefault("timeout", ClientTimeout(DEFAULT_HTTP_TIMEOUT))
|
|
159
|
+
session = await self.async_cache_and_return_session(
|
|
160
|
+
endpoint_uri, request_timeout=kwargs["timeout"]
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
self.logger.debug(f'making post request, {endpoint_uri=}, {kwargs=}')
|
|
165
|
+
response = await session.post(endpoint_uri, **dict(**kwargs, data=data))
|
|
166
|
+
response.raise_for_status()
|
|
167
|
+
return await response.read()
|
|
168
|
+
finally:
|
|
169
|
+
self.logger.debug(f'task is done/canceled, session will close, {session=}')
|
|
170
|
+
if not session.closed:
|
|
171
|
+
await session.close()
|
|
172
|
+
|
|
173
|
+
async def async_json_make_get_request(
|
|
174
|
+
self, endpoint_uri: URI, *args: Any, **kwargs: Any
|
|
175
|
+
) -> Dict[str, Any]:
|
|
176
|
+
kwargs.setdefault("timeout", ClientTimeout(DEFAULT_HTTP_TIMEOUT))
|
|
177
|
+
session = await self.async_cache_and_return_session(
|
|
178
|
+
endpoint_uri, request_timeout=kwargs["timeout"]
|
|
179
|
+
)
|
|
180
|
+
try:
|
|
181
|
+
response = await session.get(endpoint_uri, *args, **kwargs)
|
|
182
|
+
response.raise_for_status()
|
|
183
|
+
return await response.json()
|
|
184
|
+
finally:
|
|
185
|
+
self.logger.debug(f'task is done/canceled, {session=}')
|
|
186
|
+
if not session.closed:
|
|
187
|
+
await session.close()
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class MultiRpcAsyncHTTPProvider(AsyncHTTPProvider):
|
|
191
|
+
"""
|
|
192
|
+
NOTE: It's based on web3==7.7.0 . If you update web3 check if it's compatible.
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
def __init__(self, *args, **kwargs):
|
|
196
|
+
super().__init__(*args, **kwargs)
|
|
197
|
+
self._request_session_manager = MultiRpcHTTPSessionManager()
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
async def create_web3_from_rpc(rpc_urls: NestedDict, is_proof_of_authority: bool) -> NestedDict:
|
|
201
|
+
async def create_web3(rpc_: str):
|
|
202
|
+
async_w3: AsyncWeb3
|
|
203
|
+
if rpc_.startswith("http"):
|
|
204
|
+
async_w3 = AsyncWeb3(MultiRpcAsyncHTTPProvider(rpc_))
|
|
205
|
+
else:
|
|
206
|
+
async_w3 = AsyncWeb3(WebSocketProvider(rpc_))
|
|
207
|
+
if is_proof_of_authority:
|
|
208
|
+
async_w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
|
|
209
|
+
try:
|
|
210
|
+
status = await async_w3.is_connected()
|
|
211
|
+
except (asyncio.exceptions.TimeoutError, aiohttp.client_exceptions.ClientResponseError):
|
|
212
|
+
status = False
|
|
213
|
+
return async_w3, status
|
|
214
|
+
|
|
215
|
+
providers = NestedDict()
|
|
216
|
+
for key, rpcs in rpc_urls.items():
|
|
217
|
+
valid_rpcs = []
|
|
218
|
+
|
|
219
|
+
if len(rpcs) > MaxRPCInEachBracket:
|
|
220
|
+
raise MaximumRPCInEachBracketReached
|
|
221
|
+
|
|
222
|
+
for i, rpc in enumerate(rpcs):
|
|
223
|
+
w3, w3_connected = await create_web3(rpc)
|
|
224
|
+
if not w3_connected:
|
|
225
|
+
MultiRPCLogger.warning(f"This rpc({rpc}) doesn't work")
|
|
226
|
+
continue
|
|
227
|
+
valid_rpcs.append(w3)
|
|
228
|
+
|
|
229
|
+
if len(valid_rpcs) == 0:
|
|
230
|
+
raise AtLastProvideOneValidRPCInEachBracket
|
|
231
|
+
|
|
232
|
+
providers[key] = valid_rpcs
|
|
233
|
+
|
|
234
|
+
return providers
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
async def calculate_chain_id(providers: NestedDict) -> int:
|
|
238
|
+
last_error = None
|
|
239
|
+
for key, providers in providers.items():
|
|
240
|
+
for provider in providers:
|
|
241
|
+
try:
|
|
242
|
+
return await asyncio.wait_for(provider.eth.chain_id, timeout=2)
|
|
243
|
+
except asyncio.TimeoutError as e:
|
|
244
|
+
last_error = e
|
|
245
|
+
MultiRPCLogger.warning(f"Can't acquire chain id from this RPC {provider.provider.endpoint_uri}")
|
|
246
|
+
raise last_error
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def reduce_list_of_list(ls: List[List]) -> List[any]:
|
|
250
|
+
return reduce(lambda ps, p: ps + p, ls)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@dataclass
|
|
254
|
+
class ChainConfigTest:
|
|
255
|
+
name: str
|
|
256
|
+
contract_address: str
|
|
257
|
+
rpc: NestedDict
|
|
258
|
+
tx_hash: str
|
|
259
|
+
is_proof_authority: bool = False
|
|
260
|
+
multicall_address: str = None
|
|
261
|
+
|
|
262
|
+
def __post_init__(self):
|
|
263
|
+
self.contract_address = Web3.to_checksum_address(self.contract_address)
|
|
264
|
+
if self.multicall_address:
|
|
265
|
+
self.multicall_address = Web3.to_checksum_address(self.multicall_address)
|
src/tests/__init__.py
ADDED
|
File without changes
|
src/tests/abi.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"inputs": [
|
|
4
|
+
{
|
|
5
|
+
"internalType": "uint256",
|
|
6
|
+
"name": "value",
|
|
7
|
+
"type": "uint256"
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"name": "set",
|
|
11
|
+
"outputs": [],
|
|
12
|
+
"stateMutability": "nonpayable",
|
|
13
|
+
"type": "function"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"inputs": [
|
|
17
|
+
{
|
|
18
|
+
"internalType": "address",
|
|
19
|
+
"name": "",
|
|
20
|
+
"type": "address"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"name": "map",
|
|
24
|
+
"outputs": [
|
|
25
|
+
{
|
|
26
|
+
"internalType": "uint256",
|
|
27
|
+
"name": "",
|
|
28
|
+
"type": "uint256"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"stateMutability": "view",
|
|
32
|
+
"type": "function"
|
|
33
|
+
}
|
|
34
|
+
]
|
src/tests/constants.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from src.multirpc.utils import ChainConfigTest, NestedDict
|
|
4
|
+
|
|
5
|
+
# Arbitrum Configuration
|
|
6
|
+
ArbConfig = ChainConfigTest(
|
|
7
|
+
'Arbitrum',
|
|
8
|
+
'0xCFE3c06Fe982A7D16ce3826C64c5f0730054Dc95',
|
|
9
|
+
NestedDict({
|
|
10
|
+
"view": {
|
|
11
|
+
1: ['https://1rpc.io/arb', 'https://rpc.ankr.com/arbitrum', 'https://arbitrum.drpc.org'],
|
|
12
|
+
},
|
|
13
|
+
"transaction": {
|
|
14
|
+
1: ['https://1rpc.io/arb', 'https://rpc.ankr.com/arbitrum', 'https://arbitrum.drpc.org'],
|
|
15
|
+
}
|
|
16
|
+
}),
|
|
17
|
+
'0xbc0f34536fdf5d2593081b112d49d714993d879032e0e9c6998afc3110b7f0ed'
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Polygon Configuration
|
|
21
|
+
PolyConfig = ChainConfigTest(
|
|
22
|
+
'Polygon',
|
|
23
|
+
'0xCa7DFDc4dB0F27484Cf5EEa1CdF380301Ef07Ce2',
|
|
24
|
+
NestedDict({
|
|
25
|
+
"view": {
|
|
26
|
+
1: ['https://1rpc.io/matic', 'https://polygon-rpc.com'],
|
|
27
|
+
},
|
|
28
|
+
"transaction": {
|
|
29
|
+
1: ['https://1rpc.io/matic', 'https://polygon-rpc.com'],
|
|
30
|
+
}
|
|
31
|
+
}),
|
|
32
|
+
'0x4b8756bd1d32f62b2b9e3b46b80917bd3de4fd95695bad33e483293284f28678',
|
|
33
|
+
is_proof_authority=True
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Base Configuration
|
|
37
|
+
BaseConfig = ChainConfigTest(
|
|
38
|
+
'Base',
|
|
39
|
+
'0x1d58e7F58d085c87E34b18DAe5A6D08d187cbcbe',
|
|
40
|
+
NestedDict({
|
|
41
|
+
"view": {
|
|
42
|
+
1: ['https://base-rpc.publicnode.com', 'https://base.drpc.org'],
|
|
43
|
+
},
|
|
44
|
+
"transaction": {
|
|
45
|
+
1: ['https://base-rpc.publicnode.com', 'https://base.drpc.org'],
|
|
46
|
+
}
|
|
47
|
+
}),
|
|
48
|
+
'0xbd342d36d503af057cd79fd4f252b4629d6013d0748a2742dc99c9fcbe522072',
|
|
49
|
+
is_proof_authority=True
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Mantle Configuration
|
|
53
|
+
MantleConfig = ChainConfigTest(
|
|
54
|
+
'Mantle',
|
|
55
|
+
'0x535D41D93cDc0818Ad8Eeb452B74e502A5742874',
|
|
56
|
+
NestedDict({
|
|
57
|
+
"view": {
|
|
58
|
+
1: ['https://1rpc.io/mantle', 'https://mantle.drpc.org'],
|
|
59
|
+
},
|
|
60
|
+
"transaction": {
|
|
61
|
+
1: ['https://1rpc.io/mantle', 'https://mantle.drpc.org'],
|
|
62
|
+
}
|
|
63
|
+
}),
|
|
64
|
+
'0x9f33a56be9983753abebbe8fb048601a141097289d96b9844afb36e68f72ef82',
|
|
65
|
+
is_proof_authority=False,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
RPCsSupportingTxTrace = [
|
|
69
|
+
'https://arbitrum.drpc.org', # Arbitrum
|
|
70
|
+
'https://polygon-rpc.com', # Polygon
|
|
71
|
+
'https://base.drpc.org', # Base
|
|
72
|
+
'https://mantle.drpc.org' # Mantle
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
with open("tests/abi.json", "r") as f:
|
|
76
|
+
abi = json.load(f)
|
|
77
|
+
|
|
78
|
+
PreviousBlock = 3
|
src/tests/contract.sol
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
|
2
|
+
pragma solidity ^0.8.9;
|
|
3
|
+
|
|
4
|
+
contract Mapping {
|
|
5
|
+
mapping(address => uint256) public map;
|
|
6
|
+
|
|
7
|
+
function set(uint256 value) public {
|
|
8
|
+
// Revert if the input is exactly one byte of 0x00
|
|
9
|
+
require(
|
|
10
|
+
value >= 10,
|
|
11
|
+
"Error: 10 < value is not allowed."
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
map[msg.sender] = value;
|
|
15
|
+
}
|
|
16
|
+
}
|
src/tests/test.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import random
|
|
4
|
+
|
|
5
|
+
from eth_account import Account
|
|
6
|
+
from web3.exceptions import MismatchedABI
|
|
7
|
+
|
|
8
|
+
from src.multirpc.async_multi_rpc_interface import AsyncMultiRpc
|
|
9
|
+
from src.multirpc.constants import GasEstimationMethod, ViewPolicy
|
|
10
|
+
from src.multirpc.sync_multi_rpc_interface import MultiRpc
|
|
11
|
+
from src.multirpc.utils import ChainConfigTest
|
|
12
|
+
from src.tests.constants import ArbConfig, BaseConfig, PolyConfig, RPCsSupportingTxTrace, abi
|
|
13
|
+
from src.tests.test_settings import LogLevel, PrivateKey1, PrivateKey2
|
|
14
|
+
|
|
15
|
+
PreviousBlock = 3
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def async_test_map(mr: AsyncMultiRpc, addr: str = None, pk: str = None):
|
|
19
|
+
random_int = random.randint(10, 100)
|
|
20
|
+
print(f"Random int: {random_int}")
|
|
21
|
+
# await mr.functions.set(random_int).call(address=addr, private_key=pk,
|
|
22
|
+
# gas_estimation_method=GasEstimationMethod.GAS_API_PROVIDER)
|
|
23
|
+
# await mr.functions.set(random_int).call(address=addr, private_key=pk,
|
|
24
|
+
# gas_estimation_method=GasEstimationMethod.FIXED)
|
|
25
|
+
|
|
26
|
+
# for failure purpose
|
|
27
|
+
try:
|
|
28
|
+
await mr.functions.set(random_int, random_int).call(address=addr, private_key=pk,
|
|
29
|
+
gas_estimation_method=GasEstimationMethod.RPC)
|
|
30
|
+
except MismatchedABI:
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
type(random_int)
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
await mr.functions.set(random.randint(1, 9)
|
|
37
|
+
).call(address=addr, private_key=pk,
|
|
38
|
+
gas_estimation_method=GasEstimationMethod.RPC,
|
|
39
|
+
enable_estimate_gas_limit=False
|
|
40
|
+
)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
print(e)
|
|
43
|
+
|
|
44
|
+
print(f'encoded function: {mr.functions.set(random_int).get_encoded_data()}')
|
|
45
|
+
tx_receipt = await mr.functions.set(random_int).call(address=addr, private_key=pk,
|
|
46
|
+
gas_estimation_method=GasEstimationMethod.RPC)
|
|
47
|
+
|
|
48
|
+
print(f"{tx_receipt=}")
|
|
49
|
+
result = await mr.functions.map(addr).call()
|
|
50
|
+
print(f"map(addr: {addr}): {result}")
|
|
51
|
+
assert random_int == result, "test was not successful"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def async_main(chain_config: ChainConfigTest):
|
|
55
|
+
multi_rpc = AsyncMultiRpc(chain_config.rpc, chain_config.contract_address,
|
|
56
|
+
rpcs_supporting_tx_trace=RPCsSupportingTxTrace,
|
|
57
|
+
view_policy=ViewPolicy.MostUpdated,
|
|
58
|
+
contract_abi=abi, gas_estimation=None, log_level=LogLevel,
|
|
59
|
+
is_proof_authority=config_.is_proof_authority,
|
|
60
|
+
multicall_custom_address=chain_config.multicall_address, enable_estimate_gas_limit=True)
|
|
61
|
+
multi_rpc.set_account(address1, private_key=PrivateKey1)
|
|
62
|
+
|
|
63
|
+
p_block = await multi_rpc.get_block_number() - PreviousBlock
|
|
64
|
+
print(f"tx_receipt: {await multi_rpc.get_tx_receipt(chain_config.tx_hash)}")
|
|
65
|
+
print(f"block: {await multi_rpc.get_block(p_block - 1000)}")
|
|
66
|
+
print(f"Nonce: {await multi_rpc.get_nonce(address1)}")
|
|
67
|
+
print(f"map({address1}): {await multi_rpc.functions.map(address1).call()}")
|
|
68
|
+
|
|
69
|
+
results = await multi_rpc.functions.map([(address1,), (address2,)] * 100).multicall()
|
|
70
|
+
print(f"map({address1, address2}): {[res for res in results]}")
|
|
71
|
+
print(f"map({address1}) in {p_block=}: "
|
|
72
|
+
f"{await multi_rpc.functions.map(address1).call(block_identifier=p_block)}")
|
|
73
|
+
|
|
74
|
+
await async_test_map(multi_rpc, address1)
|
|
75
|
+
# await async_test_map(multi_rpc, address2, PrivateKey2)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def sync_test_map(mr: MultiRpc, addr: str = None, pk: str = None):
|
|
79
|
+
random_int = random.randint(10, 100)
|
|
80
|
+
print(f"Random int: {random_int}")
|
|
81
|
+
print(f'encoded function: {mr.functions.set(random_int).get_encoded_data()}')
|
|
82
|
+
mr.functions.set(random_int).call(address=addr, private_key=pk)
|
|
83
|
+
|
|
84
|
+
result = mr.functions.map(addr).call()
|
|
85
|
+
print(f"map(addr: {addr}): {result}")
|
|
86
|
+
assert random_int == result, "test was not successful"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def sync_main(chain_config: ChainConfigTest):
|
|
90
|
+
multi_rpc = MultiRpc(chain_config.rpc, chain_config.contract_address, contract_abi=abi,
|
|
91
|
+
rpcs_supporting_tx_trace=RPCsSupportingTxTrace,
|
|
92
|
+
gas_estimation=None,
|
|
93
|
+
enable_estimate_gas_limit=True, log_level=LogLevel,
|
|
94
|
+
is_proof_authority=config_.is_proof_authority,
|
|
95
|
+
multicall_custom_address=chain_config.multicall_address)
|
|
96
|
+
multi_rpc.set_account(address1, private_key=PrivateKey1)
|
|
97
|
+
|
|
98
|
+
p_block = multi_rpc.get_block_number() - PreviousBlock
|
|
99
|
+
print(f"tx_receipt: {multi_rpc.get_tx_receipt(chain_config.tx_hash)}")
|
|
100
|
+
print(f"block: {multi_rpc.get_block(p_block - 1000)}")
|
|
101
|
+
print(f"Nonce: {multi_rpc.get_nonce(address1)}")
|
|
102
|
+
print(f"map({address1}): {multi_rpc.functions.map(address1).call()}")
|
|
103
|
+
|
|
104
|
+
results = multi_rpc.functions.map([(address1,), (address2,)]).multicall()
|
|
105
|
+
print(f"map({address1, address2}): {[res for res in results]}")
|
|
106
|
+
print(f"map({address1}) in {p_block=}: "
|
|
107
|
+
f"{multi_rpc.functions.map(address1).call(block_identifier=p_block)}")
|
|
108
|
+
|
|
109
|
+
sync_test_map(multi_rpc, address1)
|
|
110
|
+
sync_test_map(multi_rpc, address2, PrivateKey2)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
async def test(chain_config: ChainConfigTest):
|
|
114
|
+
# try:
|
|
115
|
+
# sync_main(chain_config)
|
|
116
|
+
# print("###sync test was successful###")
|
|
117
|
+
# except Exception as e:
|
|
118
|
+
# logging.error(e)
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
await async_main(chain_config)
|
|
122
|
+
print('###async test was successful###')
|
|
123
|
+
except Exception as e:
|
|
124
|
+
logging.error(e)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if __name__ == '__main__':
|
|
128
|
+
address1 = Account.from_key(PrivateKey1).address
|
|
129
|
+
address2 = Account.from_key(PrivateKey2).address
|
|
130
|
+
for config_ in [
|
|
131
|
+
ArbConfig,
|
|
132
|
+
PolyConfig,
|
|
133
|
+
BaseConfig,
|
|
134
|
+
# MantleConfig
|
|
135
|
+
]:
|
|
136
|
+
print(f"=============================== Start Testing on {config_.name} ===============================")
|
|
137
|
+
asyncio.run(test(config_))
|
|
138
|
+
print(f"=============================== Test on {config_.name} Completed ===============================\n\n")
|