mm-eth 0.5.6__py3-none-any.whl → 0.5.8__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/async_rpc.py +93 -0
- mm_eth/ens.py +22 -1
- mm_eth/erc20.py +22 -1
- mm_eth/ethernodes.py +2 -2
- mm_eth/utils.py +17 -1
- {mm_eth-0.5.6.dist-info → mm_eth-0.5.8.dist-info}/METADATA +4 -4
- {mm_eth-0.5.6.dist-info → mm_eth-0.5.8.dist-info}/RECORD +9 -8
- {mm_eth-0.5.6.dist-info → mm_eth-0.5.8.dist-info}/WHEEL +0 -0
- {mm_eth-0.5.6.dist-info → mm_eth-0.5.8.dist-info}/entry_points.txt +0 -0
mm_eth/async_rpc.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import websockets
|
|
5
|
+
from mm_crypto_utils import Nodes, Proxies, random_node, random_proxy
|
|
6
|
+
from mm_std import Err, Ok, Result, ahr
|
|
7
|
+
|
|
8
|
+
from mm_eth.utils import hex_str_to_int
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def rpc_call(
|
|
12
|
+
*,
|
|
13
|
+
nodes: Nodes,
|
|
14
|
+
method: str,
|
|
15
|
+
params: list[object],
|
|
16
|
+
id_: int = 1,
|
|
17
|
+
timeout: int = 10,
|
|
18
|
+
proxies: Proxies = None,
|
|
19
|
+
attempts: int = 1,
|
|
20
|
+
) -> Result[Any]:
|
|
21
|
+
data = {"jsonrpc": "2.0", "method": method, "params": params, "id": id_}
|
|
22
|
+
res: Result[Any] = Err("not started yet")
|
|
23
|
+
for _ in range(attempts):
|
|
24
|
+
node = random_node(nodes)
|
|
25
|
+
res = (
|
|
26
|
+
await _http_call(node, data, timeout, random_proxy(proxies))
|
|
27
|
+
if node.startswith("http")
|
|
28
|
+
else await _ws_call(node, data, timeout)
|
|
29
|
+
)
|
|
30
|
+
if isinstance(res, Ok):
|
|
31
|
+
return res
|
|
32
|
+
return res
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
async def _http_call(node: str, data: dict[str, object], timeout: int, proxy: str | None) -> Result[Any]:
|
|
36
|
+
res = await ahr(node, method="POST", proxy=proxy, timeout=timeout, params=data, json_params=True)
|
|
37
|
+
if res.is_error():
|
|
38
|
+
return res.to_err_result()
|
|
39
|
+
try:
|
|
40
|
+
err = res.json.get("error", {}).get("message", "")
|
|
41
|
+
if err:
|
|
42
|
+
return res.to_err_result(f"service_error: {err}")
|
|
43
|
+
if "result" in res.json:
|
|
44
|
+
return res.to_ok_result(res.json["result"])
|
|
45
|
+
return res.to_err_result("unknown_response")
|
|
46
|
+
except Exception as err:
|
|
47
|
+
return res.to_err_result(f"exception: {err}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def _ws_call(node: str, data: dict[str, object], timeout: int) -> Result[Any]:
|
|
51
|
+
try:
|
|
52
|
+
async with websockets.connect(node, timeout=timeout) as ws:
|
|
53
|
+
await ws.send(json.dumps(data))
|
|
54
|
+
response = json.loads(await ws.recv())
|
|
55
|
+
|
|
56
|
+
err = response.get("error", {}).get("message", "")
|
|
57
|
+
if err:
|
|
58
|
+
return Err(f"service_error: {err}")
|
|
59
|
+
if "result" in response:
|
|
60
|
+
return Ok(response["result"], response)
|
|
61
|
+
return Err(f"unknown_response: {response}")
|
|
62
|
+
except TimeoutError:
|
|
63
|
+
return Err("timeout")
|
|
64
|
+
except Exception as err:
|
|
65
|
+
return Err(f"exception: {err}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def eth_block_number(rpc_urls: Nodes, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[int]:
|
|
69
|
+
return (
|
|
70
|
+
await rpc_call(
|
|
71
|
+
nodes=rpc_urls,
|
|
72
|
+
method="eth_blockNumber",
|
|
73
|
+
params=[],
|
|
74
|
+
timeout=timeout,
|
|
75
|
+
proxies=proxies,
|
|
76
|
+
attempts=attempts,
|
|
77
|
+
)
|
|
78
|
+
).and_then(hex_str_to_int)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async def eth_get_balance(
|
|
82
|
+
rpc_urls: Nodes, address: str, timeout: int = 10, proxies: Proxies = None, attempts: int = 1
|
|
83
|
+
) -> Result[int]:
|
|
84
|
+
return (
|
|
85
|
+
await rpc_call(
|
|
86
|
+
nodes=rpc_urls,
|
|
87
|
+
method="eth_getBalance",
|
|
88
|
+
params=[address, "latest"],
|
|
89
|
+
timeout=timeout,
|
|
90
|
+
proxies=proxies,
|
|
91
|
+
attempts=attempts,
|
|
92
|
+
)
|
|
93
|
+
).and_then(hex_str_to_int)
|
mm_eth/ens.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from mm_crypto_utils import Nodes, Proxies, random_node, random_proxy
|
|
2
2
|
from mm_std import Err, Ok, Result
|
|
3
3
|
|
|
4
|
-
from mm_eth.utils import get_w3
|
|
4
|
+
from mm_eth.utils import get_async_w3, get_w3
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def get_name_with_retries(
|
|
@@ -21,3 +21,24 @@ def get_name(rpc_url: str, address: str, timeout: float = 5, proxy: str | None =
|
|
|
21
21
|
return Ok(w3.ens.name(w3.to_checksum_address(address))) # type: ignore[union-attr]
|
|
22
22
|
except Exception as e:
|
|
23
23
|
return Err(e)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def async_get_name(rpc_url: str, address: str, timeout: float = 5, proxy: str | None = None) -> Result[str | None]:
|
|
27
|
+
try:
|
|
28
|
+
w3 = await get_async_w3(rpc_url, timeout=timeout, proxy=proxy)
|
|
29
|
+
res = await w3.ens.name(w3.to_checksum_address(address)) # type: ignore[union-attr]
|
|
30
|
+
await w3.provider.disconnect()
|
|
31
|
+
return Ok(res)
|
|
32
|
+
except Exception as e:
|
|
33
|
+
return Err(e)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def async_get_name_with_retries(
|
|
37
|
+
rpc_urls: Nodes, address: str, retries: int, timeout: float = 5, proxies: Proxies = None
|
|
38
|
+
) -> Result[str | None]:
|
|
39
|
+
res: Result[str | None] = Err("not started yet")
|
|
40
|
+
for _ in range(retries):
|
|
41
|
+
res = await async_get_name(random_node(rpc_urls), address, timeout=timeout, proxy=random_proxy(proxies))
|
|
42
|
+
if res.is_ok():
|
|
43
|
+
return res
|
|
44
|
+
return res
|
mm_eth/erc20.py
CHANGED
|
@@ -11,7 +11,7 @@ from eth_utils import to_checksum_address, to_hex
|
|
|
11
11
|
from mm_crypto_utils import Nodes, Proxies
|
|
12
12
|
from mm_std import Err, Ok, Result
|
|
13
13
|
|
|
14
|
-
from mm_eth import rpc
|
|
14
|
+
from mm_eth import async_rpc, rpc
|
|
15
15
|
from mm_eth.rpc import Log
|
|
16
16
|
from mm_eth.tx import SignedTx, sign_legacy_tx, sign_tx
|
|
17
17
|
from mm_eth.utils import hex_str_to_int, hex_to_bytes, log_topic_to_address
|
|
@@ -70,6 +70,27 @@ def get_balance(
|
|
|
70
70
|
).and_then(hex_str_to_int)
|
|
71
71
|
|
|
72
72
|
|
|
73
|
+
async def async_get_balance(
|
|
74
|
+
rpc_urls: Nodes,
|
|
75
|
+
token_address: str,
|
|
76
|
+
user_address: str,
|
|
77
|
+
timeout: int = 10,
|
|
78
|
+
proxies: Proxies = None,
|
|
79
|
+
attempts: int = 1,
|
|
80
|
+
) -> Result[int]:
|
|
81
|
+
data = "0x70a08231000000000000000000000000" + user_address[2:]
|
|
82
|
+
return (
|
|
83
|
+
await async_rpc.rpc_call(
|
|
84
|
+
nodes=rpc_urls,
|
|
85
|
+
method="eth_call",
|
|
86
|
+
params=[{"to": token_address, "data": data}, "latest"],
|
|
87
|
+
timeout=timeout,
|
|
88
|
+
proxies=proxies,
|
|
89
|
+
attempts=attempts,
|
|
90
|
+
)
|
|
91
|
+
).and_then(hex_str_to_int)
|
|
92
|
+
|
|
93
|
+
|
|
73
94
|
def get_name(rpc_urls: Nodes, address: str, timeout: int = 10, proxies: Proxies = None, attempts: int = 1) -> Result[str]:
|
|
74
95
|
return rpc.rpc_call(
|
|
75
96
|
nodes=rpc_urls,
|
mm_eth/ethernodes.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
|
-
from mm_std import CHROME_USER_AGENT, Result, hr
|
|
3
|
+
from mm_std import CHROME_USER_AGENT, Ok, Result, hr
|
|
4
4
|
from pydantic import BaseModel, Field
|
|
5
5
|
|
|
6
6
|
|
|
@@ -29,6 +29,6 @@ def search_nodes(offset: int = 0, proxy: str | None = None) -> Result[SearchResu
|
|
|
29
29
|
if res.is_error():
|
|
30
30
|
return res.to_err_result()
|
|
31
31
|
try:
|
|
32
|
-
return
|
|
32
|
+
return Ok(SearchResult(**res.json))
|
|
33
33
|
except Exception as e:
|
|
34
34
|
return res.to_err_result(f"exception: {e}")
|
mm_eth/utils.py
CHANGED
|
@@ -2,13 +2,15 @@ import re
|
|
|
2
2
|
from decimal import Decimal, localcontext
|
|
3
3
|
from typing import Any, cast
|
|
4
4
|
|
|
5
|
+
import aiohttp
|
|
5
6
|
import eth_utils
|
|
6
7
|
import pydash
|
|
8
|
+
from aiohttp_socks import ProxyConnector
|
|
7
9
|
from eth_typing import HexStr
|
|
8
10
|
from hexbytes import HexBytes
|
|
9
11
|
from mm_std import Err, Ok, Result, number_with_separator
|
|
10
12
|
from pydantic import BaseModel
|
|
11
|
-
from web3 import Web3
|
|
13
|
+
from web3 import AsyncWeb3, Web3
|
|
12
14
|
from web3.types import Wei
|
|
13
15
|
|
|
14
16
|
|
|
@@ -193,6 +195,20 @@ def get_w3(rpc_url: str, timeout: float | None = None, proxy: str | None = None)
|
|
|
193
195
|
return Web3(Web3.HTTPProvider(rpc_url, request_kwargs=request_kwargs))
|
|
194
196
|
|
|
195
197
|
|
|
198
|
+
async def get_async_w3(rpc_url: str, timeout: float | None = None, proxy: str | None = None) -> AsyncWeb3:
|
|
199
|
+
request_kwargs: dict[str, object] = {"timeout": timeout}
|
|
200
|
+
if proxy and proxy.startswith("http"):
|
|
201
|
+
request_kwargs["proxy"] = proxy
|
|
202
|
+
provider = AsyncWeb3.AsyncHTTPProvider(rpc_url, request_kwargs=request_kwargs, exception_retry_configuration=None)
|
|
203
|
+
w3 = AsyncWeb3(provider)
|
|
204
|
+
|
|
205
|
+
if proxy and proxy.startswith("socks"):
|
|
206
|
+
session = aiohttp.ClientSession(connector=ProxyConnector.from_url(proxy))
|
|
207
|
+
await provider.cache_async_session(session)
|
|
208
|
+
|
|
209
|
+
return w3
|
|
210
|
+
|
|
211
|
+
|
|
196
212
|
def name_network(chain_id: int) -> str:
|
|
197
213
|
match chain_id:
|
|
198
214
|
case 1:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mm-eth
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.8
|
|
4
4
|
Requires-Python: >=3.12
|
|
5
|
-
Requires-Dist:
|
|
6
|
-
Requires-Dist:
|
|
5
|
+
Requires-Dist: aiohttp-socks~=0.10.1
|
|
6
|
+
Requires-Dist: mm-crypto-utils>=0.2.9
|
|
7
7
|
Requires-Dist: typer>=0.15.2
|
|
8
|
-
Requires-Dist: web3~=7.
|
|
8
|
+
Requires-Dist: web3~=7.9.0
|
|
9
9
|
Requires-Dist: websocket-client~=1.8.0
|
|
@@ -2,17 +2,18 @@ mm_eth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
2
2
|
mm_eth/abi.py,sha256=Qf-QOsR9QexyQM9XWKNeTMkRarIL3XQJbaDbJ8ifMrw,4856
|
|
3
3
|
mm_eth/account.py,sha256=k0MNMatBe0zo1iKZiB_Tq6zyEIo22IncD6ewNUJp3dY,2075
|
|
4
4
|
mm_eth/anvil.py,sha256=98RCfI7dEpxFBTV6UErYvubWVP3n0ctUFn1--4kZ84U,1603
|
|
5
|
+
mm_eth/async_rpc.py,sha256=8rMtg3Hr6Pn7NaFROxxWsXQSM8wJ92EUYlOOoc6_TOk,2934
|
|
5
6
|
mm_eth/constants.py,sha256=Cy_G-IleBH4gAZ4ok8AGHHlqmdW0ZM7ZldyVpzAfWLs,54
|
|
6
7
|
mm_eth/deploy.py,sha256=SB3ruY808_5UnG8kHR34uVP66P3zOWZu0ImKD7UUv2s,691
|
|
7
|
-
mm_eth/ens.py,sha256=
|
|
8
|
-
mm_eth/erc20.py,sha256=
|
|
9
|
-
mm_eth/ethernodes.py,sha256=
|
|
8
|
+
mm_eth/ens.py,sha256=amMs4xbBia1hVHKglKwqeheL_8NpukKNAZIsg65wpDA,1689
|
|
9
|
+
mm_eth/erc20.py,sha256=LZmTkjmXpN7sUhOtSLiTqb8Ubet9C9q32EQwTS78OqE,7378
|
|
10
|
+
mm_eth/ethernodes.py,sha256=V4VVbC6Nr9jhwT7blxtLugXC5KfXqE8n-kP0VvGHbqo,3070
|
|
10
11
|
mm_eth/json_encoder.py,sha256=S4oD-qfTVztMb4sRpY1puhBQwOBofTyQXWszmdXk4og,433
|
|
11
12
|
mm_eth/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
13
|
mm_eth/rpc.py,sha256=k0eHxo_Dp6G0fHQ_rD-QbwOJz5ngC6kxBjl5NEHnyw8,13832
|
|
13
14
|
mm_eth/solc.py,sha256=dYRvT8PjZlLDZhNsc_-0790Eug_ZwU2G-iBfIdGj6wQ,1071
|
|
14
15
|
mm_eth/tx.py,sha256=efSoMCoWkenbGdHo1_LX66_Edz1HvED5-J_i3wrHwMw,4051
|
|
15
|
-
mm_eth/utils.py,sha256=
|
|
16
|
+
mm_eth/utils.py,sha256=bkh4EBaxTYEnrR1s7zQ2hzZCwXw1r-2HmfN-dpTXwRc,8069
|
|
16
17
|
mm_eth/vault.py,sha256=h8NyiOQh5YFskh1lZA3KyvnJUnxl9769ME2ChplG0CM,1477
|
|
17
18
|
mm_eth/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
19
|
mm_eth/cli/calcs.py,sha256=cLFTYNAN-I53tUiSg-zFfVr2afjIZPftDDjHj16FBz0,1068
|
|
@@ -41,7 +42,7 @@ mm_eth/cli/cmd/wallet/private_key_cmd.py,sha256=Fv_2OLog1h32pIP7PJITwl_pHdy3BXva
|
|
|
41
42
|
mm_eth/cli/examples/balances.toml,sha256=i_ALpiEcf8-0TFiUg1cgJhxxfHYeBl9x0b3tnUWjswU,421
|
|
42
43
|
mm_eth/cli/examples/call_contract.toml,sha256=ZQWK-409V_vLIZ2bsRD5RCWPPzShPz2KJTTRQY4YaGw,248
|
|
43
44
|
mm_eth/cli/examples/transfer.toml,sha256=8mWuphDquoSDJw-hb9VJqtonjmv3kJ5Ip8jJ4t5YnfM,1810
|
|
44
|
-
mm_eth-0.5.
|
|
45
|
-
mm_eth-0.5.
|
|
46
|
-
mm_eth-0.5.
|
|
47
|
-
mm_eth-0.5.
|
|
45
|
+
mm_eth-0.5.8.dist-info/METADATA,sha256=8YvcBQc-8hK5tLsYKGyxmUtfLl9o8WeEXtn4verNNFY,244
|
|
46
|
+
mm_eth-0.5.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
47
|
+
mm_eth-0.5.8.dist-info/entry_points.txt,sha256=aGhpsozl8NIrkuUcX5fSgURCcDhr3ShUdeTSIrJq4oc,46
|
|
48
|
+
mm_eth-0.5.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|