mm-eth 0.1.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.
- mm_eth/__init__.py +0 -0
- mm_eth/abi/zksync.json +2092 -0
- mm_eth/abi.py +130 -0
- mm_eth/account.py +70 -0
- mm_eth/anvil.py +56 -0
- mm_eth/cli/__init__.py +0 -0
- mm_eth/cli/calcs.py +88 -0
- mm_eth/cli/cli.py +233 -0
- mm_eth/cli/cli_helpers.py +195 -0
- mm_eth/cli/cli_utils.py +150 -0
- mm_eth/cli/cmd/__init__.py +0 -0
- mm_eth/cli/cmd/balance_cmd.py +59 -0
- mm_eth/cli/cmd/balances_cmd.py +121 -0
- mm_eth/cli/cmd/call_contract_cmd.py +44 -0
- mm_eth/cli/cmd/config_example_cmd.py +9 -0
- mm_eth/cli/cmd/deploy_cmd.py +41 -0
- mm_eth/cli/cmd/encode_input_data_cmd.py +10 -0
- mm_eth/cli/cmd/mnemonic_cmd.py +27 -0
- mm_eth/cli/cmd/node_cmd.py +47 -0
- mm_eth/cli/cmd/private_key_cmd.py +10 -0
- mm_eth/cli/cmd/rpc_cmd.py +81 -0
- mm_eth/cli/cmd/send_contract_cmd.py +247 -0
- mm_eth/cli/cmd/solc_cmd.py +25 -0
- mm_eth/cli/cmd/token_cmd.py +29 -0
- mm_eth/cli/cmd/transfer_erc20_cmd.py +275 -0
- mm_eth/cli/cmd/transfer_eth_cmd.py +252 -0
- mm_eth/cli/cmd/vault_cmd.py +16 -0
- mm_eth/cli/config_examples/balances.yml +15 -0
- mm_eth/cli/config_examples/call_contract.yml +5 -0
- mm_eth/cli/config_examples/transfer_erc20.yml +26 -0
- mm_eth/cli/config_examples/transfer_eth.yml +24 -0
- mm_eth/cli/validators.py +84 -0
- mm_eth/deploy.py +20 -0
- mm_eth/ens.py +16 -0
- mm_eth/erc20.py +240 -0
- mm_eth/ethernodes.py +34 -0
- mm_eth/py.typed +0 -0
- mm_eth/rpc.py +478 -0
- mm_eth/services/__init__.py +0 -0
- mm_eth/solc.py +34 -0
- mm_eth/tx.py +164 -0
- mm_eth/types.py +5 -0
- mm_eth/utils.py +245 -0
- mm_eth/vault.py +38 -0
- mm_eth/zksync.py +203 -0
- mm_eth-0.1.0.dist-info/METADATA +24 -0
- mm_eth-0.1.0.dist-info/RECORD +50 -0
- mm_eth-0.1.0.dist-info/WHEEL +5 -0
- mm_eth-0.1.0.dist-info/entry_points.txt +2 -0
- mm_eth-0.1.0.dist-info/top_level.txt +1 -0
mm_eth/cli/validators.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from decimal import Decimal, InvalidOperation
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from mm_std import str_to_list
|
|
6
|
+
|
|
7
|
+
from mm_eth.cli import calcs
|
|
8
|
+
from mm_eth.utils import to_wei
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def wei_validator(v: str | None) -> int | None:
|
|
12
|
+
if v is None:
|
|
13
|
+
return None
|
|
14
|
+
return to_wei(v)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def log_validator(v: str | None) -> str | None:
|
|
18
|
+
if v is None:
|
|
19
|
+
return None
|
|
20
|
+
log_file = Path(v).expanduser()
|
|
21
|
+
log_file.touch(exist_ok=True)
|
|
22
|
+
if not (log_file.is_file() and os.access(log_file, os.W_OK)):
|
|
23
|
+
raise ValueError(f"wrong log path: {v}")
|
|
24
|
+
return v
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def nodes_validator(v: str | list[str] | None) -> list[str]:
|
|
28
|
+
if v is None:
|
|
29
|
+
return []
|
|
30
|
+
if isinstance(v, str):
|
|
31
|
+
return str_to_list(v, unique=True, remove_comments=True, split_line=True)
|
|
32
|
+
return v
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def addresses_validator(v: str | list[str] | None) -> list[str]:
|
|
36
|
+
if v is None:
|
|
37
|
+
return []
|
|
38
|
+
if isinstance(v, str):
|
|
39
|
+
return str_to_list(v, unique=True, remove_comments=True, split_line=True, lower=True)
|
|
40
|
+
return v
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def delay_validator(v: str | Decimal) -> Decimal | tuple[Decimal, Decimal]:
|
|
44
|
+
if isinstance(v, int | float):
|
|
45
|
+
return Decimal(str(v))
|
|
46
|
+
elif isinstance(v, str):
|
|
47
|
+
arr = [a.strip() for a in v.split("-")]
|
|
48
|
+
if len(arr) != 2:
|
|
49
|
+
raise ValueError("wrong delay value")
|
|
50
|
+
try:
|
|
51
|
+
return Decimal(arr[0]), Decimal(arr[1])
|
|
52
|
+
except InvalidOperation:
|
|
53
|
+
raise ValueError("wrong delay value") from None
|
|
54
|
+
raise ValueError("wrong delay value")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def is_valid_calc_var_wei_value(value: str | None, base_name: str = "var", decimals: int | None = None) -> bool:
|
|
58
|
+
if value is None:
|
|
59
|
+
return True # check for None on BaseModel.field type level
|
|
60
|
+
try:
|
|
61
|
+
calcs.calc_var_wei_value(value, var_value=123, var_name=base_name, decimals=decimals)
|
|
62
|
+
return True
|
|
63
|
+
except ValueError:
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def is_valid_calc_decimal_value(value: str | None) -> bool:
|
|
68
|
+
if value is None:
|
|
69
|
+
return True # check for None on BaseModel.field type level
|
|
70
|
+
try:
|
|
71
|
+
calcs.calc_decimal_value(value)
|
|
72
|
+
return True
|
|
73
|
+
except ValueError:
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def is_valid_calc_function_args(value: str | None) -> bool:
|
|
78
|
+
if value is None:
|
|
79
|
+
return True
|
|
80
|
+
try:
|
|
81
|
+
calcs.calc_function_args(value)
|
|
82
|
+
return True
|
|
83
|
+
except ValueError:
|
|
84
|
+
return False
|
mm_eth/deploy.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import eth_utils
|
|
2
|
+
import rlp
|
|
3
|
+
from eth_utils import keccak
|
|
4
|
+
|
|
5
|
+
from mm_eth import abi
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_deploy_contract_data(contract_bin: str, constructor_types: list[str], constructor_values: list[object]) -> str:
|
|
9
|
+
constructor_data = ""
|
|
10
|
+
if constructor_types and constructor_values:
|
|
11
|
+
constructor_data = abi.encode_data(constructor_types, constructor_values)[2:]
|
|
12
|
+
return contract_bin + constructor_data
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_contract_address(sender_address: str, nonce: int) -> str:
|
|
16
|
+
sender_bytes = eth_utils.to_bytes(hexstr=sender_address)
|
|
17
|
+
raw = rlp.encode([sender_bytes, nonce])
|
|
18
|
+
h = keccak(raw)
|
|
19
|
+
address_bytes = h[12:]
|
|
20
|
+
return eth_utils.to_checksum_address(address_bytes).lower()
|
mm_eth/ens.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from mm_std import Err, Ok, Result
|
|
2
|
+
|
|
3
|
+
from mm_eth.types import Nodes, Proxies
|
|
4
|
+
from mm_eth.utils import get_w3, random_node, random_proxy
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_name(nodes: Nodes, address: str, timeout: int = 10, proxies: Proxies = None, attempts: int = 3) -> Result[str]:
|
|
8
|
+
result = Err("not_started")
|
|
9
|
+
for _ in range(attempts):
|
|
10
|
+
try:
|
|
11
|
+
w3 = get_w3(random_node(nodes), timeout=timeout, proxy=random_proxy(proxies))
|
|
12
|
+
res = w3.ens.name(w3.to_checksum_address(address)) # type: ignore[union-attr]
|
|
13
|
+
return Ok(res or "")
|
|
14
|
+
except Exception as e:
|
|
15
|
+
result = Err(e)
|
|
16
|
+
return result
|
mm_eth/erc20.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import string
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
import eth_abi
|
|
8
|
+
import eth_utils
|
|
9
|
+
from eth_typing import HexStr
|
|
10
|
+
from eth_utils import to_checksum_address, to_hex
|
|
11
|
+
from mm_std import Err, Ok, Result
|
|
12
|
+
|
|
13
|
+
from mm_eth import rpc
|
|
14
|
+
from mm_eth.rpc import Log
|
|
15
|
+
from mm_eth.tx import SignedTx, sign_legacy_tx, sign_tx
|
|
16
|
+
from mm_eth.types import Nodes, Proxies
|
|
17
|
+
from mm_eth.utils import hex_str_to_int, hex_to_bytes, log_topic_to_address
|
|
18
|
+
|
|
19
|
+
TRANSFER_METHOD = "0xa9059cbb"
|
|
20
|
+
TRANSFER_TOPIC = HexStr("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
USDT_MAINNET_ADDRESS = "0xdac17f958d2ee523a2206206994597c13d831ec7"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class TransferEventLog:
|
|
28
|
+
token_address: str
|
|
29
|
+
from_address: str
|
|
30
|
+
to_address: str
|
|
31
|
+
value: int
|
|
32
|
+
tx_hash: str
|
|
33
|
+
block_number: int
|
|
34
|
+
log_index: int
|
|
35
|
+
|
|
36
|
+
@staticmethod
|
|
37
|
+
def from_log(log: Log) -> Result[TransferEventLog]:
|
|
38
|
+
try:
|
|
39
|
+
return Ok(
|
|
40
|
+
TransferEventLog(
|
|
41
|
+
token_address=log.address,
|
|
42
|
+
from_address=log_topic_to_address(log.topics[1]),
|
|
43
|
+
to_address=log_topic_to_address(log.topics[2]),
|
|
44
|
+
value=int(log.data, 16),
|
|
45
|
+
tx_hash=log.transaction_hash,
|
|
46
|
+
block_number=log.block_number,
|
|
47
|
+
log_index=log.log_index,
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
except Exception as err:
|
|
51
|
+
return Err(err)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_balance(
|
|
55
|
+
rpc_urls: Nodes,
|
|
56
|
+
token_address: str,
|
|
57
|
+
user_address: str,
|
|
58
|
+
timeout: int = 10,
|
|
59
|
+
proxies: Proxies = None,
|
|
60
|
+
attempts: int = 1,
|
|
61
|
+
) -> Result[int]:
|
|
62
|
+
data = "0x70a08231000000000000000000000000" + user_address[2:]
|
|
63
|
+
return rpc.rpc_call(
|
|
64
|
+
nodes=rpc_urls,
|
|
65
|
+
method="eth_call",
|
|
66
|
+
params=[{"to": token_address, "data": data}, "latest"],
|
|
67
|
+
timeout=timeout,
|
|
68
|
+
proxies=proxies,
|
|
69
|
+
attempts=attempts,
|
|
70
|
+
).and_then(hex_str_to_int)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_name(rpc_urls: Nodes, address: str, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[str]:
|
|
74
|
+
return rpc.rpc_call(
|
|
75
|
+
nodes=rpc_urls,
|
|
76
|
+
method="eth_call",
|
|
77
|
+
params=[{"to": address, "data": "0x06fdde03"}, "latest"],
|
|
78
|
+
timeout=timeout,
|
|
79
|
+
proxies=proxies,
|
|
80
|
+
attempts=attempts,
|
|
81
|
+
).and_then(_normalize_str)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_symbol(rpc_urls: Nodes, address: str, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[str]:
|
|
85
|
+
return rpc.rpc_call(
|
|
86
|
+
nodes=rpc_urls,
|
|
87
|
+
method="eth_call",
|
|
88
|
+
params=[{"to": address, "data": "0x95d89b41"}, "latest"],
|
|
89
|
+
timeout=timeout,
|
|
90
|
+
proxies=proxies,
|
|
91
|
+
attempts=attempts,
|
|
92
|
+
).and_then(_normalize_str)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_decimals(rpc_urls: Nodes, address: str, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[int]:
|
|
96
|
+
res = rpc.rpc_call(
|
|
97
|
+
nodes=rpc_urls,
|
|
98
|
+
method="eth_call",
|
|
99
|
+
params=[{"to": address, "data": "0x313ce567"}, "latest"],
|
|
100
|
+
timeout=timeout,
|
|
101
|
+
proxies=proxies,
|
|
102
|
+
attempts=attempts,
|
|
103
|
+
)
|
|
104
|
+
if isinstance(res, Err):
|
|
105
|
+
return res
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
if res.ok == "0x":
|
|
109
|
+
return Err("no_decimals", data=res.data)
|
|
110
|
+
|
|
111
|
+
if len(res.ok) > 66:
|
|
112
|
+
result = eth_utils.to_int(hexstr=res.ok[0:66])
|
|
113
|
+
else:
|
|
114
|
+
result = eth_utils.to_int(hexstr=res.ok)
|
|
115
|
+
return Ok(result, data=res.data)
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
return Err(f"exception: {e}", data=res.data)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def encode_transfer_input_data(recipient: str, value: int) -> str:
|
|
122
|
+
recipient = to_checksum_address(recipient)
|
|
123
|
+
input_data = hex_to_bytes(TRANSFER_METHOD) + eth_abi.encode(["address", "uint256"], [recipient, value])
|
|
124
|
+
return to_hex(input_data)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def transfer_token_legacy(
|
|
128
|
+
*,
|
|
129
|
+
rpc_urls: str | Sequence[str],
|
|
130
|
+
token_address: str,
|
|
131
|
+
recipient_address: str,
|
|
132
|
+
value: int,
|
|
133
|
+
nonce: int,
|
|
134
|
+
gas_price: int,
|
|
135
|
+
gas_limit: int,
|
|
136
|
+
private_key: str,
|
|
137
|
+
chain_id: int,
|
|
138
|
+
timeout: int = 10,
|
|
139
|
+
proxies: Proxies = None,
|
|
140
|
+
attempts: int = 1,
|
|
141
|
+
) -> Result[str]:
|
|
142
|
+
input_data = encode_transfer_input_data(recipient_address, value)
|
|
143
|
+
signed_tx = sign_legacy_tx(
|
|
144
|
+
nonce=nonce,
|
|
145
|
+
gas_price=gas_price,
|
|
146
|
+
gas=gas_limit,
|
|
147
|
+
private_key=private_key,
|
|
148
|
+
chain_id=chain_id,
|
|
149
|
+
data=input_data,
|
|
150
|
+
to=token_address,
|
|
151
|
+
)
|
|
152
|
+
return rpc.eth_send_raw_transaction(rpc_urls, signed_tx.raw_tx, timeout=timeout, proxies=proxies, attempts=attempts)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def transfer_token(
|
|
156
|
+
*,
|
|
157
|
+
rpc_urls: Nodes,
|
|
158
|
+
token_address: str,
|
|
159
|
+
recipient_address: str,
|
|
160
|
+
value: int,
|
|
161
|
+
nonce: int,
|
|
162
|
+
max_fee_per_gas: int,
|
|
163
|
+
max_priority_fee_per_gas: int,
|
|
164
|
+
gas_limit: int,
|
|
165
|
+
private_key: str,
|
|
166
|
+
chain_id: int,
|
|
167
|
+
timeout: int = 10,
|
|
168
|
+
proxies: Proxies = None,
|
|
169
|
+
attempts: int = 1,
|
|
170
|
+
) -> Result[str]:
|
|
171
|
+
input_data = encode_transfer_input_data(recipient_address, value)
|
|
172
|
+
signed_tx = sign_tx(
|
|
173
|
+
nonce=nonce,
|
|
174
|
+
max_fee_per_gas=max_fee_per_gas,
|
|
175
|
+
max_priority_fee_per_gas=max_priority_fee_per_gas,
|
|
176
|
+
gas=gas_limit,
|
|
177
|
+
private_key=private_key,
|
|
178
|
+
chain_id=chain_id,
|
|
179
|
+
data=input_data,
|
|
180
|
+
to=token_address,
|
|
181
|
+
)
|
|
182
|
+
return rpc.eth_send_raw_transaction(rpc_urls, signed_tx.raw_tx, timeout=timeout, proxies=proxies, attempts=attempts)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def sign_transfer_tx(
|
|
186
|
+
*,
|
|
187
|
+
token_address: str,
|
|
188
|
+
recipient_address: str,
|
|
189
|
+
value: int,
|
|
190
|
+
nonce: int,
|
|
191
|
+
max_fee_per_gas: int,
|
|
192
|
+
max_priority_fee_per_gas: int,
|
|
193
|
+
gas_limit: int,
|
|
194
|
+
private_key: str,
|
|
195
|
+
chain_id: int,
|
|
196
|
+
) -> SignedTx:
|
|
197
|
+
input_data = encode_transfer_input_data(recipient_address, value)
|
|
198
|
+
return sign_tx(
|
|
199
|
+
nonce=nonce,
|
|
200
|
+
max_fee_per_gas=max_fee_per_gas,
|
|
201
|
+
max_priority_fee_per_gas=max_priority_fee_per_gas,
|
|
202
|
+
gas=gas_limit,
|
|
203
|
+
private_key=private_key,
|
|
204
|
+
chain_id=chain_id,
|
|
205
|
+
data=input_data,
|
|
206
|
+
to=token_address,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def get_transfer_event_logs(
|
|
211
|
+
rpc_urls: Nodes,
|
|
212
|
+
token_address: str | None,
|
|
213
|
+
from_block: int,
|
|
214
|
+
to_block: int,
|
|
215
|
+
timeout: int = 10,
|
|
216
|
+
proxies: Proxies = None,
|
|
217
|
+
attempts: int = 1,
|
|
218
|
+
) -> Result[list[TransferEventLog]]:
|
|
219
|
+
res = rpc.eth_get_logs(
|
|
220
|
+
rpc_urls,
|
|
221
|
+
address=token_address,
|
|
222
|
+
topics=[TRANSFER_TOPIC],
|
|
223
|
+
from_block=from_block,
|
|
224
|
+
to_block=to_block,
|
|
225
|
+
timeout=timeout,
|
|
226
|
+
proxies=proxies,
|
|
227
|
+
attempts=attempts,
|
|
228
|
+
)
|
|
229
|
+
if isinstance(res, Err):
|
|
230
|
+
return res
|
|
231
|
+
result: list[TransferEventLog] = []
|
|
232
|
+
for log in res.ok:
|
|
233
|
+
event_log_res = TransferEventLog.from_log(log)
|
|
234
|
+
if isinstance(event_log_res, Ok):
|
|
235
|
+
result.append(event_log_res.ok)
|
|
236
|
+
return Ok(result, data=res.data)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _normalize_str(value: str) -> Ok[str]:
|
|
240
|
+
return Ok("".join(filter(lambda x: x in string.printable, eth_utils.to_text(hexstr=value))).strip())
|
mm_eth/ethernodes.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from mm_std import CHROME_USER_AGENT, Result, hr
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SearchResult(BaseModel):
|
|
8
|
+
class Node(BaseModel):
|
|
9
|
+
id: str
|
|
10
|
+
host: str
|
|
11
|
+
port: int
|
|
12
|
+
client: str
|
|
13
|
+
client_version: str = Field(..., alias="clientVersion")
|
|
14
|
+
os: str
|
|
15
|
+
last_update: datetime = Field(..., alias="lastUpdate")
|
|
16
|
+
country: str
|
|
17
|
+
in_sync: int | None = Field(None, alias="inSync")
|
|
18
|
+
isp: str
|
|
19
|
+
|
|
20
|
+
draw: int
|
|
21
|
+
records_total: int = Field(..., alias="recordsTotal")
|
|
22
|
+
records_filtered: int = Field(..., alias="recordsFiltered")
|
|
23
|
+
data: list[Node]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def search_nodes(offset: int = 0, proxy: str | None = None) -> Result[SearchResult]:
|
|
27
|
+
url = f"https://www.ethernodes.org/data?draw=1&columns%5B0%5D%5Bdata%5D=id&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=host&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=true&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B2%5D%5Bdata%5D=isp&columns%5B2%5D%5Bname%5D=&columns%5B2%5D%5Bsearchable%5D=true&columns%5B2%5D%5Borderable%5D=true&columns%5B2%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B2%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B3%5D%5Bdata%5D=country&columns%5B3%5D%5Bname%5D=&columns%5B3%5D%5Bsearchable%5D=true&columns%5B3%5D%5Borderable%5D=true&columns%5B3%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B3%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B4%5D%5Bdata%5D=client&columns%5B4%5D%5Bname%5D=&columns%5B4%5D%5Bsearchable%5D=true&columns%5B4%5D%5Borderable%5D=true&columns%5B4%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B4%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B5%5D%5Bdata%5D=clientVersion&columns%5B5%5D%5Bname%5D=&columns%5B5%5D%5Bsearchable%5D=true&columns%5B5%5D%5Borderable%5D=true&columns%5B5%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B5%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B6%5D%5Bdata%5D=os&columns%5B6%5D%5Bname%5D=&columns%5B6%5D%5Bsearchable%5D=true&columns%5B6%5D%5Borderable%5D=true&columns%5B6%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B6%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B7%5D%5Bdata%5D=lastUpdate&columns%5B7%5D%5Bname%5D=&columns%5B7%5D%5Bsearchable%5D=true&columns%5B7%5D%5Borderable%5D=true&columns%5B7%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B7%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B8%5D%5Bdata%5D=inSync&columns%5B8%5D%5Bname%5D=&columns%5B8%5D%5Bsearchable%5D=true&columns%5B8%5D%5Borderable%5D=true&columns%5B8%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B8%5D%5Bsearch%5D%5Bregex%5D=false&order%5B0%5D%5Bcolumn%5D=0&order%5B0%5D%5Bdir%5D=asc&search%5Bvalue%5D=&search%5Bregex%5D=false&length=100&start={offset}"
|
|
28
|
+
res = hr(url, proxy=proxy, user_agent=CHROME_USER_AGENT)
|
|
29
|
+
if res.is_error():
|
|
30
|
+
return res.to_err_result()
|
|
31
|
+
try:
|
|
32
|
+
return res.to_ok_result(SearchResult(**res.json))
|
|
33
|
+
except Exception as e:
|
|
34
|
+
return res.to_err_result(f"exception: {e}")
|
mm_eth/py.typed
ADDED
|
File without changes
|