mm-eth 0.5.17__py3-none-any.whl → 0.6.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/abi.py +2 -4
- mm_eth/account.py +51 -18
- mm_eth/anvil.py +8 -8
- mm_eth/cli/calcs.py +1 -16
- mm_eth/cli/cli.py +55 -159
- mm_eth/cli/cli_utils.py +14 -27
- mm_eth/cli/cmd/balance_cmd.py +15 -16
- mm_eth/cli/cmd/balances_cmd.py +35 -36
- mm_eth/cli/cmd/deploy_cmd.py +9 -11
- mm_eth/cli/cmd/node_cmd.py +20 -15
- mm_eth/cli/cmd/solc_cmd.py +7 -6
- mm_eth/cli/cmd/transfer_cmd.py +210 -128
- mm_eth/cli/cmd/wallet/private_key_cmd.py +5 -4
- mm_eth/cli/rpc_helpers.py +32 -115
- mm_eth/cli/validators.py +13 -16
- mm_eth/converters.py +56 -0
- mm_eth/erc20.py +6 -224
- mm_eth/retry.py +143 -0
- mm_eth/rpc.py +226 -428
- mm_eth/solc.py +30 -17
- mm_eth/tx.py +8 -9
- mm_eth/utils.py +0 -224
- {mm_eth-0.5.17.dist-info → mm_eth-0.6.0.dist-info}/METADATA +3 -2
- mm_eth-0.6.0.dist-info/RECORD +32 -0
- mm_eth/async_rpc.py +0 -94
- mm_eth/cli/cmd/call_contract_cmd.py +0 -44
- mm_eth/cli/cmd/encode_input_data_cmd.py +0 -10
- mm_eth/cli/cmd/example_cmd.py +0 -9
- mm_eth/cli/cmd/rpc_cmd.py +0 -78
- mm_eth/cli/cmd/token_cmd.py +0 -29
- mm_eth/cli/cmd/tx_cmd.py +0 -16
- mm_eth/cli/cmd/vault_cmd.py +0 -19
- mm_eth/cli/examples/balances.toml +0 -18
- mm_eth/cli/examples/call_contract.toml +0 -9
- mm_eth/cli/examples/transfer.toml +0 -46
- mm_eth/cli/print_helpers.py +0 -37
- mm_eth/constants.py +0 -1
- mm_eth/ens.py +0 -106
- mm_eth/ethernodes.py +0 -34
- mm_eth/json_encoder.py +0 -15
- mm_eth/py.typed +0 -0
- mm_eth/rpc_async.py +0 -160
- mm_eth/vault.py +0 -38
- mm_eth-0.5.17.dist-info/RECORD +0 -49
- {mm_eth-0.5.17.dist-info → mm_eth-0.6.0.dist-info}/WHEEL +0 -0
- {mm_eth-0.5.17.dist-info → mm_eth-0.6.0.dist-info}/entry_points.txt +0 -0
mm_eth/rpc.py
CHANGED
|
@@ -1,403 +1,104 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import asyncio
|
|
3
2
|
import json
|
|
4
|
-
|
|
3
|
+
import string
|
|
4
|
+
from collections.abc import Sequence
|
|
5
5
|
from typing import Any, Literal, cast
|
|
6
6
|
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
from
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
block_number: int
|
|
21
|
-
from_address: str
|
|
22
|
-
to_address: str | None
|
|
23
|
-
contract_address: str | None
|
|
24
|
-
status: int | None
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@dataclass
|
|
28
|
-
class Log:
|
|
29
|
-
address: str
|
|
30
|
-
block_hash: str
|
|
31
|
-
block_number: int
|
|
32
|
-
data: str
|
|
33
|
-
log_index: int
|
|
34
|
-
removed: bool
|
|
35
|
-
topics: list[str]
|
|
36
|
-
transaction_hash: str
|
|
37
|
-
transaction_index: int
|
|
38
|
-
|
|
39
|
-
@classmethod
|
|
40
|
-
def from_json_rpc_dict(cls, data: dict[str, Any]) -> Result[Log]:
|
|
41
|
-
try:
|
|
42
|
-
return Ok(
|
|
43
|
-
Log(
|
|
44
|
-
address=data["address"],
|
|
45
|
-
block_hash=data["blockHash"],
|
|
46
|
-
block_number=int(data["blockNumber"], 16),
|
|
47
|
-
data=data["data"],
|
|
48
|
-
log_index=int(data["logIndex"], 16),
|
|
49
|
-
removed=data["removed"],
|
|
50
|
-
topics=data["topics"],
|
|
51
|
-
transaction_hash=data["transactionHash"],
|
|
52
|
-
transaction_index=int(data["transactionIndex"], 16),
|
|
53
|
-
),
|
|
54
|
-
)
|
|
55
|
-
except Exception as err:
|
|
56
|
-
return Err(f"exception: {err}")
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
class TxData(BaseModel):
|
|
60
|
-
block_number: int | None # for pending tx it can be none
|
|
61
|
-
from_: str
|
|
62
|
-
to: str | None
|
|
63
|
-
gas: int
|
|
64
|
-
gas_price: int
|
|
65
|
-
value: int
|
|
66
|
-
hash: str
|
|
67
|
-
input: str
|
|
68
|
-
nonce: int
|
|
69
|
-
v: int
|
|
70
|
-
r: str
|
|
71
|
-
s: str
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def rpc_call(
|
|
75
|
-
*,
|
|
76
|
-
nodes: Nodes,
|
|
7
|
+
import ens.utils
|
|
8
|
+
import eth_utils
|
|
9
|
+
import pydash
|
|
10
|
+
import websockets
|
|
11
|
+
from eth_typing import BlockIdentifier
|
|
12
|
+
from mm_std import Result, http_request
|
|
13
|
+
from web3.types import TxReceipt
|
|
14
|
+
|
|
15
|
+
TIMEOUT = 7.0
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def rpc_call(
|
|
19
|
+
node: str,
|
|
77
20
|
method: str,
|
|
78
|
-
params:
|
|
21
|
+
params: Sequence[object],
|
|
22
|
+
timeout: float,
|
|
23
|
+
proxy: str | None,
|
|
79
24
|
id_: int = 1,
|
|
80
|
-
timeout: int = 10,
|
|
81
|
-
proxies: Proxies = None,
|
|
82
|
-
attempts: int = 1,
|
|
83
25
|
) -> Result[Any]:
|
|
84
26
|
data = {"jsonrpc": "2.0", "method": method, "params": params, "id": id_}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
res = _http_call(node, data, timeout, random_proxy(proxies)) if node.startswith("http") else _ws_call(node, data, timeout)
|
|
89
|
-
if isinstance(res, Ok):
|
|
90
|
-
return res
|
|
91
|
-
return res
|
|
27
|
+
if node.startswith("http"):
|
|
28
|
+
return await _http_call(node, data, timeout, proxy)
|
|
29
|
+
return await _ws_call(node, data, timeout)
|
|
92
30
|
|
|
93
31
|
|
|
94
|
-
def _http_call(node: str, data: dict[str, object], timeout:
|
|
95
|
-
res =
|
|
32
|
+
async def _http_call(node: str, data: dict[str, object], timeout: float, proxy: str | None) -> Result[Any]:
|
|
33
|
+
res = await http_request(node, method="POST", proxy=proxy, timeout=timeout, json=data)
|
|
34
|
+
if res.is_err():
|
|
35
|
+
return res.to_err()
|
|
96
36
|
try:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
err = res.json.get("error", {}).get("message", "")
|
|
37
|
+
parsed_body = res.parse_json_body()
|
|
38
|
+
err = parsed_body.get("error", {}).get("message", "")
|
|
101
39
|
if err:
|
|
102
|
-
return res.
|
|
103
|
-
if "result" in
|
|
104
|
-
return res.
|
|
40
|
+
return res.to_err(f"service_error: {err}")
|
|
41
|
+
if "result" in parsed_body:
|
|
42
|
+
return res.to_ok(parsed_body["result"])
|
|
43
|
+
return res.to_ok("unknown_response")
|
|
44
|
+
except Exception as e:
|
|
45
|
+
return res.to_err(e)
|
|
105
46
|
|
|
106
|
-
return res.to_err_result("unknown_response")
|
|
107
|
-
except Exception as err:
|
|
108
|
-
return res.to_err_result(f"exception: {err}")
|
|
109
47
|
|
|
110
|
-
|
|
111
|
-
def _ws_call(node: str, data: dict[str, object], timeout: int) -> Result[Any]:
|
|
48
|
+
async def _ws_call(node: str, data: dict[str, object], timeout: float) -> Result[Any]:
|
|
112
49
|
try:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
50
|
+
async with asyncio.timeout(timeout):
|
|
51
|
+
async with websockets.connect(node) as ws:
|
|
52
|
+
await ws.send(json.dumps(data))
|
|
53
|
+
response = json.loads(await ws.recv())
|
|
54
|
+
|
|
55
|
+
err = pydash.get(response, "error.message")
|
|
118
56
|
if err:
|
|
119
|
-
return
|
|
57
|
+
return Result.err(f"service_error: {err}", {"response": response})
|
|
120
58
|
if "result" in response:
|
|
121
|
-
return
|
|
122
|
-
return
|
|
59
|
+
return Result.ok(response["result"], {"response": response})
|
|
60
|
+
return Result.err("unknown_response", {"response": response})
|
|
123
61
|
except TimeoutError:
|
|
124
|
-
return
|
|
125
|
-
except Exception as
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
).
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def eth_chain_id(
|
|
141
|
-
return rpc_call(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
proxies=proxies,
|
|
147
|
-
attempts=attempts,
|
|
148
|
-
).and_then(hex_str_to_int)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def net_peer_count(rpc_urls: Nodes, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[int]:
|
|
152
|
-
return rpc_call(
|
|
153
|
-
nodes=rpc_urls,
|
|
154
|
-
method="net_peerCount",
|
|
155
|
-
params=[],
|
|
156
|
-
timeout=timeout,
|
|
157
|
-
proxies=proxies,
|
|
158
|
-
attempts=attempts,
|
|
159
|
-
).and_then(hex_str_to_int)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def web3_client_version(rpc_urls: Nodes, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[str]:
|
|
163
|
-
return rpc_call(
|
|
164
|
-
nodes=rpc_urls,
|
|
165
|
-
method="web3_clientVersion",
|
|
166
|
-
params=[],
|
|
167
|
-
timeout=timeout,
|
|
168
|
-
proxies=proxies,
|
|
169
|
-
attempts=attempts,
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def net_version(nodes: Nodes, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[str]:
|
|
174
|
-
return rpc_call(nodes=nodes, method="net_version", params=[], timeout=timeout, proxies=proxies, attempts=attempts)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
def eth_get_code(rpc_urls: Nodes, address: str, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[str]:
|
|
178
|
-
return rpc_call(
|
|
179
|
-
nodes=rpc_urls,
|
|
180
|
-
method="eth_getCode",
|
|
181
|
-
params=[address, "latest"],
|
|
182
|
-
timeout=timeout,
|
|
183
|
-
proxies=proxies,
|
|
184
|
-
attempts=attempts,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
def eth_send_raw_transaction(
|
|
189
|
-
rpc_urls: Nodes,
|
|
190
|
-
raw_tx: str,
|
|
191
|
-
timeout: int = 10,
|
|
192
|
-
proxies: Proxies = None,
|
|
193
|
-
attempts: int = 1,
|
|
194
|
-
) -> Result[str]:
|
|
195
|
-
return rpc_call(
|
|
196
|
-
nodes=rpc_urls,
|
|
197
|
-
method="eth_sendRawTransaction",
|
|
198
|
-
params=[raw_tx],
|
|
199
|
-
timeout=timeout,
|
|
200
|
-
proxies=proxies,
|
|
201
|
-
attempts=attempts,
|
|
202
|
-
)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
def eth_get_balance(rpc_urls: Nodes, address: str, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[int]:
|
|
206
|
-
return rpc_call(
|
|
207
|
-
nodes=rpc_urls,
|
|
208
|
-
method="eth_getBalance",
|
|
209
|
-
params=[address, "latest"],
|
|
210
|
-
timeout=timeout,
|
|
211
|
-
proxies=proxies,
|
|
212
|
-
attempts=attempts,
|
|
213
|
-
).and_then(hex_str_to_int)
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def eth_get_transaction_count(
|
|
217
|
-
rpc_urls: Nodes,
|
|
218
|
-
address: str,
|
|
219
|
-
timeout: int = 10,
|
|
220
|
-
proxies: Proxies = None,
|
|
221
|
-
attempts: int = 1,
|
|
222
|
-
) -> Result[int]:
|
|
223
|
-
return rpc_call(
|
|
224
|
-
nodes=rpc_urls,
|
|
225
|
-
method="eth_getTransactionCount",
|
|
226
|
-
params=[address, "latest"],
|
|
227
|
-
timeout=timeout,
|
|
228
|
-
proxies=proxies,
|
|
229
|
-
attempts=attempts,
|
|
230
|
-
).and_then(hex_str_to_int)
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
def eth_get_block_by_number(
|
|
234
|
-
rpc_urls: Nodes,
|
|
235
|
-
block_number: BlockIdentifier,
|
|
236
|
-
full_transaction: bool = False,
|
|
237
|
-
timeout: int = 10,
|
|
238
|
-
proxies: Proxies = None,
|
|
239
|
-
attempts: int = 1,
|
|
62
|
+
return Result.err("timeout")
|
|
63
|
+
except Exception as e:
|
|
64
|
+
return Result.err(e)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# -- start eth rpc calls --
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async def eth_block_number(node: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
|
|
71
|
+
return (await rpc_call(node, "eth_blockNumber", [], timeout, proxy)).map(_hex_str_to_int)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def eth_get_balance(node: str, address: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
|
|
75
|
+
return (await rpc_call(node, "eth_getBalance", [address, "latest"], timeout, proxy)).map(_hex_str_to_int)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def eth_chain_id(node: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
|
|
79
|
+
return (await rpc_call(node, "eth_chainId", [], timeout, proxy)).map(_hex_str_to_int)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
async def eth_get_block_by_number(
|
|
83
|
+
node: str, block_number: BlockIdentifier, full_transaction: bool = False, timeout: float = TIMEOUT, proxy: str | None = None
|
|
240
84
|
) -> Result[dict[str, Any]]:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
method="eth_getBlockByNumber",
|
|
244
|
-
params=[hex(block_number) if isinstance(block_number, int) else block_number, full_transaction],
|
|
245
|
-
timeout=timeout,
|
|
246
|
-
proxies=proxies,
|
|
247
|
-
attempts=attempts,
|
|
248
|
-
)
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
def eth_get_logs(
|
|
252
|
-
rpc_urls: Nodes,
|
|
253
|
-
*,
|
|
254
|
-
address: str | None = None,
|
|
255
|
-
topics: list[str] | None = None,
|
|
256
|
-
from_block: BlockIdentifier | None = None,
|
|
257
|
-
to_block: BlockIdentifier | None = None,
|
|
258
|
-
timeout: int = 10,
|
|
259
|
-
proxies: Proxies = None,
|
|
260
|
-
attempts: int = 1,
|
|
261
|
-
) -> Result[list[Log]]:
|
|
262
|
-
params: dict[str, object] = {}
|
|
263
|
-
if address:
|
|
264
|
-
params["address"] = address
|
|
265
|
-
if isinstance(from_block, int):
|
|
266
|
-
params["fromBlock"] = hex(from_block)
|
|
267
|
-
else:
|
|
268
|
-
params["fromBlock"] = "earliest"
|
|
269
|
-
if isinstance(to_block, int):
|
|
270
|
-
params["toBlock"] = hex(to_block)
|
|
271
|
-
if topics:
|
|
272
|
-
params["topics"] = topics
|
|
273
|
-
|
|
274
|
-
res = rpc_call(nodes=rpc_urls, method="eth_getLogs", params=[params], proxies=proxies, attempts=attempts, timeout=timeout)
|
|
275
|
-
if isinstance(res, Err):
|
|
276
|
-
return res
|
|
85
|
+
params = [hex(block_number) if isinstance(block_number, int) else block_number, full_transaction]
|
|
86
|
+
return await rpc_call(node, "eth_getBlockByNumber", params, timeout, proxy)
|
|
277
87
|
|
|
278
|
-
result: list[Log] = []
|
|
279
|
-
for log_data in res.ok:
|
|
280
|
-
log_res = Log.from_json_rpc_dict(log_data)
|
|
281
|
-
if isinstance(log_res, Err):
|
|
282
|
-
return Err(log_res.err, data=res.data)
|
|
283
|
-
result.append(log_res.ok)
|
|
284
|
-
return Ok(result, data=res.data)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def eth_get_transaction_receipt(
|
|
288
|
-
rpc_urls: Nodes,
|
|
289
|
-
tx_hash: str,
|
|
290
|
-
timeout: int = 10,
|
|
291
|
-
proxies: Proxies = None,
|
|
292
|
-
attempts: int = 1,
|
|
293
|
-
) -> Result[TxReceipt]:
|
|
294
|
-
res = rpc_call(
|
|
295
|
-
nodes=rpc_urls,
|
|
296
|
-
method="eth_getTransactionReceipt",
|
|
297
|
-
params=[tx_hash],
|
|
298
|
-
timeout=timeout,
|
|
299
|
-
proxies=proxies,
|
|
300
|
-
attempts=attempts,
|
|
301
|
-
)
|
|
302
|
-
if isinstance(res, Err):
|
|
303
|
-
return res
|
|
304
88
|
|
|
305
|
-
|
|
306
|
-
|
|
89
|
+
async def eth_get_transaction_count(node: str, address: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
|
|
90
|
+
return (await rpc_call(node, "eth_getTransactionCount", [address, "latest"], timeout, proxy)).map(_hex_str_to_int)
|
|
307
91
|
|
|
308
|
-
try:
|
|
309
|
-
status = None
|
|
310
|
-
receipt = cast(dict[str, Any], res.ok)
|
|
311
|
-
if "status" in receipt:
|
|
312
|
-
status = int(receipt["status"], 16)
|
|
313
|
-
return Ok(
|
|
314
|
-
TxReceipt(
|
|
315
|
-
tx_hash=tx_hash,
|
|
316
|
-
tx_index=int(receipt["transactionIndex"], 16),
|
|
317
|
-
block_number=int(receipt["blockNumber"], 16),
|
|
318
|
-
from_address=receipt["from"],
|
|
319
|
-
to_address=receipt.get("to"),
|
|
320
|
-
contract_address=receipt.get("contractAddress"),
|
|
321
|
-
status=status,
|
|
322
|
-
),
|
|
323
|
-
data=res.data,
|
|
324
|
-
)
|
|
325
|
-
except Exception as err:
|
|
326
|
-
return Err(f"exception: {err}", data=res.data)
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
def eth_get_transaction_by_hash(
|
|
330
|
-
rpc_urls: Nodes,
|
|
331
|
-
tx_hash: str,
|
|
332
|
-
timeout: int = 10,
|
|
333
|
-
proxies: Proxies = None,
|
|
334
|
-
attempts: int = 1,
|
|
335
|
-
) -> Result[TxData]:
|
|
336
|
-
res = rpc_call(
|
|
337
|
-
nodes=rpc_urls,
|
|
338
|
-
method="eth_getTransactionByHash",
|
|
339
|
-
params=[tx_hash],
|
|
340
|
-
timeout=timeout,
|
|
341
|
-
proxies=proxies,
|
|
342
|
-
attempts=attempts,
|
|
343
|
-
)
|
|
344
|
-
if isinstance(res, Err):
|
|
345
|
-
return res
|
|
346
|
-
if res.ok is None:
|
|
347
|
-
return Err("not_found", data=res.data)
|
|
348
92
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
return Ok(
|
|
352
|
-
TxData(
|
|
353
|
-
block_number=int(tx["blockNumber"], 16) if tx["blockNumber"] is not None else None,
|
|
354
|
-
from_=tx["from"],
|
|
355
|
-
to=tx.get("to"),
|
|
356
|
-
gas=int(tx["gas"], 16),
|
|
357
|
-
gas_price=int(tx["gasPrice"], 16),
|
|
358
|
-
value=int(tx["value"], 16),
|
|
359
|
-
nonce=int(tx["nonce"], 16),
|
|
360
|
-
input=tx["input"],
|
|
361
|
-
hash=tx_hash,
|
|
362
|
-
v=int(tx["v"], 16),
|
|
363
|
-
r=tx.get("r"),
|
|
364
|
-
s=tx.get("s"),
|
|
365
|
-
),
|
|
366
|
-
data=res.data,
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
except Exception as err:
|
|
370
|
-
return Err(f"exception: {err}", data=res.data)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
def eth_call(
|
|
374
|
-
rpc_urls: Nodes,
|
|
375
|
-
to: str,
|
|
376
|
-
data: str,
|
|
377
|
-
timeout: int = 10,
|
|
378
|
-
proxies: Proxies = None,
|
|
379
|
-
attempts: int = 1,
|
|
380
|
-
) -> Result[str]:
|
|
381
|
-
return rpc_call(
|
|
382
|
-
nodes=rpc_urls,
|
|
383
|
-
method="eth_call",
|
|
384
|
-
params=[{"to": to, "data": data}, "latest"],
|
|
385
|
-
timeout=timeout,
|
|
386
|
-
proxies=proxies,
|
|
387
|
-
attempts=attempts,
|
|
388
|
-
)
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
def eth_estimate_gas(
|
|
392
|
-
rpc_urls: Nodes,
|
|
93
|
+
async def eth_estimate_gas(
|
|
94
|
+
node: str,
|
|
393
95
|
from_: str,
|
|
394
96
|
to: str | None = None,
|
|
395
97
|
value: int | None = 0,
|
|
396
98
|
data: str | None = None,
|
|
397
99
|
type_: Literal["0x0", "0x2"] | None = None,
|
|
398
|
-
timeout:
|
|
399
|
-
|
|
400
|
-
attempts: int = 1,
|
|
100
|
+
timeout: float = TIMEOUT,
|
|
101
|
+
proxy: str | None = None,
|
|
401
102
|
) -> Result[int]:
|
|
402
103
|
params: dict[str, Any] = {"from": from_}
|
|
403
104
|
if to:
|
|
@@ -408,68 +109,165 @@ def eth_estimate_gas(
|
|
|
408
109
|
params["value"] = hex(value)
|
|
409
110
|
if type_:
|
|
410
111
|
params["type"] = type_
|
|
411
|
-
return rpc_call(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
proxies=proxies,
|
|
417
|
-
attempts=attempts,
|
|
418
|
-
).and_then(hex_str_to_int)
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
def eth_gas_price(rpc_urls: Nodes, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[int]:
|
|
422
|
-
return rpc_call(
|
|
423
|
-
nodes=rpc_urls,
|
|
424
|
-
method="eth_gasPrice",
|
|
425
|
-
params=[],
|
|
426
|
-
timeout=timeout,
|
|
427
|
-
proxies=proxies,
|
|
428
|
-
attempts=attempts,
|
|
429
|
-
).and_then(hex_str_to_int)
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
def eth_syncing(rpc_urls: Nodes, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[bool | dict[str, int]]:
|
|
433
|
-
res = rpc_call(nodes=rpc_urls, method="eth_syncing", params=[], timeout=timeout, proxies=proxies, attempts=attempts)
|
|
434
|
-
if isinstance(res, Err):
|
|
435
|
-
return res
|
|
112
|
+
return (await rpc_call(node, "eth_estimateGas", [params], timeout, proxy)).map(_hex_str_to_int)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async def eth_send_raw_transaction(node: str, raw_tx: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[str]:
|
|
116
|
+
return await rpc_call(node, "eth_sendRawTransaction", [raw_tx], timeout, proxy)
|
|
436
117
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
118
|
+
|
|
119
|
+
async def eth_get_transaction_receipt(
|
|
120
|
+
node: str, tx_hash: str, timeout: float = TIMEOUT, proxy: str | None = None
|
|
121
|
+
) -> Result[TxReceipt]:
|
|
122
|
+
def convert_hex_str_ints(receipt: dict[str, Any]) -> TxReceipt:
|
|
123
|
+
int_fields = {
|
|
124
|
+
"blockNumber",
|
|
125
|
+
"cumulativeGasUsed",
|
|
126
|
+
"effectiveGasPrice",
|
|
127
|
+
"gasUsed",
|
|
128
|
+
"status",
|
|
129
|
+
"transactionIndex",
|
|
130
|
+
"type",
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
converted: dict[str, Any] = {}
|
|
134
|
+
for key, value in receipt.items():
|
|
135
|
+
if key in int_fields and isinstance(value, str) and value.startswith("0x"):
|
|
136
|
+
converted[key] = int(value, 16)
|
|
442
137
|
else:
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
138
|
+
converted[key] = value
|
|
139
|
+
|
|
140
|
+
return cast(TxReceipt, converted)
|
|
141
|
+
|
|
142
|
+
res = await rpc_call(node, "eth_getTransactionReceipt", [tx_hash], timeout, proxy)
|
|
143
|
+
if res.is_err():
|
|
144
|
+
return res
|
|
145
|
+
|
|
146
|
+
if res.unwrap() is None:
|
|
147
|
+
return Result.err("no_receipt", res.extra)
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
return Result.ok(convert_hex_str_ints(res.unwrap()), res.extra)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
return Result.err(e, res.extra)
|
|
153
|
+
|
|
447
154
|
|
|
448
|
-
|
|
155
|
+
# -- end eth rpc calls --
|
|
449
156
|
|
|
157
|
+
# -- start erc20 rpc calls --
|
|
450
158
|
|
|
451
|
-
def get_tx_status(rpc_urls: Nodes, tx_hash: str, timeout: int = 5, proxies: Proxies = None, attempts: int = 5) -> Result[int]:
|
|
452
|
-
res: Result[int] = Err("not started yet")
|
|
453
|
-
for _ in range(attempts):
|
|
454
|
-
node = cast(str, random_choice(rpc_urls))
|
|
455
|
-
cast(str | None, random_choice(proxies))
|
|
456
|
-
receipt_res = eth_get_transaction_receipt(node, tx_hash, timeout, proxies=proxies, attempts=1)
|
|
457
|
-
if isinstance(receipt_res, Err) and receipt_res.err == "no_receipt":
|
|
458
|
-
return receipt_res
|
|
459
|
-
if isinstance(receipt_res, Ok) and receipt_res.ok.status is None:
|
|
460
|
-
return Err("no_status", data=res.data)
|
|
461
159
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
160
|
+
async def erc20_balance(node: str, token: str, wallet: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
|
|
161
|
+
data = "0x70a08231000000000000000000000000" + wallet[2:]
|
|
162
|
+
params = [{"to": token, "data": data}, "latest"]
|
|
163
|
+
return (await rpc_call(node, "eth_call", params, timeout, proxy)).map(_hex_str_to_int)
|
|
465
164
|
|
|
466
|
-
return res
|
|
467
165
|
|
|
166
|
+
async def erc20_name(node: str, token: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[str]:
|
|
167
|
+
params = [{"to": token, "data": "0x06fdde03"}, "latest"]
|
|
168
|
+
return (await rpc_call(node, "eth_call", params, timeout, proxy)).map(_normalize_str)
|
|
468
169
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
170
|
+
|
|
171
|
+
async def erc20_symbol(node: str, token: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[str]:
|
|
172
|
+
params = [{"to": token, "data": "0x95d89b41"}, "latest"]
|
|
173
|
+
return (await rpc_call(node, "eth_call", params, timeout, proxy)).map(_normalize_str)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
async def erc20_decimals(node: str, token: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
|
|
177
|
+
params = [{"to": token, "data": "0x313ce567"}, "latest"]
|
|
178
|
+
res = await rpc_call(node, "eth_call", params, timeout, proxy)
|
|
179
|
+
if res.is_err():
|
|
472
180
|
return res
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
181
|
+
try:
|
|
182
|
+
if res.unwrap() == "0x":
|
|
183
|
+
return res.with_error("no_decimals")
|
|
184
|
+
value = res.unwrap()
|
|
185
|
+
result = eth_utils.to_int(hexstr=value[0:66]) if len(value) > 66 else eth_utils.to_int(hexstr=value)
|
|
186
|
+
return res.with_value(result)
|
|
187
|
+
except Exception as e:
|
|
188
|
+
return res.with_error(e)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
# -- end erc20 rpc calls --
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
async def ens_name(node: str, address: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[str | None]:
|
|
195
|
+
ens_registry_address: str = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
|
|
196
|
+
func_selector_resolver: str = "0x0178b8bf" # resolver(bytes32)
|
|
197
|
+
func_selector_name: str = "0x691f3431" # name(bytes32)
|
|
198
|
+
|
|
199
|
+
checksum_addr = eth_utils.to_checksum_address(address)
|
|
200
|
+
reverse_name = checksum_addr.lower()[2:] + ".addr.reverse"
|
|
201
|
+
name_hash_hex = ens.utils.normal_name_to_hash(reverse_name).hex()
|
|
202
|
+
|
|
203
|
+
resolver_data = func_selector_resolver + name_hash_hex
|
|
204
|
+
|
|
205
|
+
resolver_params = [{"to": ens_registry_address, "data": resolver_data}, "latest"]
|
|
206
|
+
|
|
207
|
+
resolver_res = await rpc_call(node, method="eth_call", params=resolver_params, timeout=timeout, proxy=proxy)
|
|
208
|
+
if resolver_res.is_err():
|
|
209
|
+
return resolver_res
|
|
210
|
+
|
|
211
|
+
extra = {"resolver_response": resolver_res.to_dict()}
|
|
212
|
+
|
|
213
|
+
if resolver_res.is_ok() and len(resolver_res.unwrap()) != 66:
|
|
214
|
+
return Result.ok(None, extra)
|
|
215
|
+
|
|
216
|
+
resolver_address = eth_utils.to_checksum_address("0x" + resolver_res.unwrap()[-40:])
|
|
217
|
+
|
|
218
|
+
name_data: str = func_selector_name + name_hash_hex
|
|
219
|
+
name_params = [{"to": resolver_address, "data": name_data}, "latest"]
|
|
220
|
+
|
|
221
|
+
name_res = await rpc_call(node, "eth_call", name_params, timeout=timeout, proxy=proxy)
|
|
222
|
+
|
|
223
|
+
extra["name_response"] = name_res.to_dict()
|
|
224
|
+
|
|
225
|
+
if name_res.is_err():
|
|
226
|
+
return Result.err(name_res.unwrap_error(), extra)
|
|
227
|
+
|
|
228
|
+
if name_res.unwrap() == "0x":
|
|
229
|
+
return Result.ok(None, extra)
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
hex_data = name_res.unwrap()
|
|
233
|
+
length_hex = hex_data[66:130]
|
|
234
|
+
str_len = int(length_hex, 16) * 2
|
|
235
|
+
name_hex = hex_data[130 : 130 + str_len]
|
|
236
|
+
return Result.ok(bytes.fromhex(name_hex).decode("utf-8"), extra)
|
|
237
|
+
except Exception as e:
|
|
238
|
+
return Result.err(e, extra)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# -- start other --
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
async def get_base_fee_per_gas(node: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
|
|
245
|
+
res = await eth_get_block_by_number(node, "latest", False, timeout=timeout, proxy=proxy)
|
|
246
|
+
if res.is_err():
|
|
247
|
+
return Result.err(res.unwrap_error(), res.extra)
|
|
248
|
+
if "baseFeePerGas" in res.unwrap():
|
|
249
|
+
return res.with_value(int(res.unwrap()["baseFeePerGas"], 16))
|
|
250
|
+
return Result.err("no_base_fee_per_gas", res.extra)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
async def get_tx_status(node: str, tx_hash: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
|
|
254
|
+
res = await eth_get_transaction_receipt(node, tx_hash, timeout=timeout, proxy=proxy)
|
|
255
|
+
if res.is_err():
|
|
256
|
+
return Result.err(res.unwrap_error(), res.extra)
|
|
257
|
+
status = res.unwrap().get("status")
|
|
258
|
+
if status is None:
|
|
259
|
+
return Result.err("no_status", res.extra)
|
|
260
|
+
return Result.ok(status, res.extra)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# -- end other --
|
|
264
|
+
|
|
265
|
+
# -- utils --
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _hex_str_to_int(value: str) -> int:
|
|
269
|
+
return int(value, 16)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _normalize_str(value: str) -> str:
|
|
273
|
+
return "".join(filter(lambda x: x in string.printable, eth_utils.to_text(hexstr=value))).strip()
|