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/cli/cmd/vault_cmd.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
import mm_crypto_utils
|
|
4
|
-
from mm_std import fatal, print_plain
|
|
5
|
-
|
|
6
|
-
from mm_eth import vault
|
|
7
|
-
from mm_eth.account import is_private_key
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def run(keys_url: str, vault_token: str, keys_file: Path) -> None:
|
|
11
|
-
private_keys = mm_crypto_utils.read_items_from_file(keys_file, is_private_key)
|
|
12
|
-
if not private_keys:
|
|
13
|
-
fatal("private keys not found")
|
|
14
|
-
|
|
15
|
-
res = vault.set_keys_from_vault(keys_url, vault_token, private_keys)
|
|
16
|
-
if res.is_ok() and res.ok is True:
|
|
17
|
-
print_plain(f"saved {len(private_keys)} private keys to the vault")
|
|
18
|
-
else:
|
|
19
|
-
fatal(f"error: {res.err}")
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
addresses = """
|
|
2
|
-
0x10fd602Bff689e64D4720D1DCCCD3494f1f16623
|
|
3
|
-
0x58487485c3858109f5A37e42546FE87473f79a4b
|
|
4
|
-
0x97C77B548aE0d4925F5C201220fC6d8996424309
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
tokens = """
|
|
8
|
-
0x7EdF3b8579c21A8820b4C0B8352541c1CE04045f # USDT
|
|
9
|
-
0x6a55fe4884DE7E1d904BdC47A3BA092240ae9B39 # USDC
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
nodes = """
|
|
13
|
-
https://arb1.arbitrum.io/rpc
|
|
14
|
-
https://rpc.arb1.arbitrum.gateway.fm
|
|
15
|
-
https://arbitrum-one.publicnode.com
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
round_ndigits = 3
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# Each line is a transfer instruction, with format: from_address to_address [value]
|
|
2
|
-
# Value is optional. If value is not set, default_value will be used
|
|
3
|
-
# value is an expression that can contain variable 'balance' and 'random' function
|
|
4
|
-
transfers = """
|
|
5
|
-
0x10fd602Bff689e64D4720D1DCCCD3494f1f16623 0x58487485c3858109f5A37e42546FE87473f79a4b 0.1t # comments are allowed here
|
|
6
|
-
0x97C77B548aE0d4925F5C201220fC6d8996424309 0x7EdF3b8579c21A8820b4C0B8352541c1CE04045f 0.2balance-random(0.1t,0.5t)
|
|
7
|
-
0x10ecB8d838746643E613f6B5218C8e342593225c 0xE19242B72a4833eD86F1b2015d4E59052A2b278b
|
|
8
|
-
file: /path/to/other_transfers.txt # transfers from this file will be added
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
private_keys = """
|
|
12
|
-
0x7bb5b9c0ba991275f84b796b4d25fd3a8d7320911f50fade85410e7a2b000632
|
|
13
|
-
0xb7e0b671e176b04ceb0897a698d34771bfe9acf29273dc52a141be6e97145a00
|
|
14
|
-
file: /path/to/other_private_keys.txt
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
token = "0x60631C856303731BE4deb81C0303F80B652aA5b4" # If not specified, it ETH transfers
|
|
18
|
-
|
|
19
|
-
max_fee = "1.2base_fee+1gwei+random(1,200)" # 'base_fee' variable is supported
|
|
20
|
-
|
|
21
|
-
priority_fee = "1gwei+random(1,12)"
|
|
22
|
-
|
|
23
|
-
# Don't transfer if max_fee > max_fee_limit, optional
|
|
24
|
-
max_fee_limit = "10.1gwei-random(1,10)"
|
|
25
|
-
|
|
26
|
-
gas = "estimate+random(100,200)-19" # 'estimate' variable is supported
|
|
27
|
-
|
|
28
|
-
# default_value is used if transfer.value is not set in transfers. It's optional.
|
|
29
|
-
default_value = "0.5balance-random(1.5t,3t)+11t" # supported var_name=balance. For ERC20 token use 't' suffix.
|
|
30
|
-
|
|
31
|
-
value_min_limit = "0.5t+random(1,2)-7" # don't transfer if transfer.value is less than this
|
|
32
|
-
|
|
33
|
-
delay = "random(1.123,10)+1" # secs, optional
|
|
34
|
-
|
|
35
|
-
log_debug = "/path/to/file_debug.log" # optional
|
|
36
|
-
|
|
37
|
-
log_info = "/path/to/file_info.log" # optional
|
|
38
|
-
|
|
39
|
-
round_ndigits = 6 # optional, default=5
|
|
40
|
-
|
|
41
|
-
chain_id = 421613
|
|
42
|
-
|
|
43
|
-
nodes = """
|
|
44
|
-
https://arbitrum-goerli.publicnode.com
|
|
45
|
-
https://rpc.goerli.arbitrum.gateway.fm
|
|
46
|
-
"""
|
mm_eth/cli/print_helpers.py
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from rich.live import Live
|
|
2
|
-
from rich.table import Table
|
|
3
|
-
|
|
4
|
-
from mm_eth import erc20, rpc
|
|
5
|
-
from mm_eth.utils import from_wei_str
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def print_balances(
|
|
9
|
-
rpc_nodes: list[str],
|
|
10
|
-
addresses: list[str],
|
|
11
|
-
*,
|
|
12
|
-
token_address: str | None = None,
|
|
13
|
-
token_decimals: int | None = None,
|
|
14
|
-
round_ndigits: int = 5,
|
|
15
|
-
) -> None:
|
|
16
|
-
table = Table(title="balances")
|
|
17
|
-
table.add_column("n")
|
|
18
|
-
table.add_column("address")
|
|
19
|
-
table.add_column("nonce")
|
|
20
|
-
table.add_column("balance, eth")
|
|
21
|
-
if token_address is not None and token_decimals is not None:
|
|
22
|
-
table.add_column("token, t")
|
|
23
|
-
with Live(table, refresh_per_second=0.5):
|
|
24
|
-
for count, address in enumerate(addresses):
|
|
25
|
-
nonce = str(rpc.eth_get_transaction_count(rpc_nodes, address, attempts=5).ok_or_err())
|
|
26
|
-
balance = rpc.eth_get_balance(rpc_nodes, address, attempts=5).map_or_else(
|
|
27
|
-
lambda err: err,
|
|
28
|
-
lambda ok: from_wei_str(ok, "eth", round_ndigits),
|
|
29
|
-
)
|
|
30
|
-
row: list[str] = [str(count), address, nonce, balance]
|
|
31
|
-
if token_address is not None and token_decimals is not None:
|
|
32
|
-
erc20_balance = erc20.get_balance(rpc_nodes, token_address, address, attempts=5).map_or_else(
|
|
33
|
-
lambda err: err,
|
|
34
|
-
lambda ok: from_wei_str(ok, "t", decimals=token_decimals, round_ndigits=round_ndigits),
|
|
35
|
-
)
|
|
36
|
-
row.append(erc20_balance)
|
|
37
|
-
table.add_row(*row)
|
mm_eth/constants.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
SUFFIX_DECIMALS = {"eth": 18, "gwei": 9, "ether": 18}
|
mm_eth/ens.py
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
from ens.utils import normal_name_to_hash
|
|
2
|
-
from mm_crypto_utils import Nodes, Proxies, random_node, random_proxy
|
|
3
|
-
from mm_std import Err, Ok, Result
|
|
4
|
-
from web3 import Web3
|
|
5
|
-
|
|
6
|
-
from mm_eth.async_rpc import rpc_call
|
|
7
|
-
from mm_eth.utils import get_w3
|
|
8
|
-
|
|
9
|
-
ENS_REGISTRY_ADDRESS: str = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
|
|
10
|
-
FUNC_SELECTOR_RESOLVER: str = "0x0178b8bf" # resolver(bytes32)
|
|
11
|
-
FUNC_SELECTOR_NAME: str = "0x691f3431" # name(bytes32)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def get_name_with_retries(
|
|
15
|
-
rpc_urls: Nodes, address: str, retries: int, timeout: float = 5, proxies: Proxies = None
|
|
16
|
-
) -> Result[str | None]:
|
|
17
|
-
res: Result[str | None] = Err("not started yet")
|
|
18
|
-
for _ in range(retries):
|
|
19
|
-
res = get_name(random_node(rpc_urls), address, timeout=timeout, proxy=random_proxy(proxies))
|
|
20
|
-
if res.is_ok():
|
|
21
|
-
return res
|
|
22
|
-
return res
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def get_name(rpc_url: str, address: str, timeout: float = 5, proxy: str | None = None) -> Result[str | None]:
|
|
26
|
-
try:
|
|
27
|
-
w3 = get_w3(rpc_url, timeout=timeout, proxy=proxy)
|
|
28
|
-
return Ok(w3.ens.name(w3.to_checksum_address(address))) # type: ignore[union-attr]
|
|
29
|
-
except Exception as e:
|
|
30
|
-
error = str(e)
|
|
31
|
-
if not error:
|
|
32
|
-
error = e.__class__.__qualname__
|
|
33
|
-
return Err("exception: " + error)
|
|
34
|
-
|
|
35
|
-
# async def async_get_name(rpc_url: str, address: str, timeout: float = 5, proxy: str | None = None) -> Result[str | None]:
|
|
36
|
-
# w3 = await get_async_w3(rpc_url, timeout=timeout, proxy=proxy)
|
|
37
|
-
# try:
|
|
38
|
-
# res = await w3.ens.name(w3.to_checksum_address(address)) # type: ignore[union-attr]
|
|
39
|
-
# return Ok(res)
|
|
40
|
-
# except Exception as e:
|
|
41
|
-
# error = str(e)
|
|
42
|
-
# if not error:
|
|
43
|
-
# error = e.__class__.__qualname__
|
|
44
|
-
# return Err("exception: " + error)
|
|
45
|
-
# finally:
|
|
46
|
-
# await w3.provider.disconnect()
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
async def get_name_async(rpc_url: str, address: str, timeout: float = 5, proxy: str | None = None) -> Result[str | None]:
|
|
50
|
-
checksum_addr: str = Web3.to_checksum_address(address)
|
|
51
|
-
reverse_name: str = checksum_addr.lower()[2:] + ".addr.reverse"
|
|
52
|
-
name_hash_hex: str = normal_name_to_hash(reverse_name).hex()
|
|
53
|
-
|
|
54
|
-
resolver_data: str = FUNC_SELECTOR_RESOLVER + name_hash_hex
|
|
55
|
-
|
|
56
|
-
resolver_params = [{"to": ENS_REGISTRY_ADDRESS, "data": resolver_data}, "latest"]
|
|
57
|
-
|
|
58
|
-
resolver_res: Result[str] = await rpc_call(
|
|
59
|
-
nodes=rpc_url,
|
|
60
|
-
method="eth_call",
|
|
61
|
-
params=resolver_params,
|
|
62
|
-
timeout=timeout,
|
|
63
|
-
proxies=proxy,
|
|
64
|
-
attempts=1,
|
|
65
|
-
)
|
|
66
|
-
if not isinstance(resolver_res, Ok) or len(resolver_res.ok) != 66:
|
|
67
|
-
return Ok(None)
|
|
68
|
-
|
|
69
|
-
resolver_address: str = Web3.to_checksum_address("0x" + resolver_res.ok[-40:])
|
|
70
|
-
|
|
71
|
-
name_data: str = FUNC_SELECTOR_NAME + name_hash_hex
|
|
72
|
-
name_params = [{"to": resolver_address, "data": name_data}, "latest"]
|
|
73
|
-
|
|
74
|
-
name_res: Result[str] = await rpc_call(
|
|
75
|
-
nodes=rpc_url,
|
|
76
|
-
method="eth_call",
|
|
77
|
-
params=name_params,
|
|
78
|
-
timeout=timeout,
|
|
79
|
-
proxies=proxy,
|
|
80
|
-
attempts=1,
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
if isinstance(name_res, Err):
|
|
84
|
-
return name_res
|
|
85
|
-
if name_res.ok == "0x":
|
|
86
|
-
return Ok(None)
|
|
87
|
-
|
|
88
|
-
try:
|
|
89
|
-
hex_data: str = name_res.ok
|
|
90
|
-
length_hex: str = hex_data[66:130]
|
|
91
|
-
str_len: int = int(length_hex, 16) * 2
|
|
92
|
-
name_hex: str = hex_data[130 : 130 + str_len]
|
|
93
|
-
return Ok(bytes.fromhex(name_hex).decode("utf-8"))
|
|
94
|
-
except Exception as e:
|
|
95
|
-
return Err(e)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
async def get_name_with_retries_async(
|
|
99
|
-
rpc_urls: Nodes, address: str, retries: int, timeout: float = 5, proxies: Proxies = None
|
|
100
|
-
) -> Result[str | None]:
|
|
101
|
-
res: Result[str | None] = Err("not started yet")
|
|
102
|
-
for _ in range(retries):
|
|
103
|
-
res = await get_name_async(random_node(rpc_urls), address, timeout=timeout, proxy=random_proxy(proxies))
|
|
104
|
-
if res.is_ok():
|
|
105
|
-
return res
|
|
106
|
-
return res
|
mm_eth/ethernodes.py
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
|
|
3
|
-
from mm_std import CHROME_USER_AGENT, Ok, 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 Ok(SearchResult(**res.json))
|
|
33
|
-
except Exception as e:
|
|
34
|
-
return res.to_err_result(f"exception: {e}")
|
mm_eth/json_encoder.py
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
from hexbytes import HexBytes
|
|
2
|
-
from mm_std import CustomJSONEncoder
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class EthJsonEncoder(CustomJSONEncoder):
|
|
6
|
-
def default(self, o: object) -> object:
|
|
7
|
-
if isinstance(o, HexBytes):
|
|
8
|
-
return o.hex()
|
|
9
|
-
return super().default(o)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def json_default(o: object) -> str:
|
|
13
|
-
if isinstance(o, HexBytes):
|
|
14
|
-
return o.to_0x_hex()
|
|
15
|
-
raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
|
mm_eth/py.typed
DELETED
|
File without changes
|
mm_eth/rpc_async.py
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import string
|
|
3
|
-
from collections.abc import Sequence
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
import ens.utils
|
|
7
|
-
import eth_utils
|
|
8
|
-
import websockets
|
|
9
|
-
from mm_std import DataResult, http_request
|
|
10
|
-
|
|
11
|
-
DEFAULT_TIMEOUT = 7.0
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
async def rpc_call(
|
|
15
|
-
node: str,
|
|
16
|
-
method: str,
|
|
17
|
-
params: Sequence[object],
|
|
18
|
-
timeout: float,
|
|
19
|
-
proxy: str | None,
|
|
20
|
-
id_: int = 1,
|
|
21
|
-
) -> DataResult[Any]:
|
|
22
|
-
data = {"jsonrpc": "2.0", "method": method, "params": params, "id": id_}
|
|
23
|
-
if node.startswith("http"):
|
|
24
|
-
return await _http_call(node, data, timeout, proxy)
|
|
25
|
-
return await _ws_call(node, data, timeout)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
async def _http_call(node: str, data: dict[str, object], timeout: float, proxy: str | None) -> DataResult[Any]:
|
|
29
|
-
res = await http_request(node, method="POST", proxy=proxy, timeout=timeout, json=data)
|
|
30
|
-
if res.is_error():
|
|
31
|
-
return res.to_data_result_err()
|
|
32
|
-
try:
|
|
33
|
-
parsed_body = res.parse_json_body()
|
|
34
|
-
err = parsed_body.get("error", {}).get("message", "")
|
|
35
|
-
if err:
|
|
36
|
-
return res.to_data_result_err(f"service_error: {err}")
|
|
37
|
-
if "result" in parsed_body:
|
|
38
|
-
return res.to_data_result_ok(parsed_body["result"])
|
|
39
|
-
return res.to_data_result_err("unknown_response")
|
|
40
|
-
except Exception as err:
|
|
41
|
-
return res.to_data_result_err(f"exception: {err}")
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
async def _ws_call(node: str, data: dict[str, object], timeout: float) -> DataResult[Any]:
|
|
45
|
-
try:
|
|
46
|
-
async with websockets.connect(node, timeout=timeout) as ws:
|
|
47
|
-
await ws.send(json.dumps(data))
|
|
48
|
-
response = json.loads(await ws.recv())
|
|
49
|
-
|
|
50
|
-
err = response.get("error", {}).get("message", "")
|
|
51
|
-
if err:
|
|
52
|
-
return DataResult.err(f"service_error: {err}", response)
|
|
53
|
-
if "result" in response:
|
|
54
|
-
return DataResult.ok(response["result"], response)
|
|
55
|
-
return DataResult.err("unknown_response", response)
|
|
56
|
-
except TimeoutError:
|
|
57
|
-
return DataResult.err("timeout")
|
|
58
|
-
except Exception as err:
|
|
59
|
-
return DataResult.exception(err)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
async def eth_block_number(node: str, timeout: float = DEFAULT_TIMEOUT, proxy: str | None = None) -> DataResult[int]:
|
|
63
|
-
return (await rpc_call(node, "eth_blockNumber", [], timeout, proxy)).map(_hex_str_to_int)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
async def eth_get_balance(node: str, address: str, timeout: float = DEFAULT_TIMEOUT, proxy: str | None = None) -> DataResult[int]:
|
|
67
|
-
return (await rpc_call(node, "eth_getBalance", [address, "latest"], timeout, proxy)).map(_hex_str_to_int)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
async def erc20_balance(
|
|
71
|
-
node: str, token_address: str, user_address: str, timeout: float = DEFAULT_TIMEOUT, proxy: str | None = None
|
|
72
|
-
) -> DataResult[int]:
|
|
73
|
-
data = "0x70a08231000000000000000000000000" + user_address[2:]
|
|
74
|
-
params = [{"to": token_address, "data": data}, "latest"]
|
|
75
|
-
return (await rpc_call(node, "eth_call", params, timeout, proxy)).map(_hex_str_to_int)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
async def erc20_name(
|
|
79
|
-
node: str, token_address: str, timeout: float = DEFAULT_TIMEOUT, proxy: str | None = None
|
|
80
|
-
) -> DataResult[str]:
|
|
81
|
-
params = [{"to": token_address, "data": "0x06fdde03"}, "latest"]
|
|
82
|
-
return (await rpc_call(node, "eth_call", params, timeout, proxy)).map(_normalize_str)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
async def erc20_symbol(
|
|
86
|
-
node: str, token_address: str, timeout: float = DEFAULT_TIMEOUT, proxy: str | None = None
|
|
87
|
-
) -> DataResult[str]:
|
|
88
|
-
params = [{"to": token_address, "data": "0x95d89b41"}, "latest"]
|
|
89
|
-
return (await rpc_call(node, "eth_call", params, timeout, proxy)).map(_normalize_str)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
async def erc20_decimals(
|
|
93
|
-
node: str, token_address: str, timeout: float = DEFAULT_TIMEOUT, proxy: str | None = None
|
|
94
|
-
) -> DataResult[int]:
|
|
95
|
-
params = [{"to": token_address, "data": "0x313ce567"}, "latest"]
|
|
96
|
-
res = await rpc_call(node, "eth_call", params, timeout, proxy)
|
|
97
|
-
if res.is_err():
|
|
98
|
-
return res
|
|
99
|
-
try:
|
|
100
|
-
if res.unwrap() == "0x":
|
|
101
|
-
return DataResult.err("no_decimals", res.data)
|
|
102
|
-
value = res.unwrap()
|
|
103
|
-
result = eth_utils.to_int(hexstr=value[0:66]) if len(value) > 66 else eth_utils.to_int(hexstr=value)
|
|
104
|
-
return DataResult.ok(result, res.data)
|
|
105
|
-
except Exception as err:
|
|
106
|
-
return DataResult.exception(err, data=res.data)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
ENS_REGISTRY_ADDRESS: str = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
|
|
110
|
-
FUNC_SELECTOR_RESOLVER: str = "0x0178b8bf" # resolver(bytes32)
|
|
111
|
-
FUNC_SELECTOR_NAME: str = "0x691f3431" # name(bytes32)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
async def ens_name(node: str, address: str, timeout: float = DEFAULT_TIMEOUT, proxy: str | None = None) -> DataResult[str | None]:
|
|
115
|
-
checksum_addr = eth_utils.to_checksum_address(address)
|
|
116
|
-
reverse_name = checksum_addr.lower()[2:] + ".addr.reverse"
|
|
117
|
-
name_hash_hex = ens.utils.normal_name_to_hash(reverse_name).hex()
|
|
118
|
-
|
|
119
|
-
resolver_data = FUNC_SELECTOR_RESOLVER + name_hash_hex
|
|
120
|
-
|
|
121
|
-
resolver_params = [{"to": ENS_REGISTRY_ADDRESS, "data": resolver_data}, "latest"]
|
|
122
|
-
|
|
123
|
-
resolver_res = await rpc_call(node, method="eth_call", params=resolver_params, timeout=timeout, proxy=proxy)
|
|
124
|
-
if resolver_res.is_err():
|
|
125
|
-
return resolver_res
|
|
126
|
-
|
|
127
|
-
if resolver_res.is_ok() and len(resolver_res.unwrap()) != 66:
|
|
128
|
-
return DataResult.ok(None, {"revolver_response": resolver_res.dict()})
|
|
129
|
-
|
|
130
|
-
resolver_address = eth_utils.to_checksum_address("0x" + resolver_res.unwrap()[-40:])
|
|
131
|
-
|
|
132
|
-
name_data: str = FUNC_SELECTOR_NAME + name_hash_hex
|
|
133
|
-
name_params = [{"to": resolver_address, "data": name_data}, "latest"]
|
|
134
|
-
|
|
135
|
-
name_res: DataResult[str] = await rpc_call(node, "eth_call", name_params, timeout=timeout, proxy=proxy)
|
|
136
|
-
|
|
137
|
-
if name_res.is_err():
|
|
138
|
-
return DataResult.err(name_res.unwrap_err(), {"resolver_response": resolver_res.dict(), "name_response": name_res.dict()})
|
|
139
|
-
|
|
140
|
-
if name_res.unwrap() == "0x":
|
|
141
|
-
return DataResult.ok(None, {"resolver_response": resolver_res.dict(), "name_response": name_res.dict()})
|
|
142
|
-
|
|
143
|
-
try:
|
|
144
|
-
hex_data = name_res.unwrap()
|
|
145
|
-
length_hex = hex_data[66:130]
|
|
146
|
-
str_len = int(length_hex, 16) * 2
|
|
147
|
-
name_hex = hex_data[130 : 130 + str_len]
|
|
148
|
-
return DataResult.ok(
|
|
149
|
-
bytes.fromhex(name_hex).decode("utf-8"), {"resolver_response": resolver_res.dict(), "name_response": name_res.dict()}
|
|
150
|
-
)
|
|
151
|
-
except Exception as err:
|
|
152
|
-
return DataResult.exception(err, data={"resolver_response": resolver_res.dict(), "name_response": name_res.dict()})
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
def _hex_str_to_int(value: str) -> int:
|
|
156
|
-
return int(value, 16)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def _normalize_str(value: str) -> str:
|
|
160
|
-
return "".join(filter(lambda x: x in string.printable, eth_utils.to_text(hexstr=value))).strip()
|
mm_eth/vault.py
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from mm_std import Err, Ok, Result, hr
|
|
2
|
-
|
|
3
|
-
from mm_eth.account import private_to_address
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def read_keys_from_vault(keys_url: str, token: str) -> Result[dict[str, str]]:
|
|
7
|
-
data = None
|
|
8
|
-
try:
|
|
9
|
-
# keys_url example, https://vault.site.com:8200/v1/kv/keys1
|
|
10
|
-
res = hr(keys_url, headers={"X-Vault-Token": token})
|
|
11
|
-
data = res.json
|
|
12
|
-
return Ok(res.json["data"], data=data)
|
|
13
|
-
except Exception as e:
|
|
14
|
-
return Err(f"exception: {e}", data=data)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def set_keys_from_vault(keys_url: str, token: str, private_keys: list[str], verify_tls: bool = True) -> Result[bool]:
|
|
18
|
-
"""It works with KV version=1 only!!!"""
|
|
19
|
-
# TODO: check that keys_url is kv1 version and error if it's kv2
|
|
20
|
-
data = None
|
|
21
|
-
try:
|
|
22
|
-
# keys_url example, https://vault.site.com:8200/v1/kv/keys1
|
|
23
|
-
keys: dict[str, str] = {}
|
|
24
|
-
for private_key in private_keys:
|
|
25
|
-
address = private_to_address(private_key)
|
|
26
|
-
if address is None:
|
|
27
|
-
return Err("wrong private key", data=data)
|
|
28
|
-
keys[address] = private_key
|
|
29
|
-
|
|
30
|
-
res = hr(keys_url, method="post", headers={"X-Vault-Token": token}, params=keys, verify=verify_tls)
|
|
31
|
-
data = res.json
|
|
32
|
-
if res.code == 204:
|
|
33
|
-
return Ok(res.code == 204, data=data)
|
|
34
|
-
if res.code == 403:
|
|
35
|
-
return Err("permission denied", data=data)
|
|
36
|
-
return Err(res.error or "error", data=data)
|
|
37
|
-
except Exception as e:
|
|
38
|
-
return Err(f"exception: {e}", data=data)
|
mm_eth-0.5.17.dist-info/RECORD
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
mm_eth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
mm_eth/abi.py,sha256=Qf-QOsR9QexyQM9XWKNeTMkRarIL3XQJbaDbJ8ifMrw,4856
|
|
3
|
-
mm_eth/account.py,sha256=k0MNMatBe0zo1iKZiB_Tq6zyEIo22IncD6ewNUJp3dY,2075
|
|
4
|
-
mm_eth/anvil.py,sha256=98RCfI7dEpxFBTV6UErYvubWVP3n0ctUFn1--4kZ84U,1603
|
|
5
|
-
mm_eth/async_rpc.py,sha256=zjPcOoC-NTjMxbWu6cRQn-knHEQ75ZO5L-KZoaiwUJ0,2981
|
|
6
|
-
mm_eth/constants.py,sha256=Cy_G-IleBH4gAZ4ok8AGHHlqmdW0ZM7ZldyVpzAfWLs,54
|
|
7
|
-
mm_eth/deploy.py,sha256=SB3ruY808_5UnG8kHR34uVP66P3zOWZu0ImKD7UUv2s,691
|
|
8
|
-
mm_eth/ens.py,sha256=lH3TZABYFBiEEtWlyxCqrcYC-nZQsI_1wSCMzBzHPrM,3860
|
|
9
|
-
mm_eth/erc20.py,sha256=LZmTkjmXpN7sUhOtSLiTqb8Ubet9C9q32EQwTS78OqE,7378
|
|
10
|
-
mm_eth/ethernodes.py,sha256=V4VVbC6Nr9jhwT7blxtLugXC5KfXqE8n-kP0VvGHbqo,3070
|
|
11
|
-
mm_eth/json_encoder.py,sha256=S4oD-qfTVztMb4sRpY1puhBQwOBofTyQXWszmdXk4og,433
|
|
12
|
-
mm_eth/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
mm_eth/rpc.py,sha256=k0eHxo_Dp6G0fHQ_rD-QbwOJz5ngC6kxBjl5NEHnyw8,13832
|
|
14
|
-
mm_eth/rpc_async.py,sha256=PU19G9_tJSa4vfmaFUowjRCRy8dvfyJh9Lpkpe4FR4k,6462
|
|
15
|
-
mm_eth/solc.py,sha256=dYRvT8PjZlLDZhNsc_-0790Eug_ZwU2G-iBfIdGj6wQ,1071
|
|
16
|
-
mm_eth/tx.py,sha256=efSoMCoWkenbGdHo1_LX66_Edz1HvED5-J_i3wrHwMw,4051
|
|
17
|
-
mm_eth/utils.py,sha256=FytG3U6h80mnUaKP8W2mPZ77EuOp4U7pVbPuoKI3wW4,8215
|
|
18
|
-
mm_eth/vault.py,sha256=h8NyiOQh5YFskh1lZA3KyvnJUnxl9769ME2ChplG0CM,1477
|
|
19
|
-
mm_eth/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
mm_eth/cli/calcs.py,sha256=cLFTYNAN-I53tUiSg-zFfVr2afjIZPftDDjHj16FBz0,1068
|
|
21
|
-
mm_eth/cli/cli.py,sha256=yunHFL67-cEBXfogVgYzqLIhtjPIlyHtC6UNZdNg9Mw,9332
|
|
22
|
-
mm_eth/cli/cli_utils.py,sha256=6TIGGEh3zGPTJQ6DKeOdz8JBg0XdL5gWt6WwppRThyk,1814
|
|
23
|
-
mm_eth/cli/print_helpers.py,sha256=yOiOFjTKloumwf07AqNIHQswUo8t0yuT9bpeGBGl60Q,1470
|
|
24
|
-
mm_eth/cli/rpc_helpers.py,sha256=FMV-QVNM3v9X8H_-DP0hjNRqmm7KOnfzkw9bP17Qbz0,4499
|
|
25
|
-
mm_eth/cli/validators.py,sha256=KIAQUohl4_KKDvynbeqIeywtNnMWhTKYlnTdaxcjn6U,1690
|
|
26
|
-
mm_eth/cli/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
-
mm_eth/cli/cmd/balance_cmd.py,sha256=aWJFteLnkgw9RqDsIV34a5_rh00fmFR9SXSJUtVyP3U,1856
|
|
28
|
-
mm_eth/cli/cmd/balances_cmd.py,sha256=4UiWSNH9OCnkvcMGPTygEss8119do-rfG7QtsNMfWZs,4197
|
|
29
|
-
mm_eth/cli/cmd/call_contract_cmd.py,sha256=RbBPvyUEQ45hQINYDKkx1yWhPygdymVKlRL26xI31uk,1264
|
|
30
|
-
mm_eth/cli/cmd/deploy_cmd.py,sha256=0oBp_RZw_DIEtBFRc6QKdAw5oouwwpDIdeXvuyP9xdU,1272
|
|
31
|
-
mm_eth/cli/cmd/encode_input_data_cmd.py,sha256=9UQ1MKPEFQJ8j_COsP3KGKhwOf9tT3feBezI8vvxTlw,267
|
|
32
|
-
mm_eth/cli/cmd/example_cmd.py,sha256=QLipRKoR6T1my9qnMpzk2uPtj61mbwcuCMNFmyqRgJo,276
|
|
33
|
-
mm_eth/cli/cmd/node_cmd.py,sha256=tFk4YECUUb-Rt_YIfMc_IGlqzxQjpsZmjp7OK2v0crI,2502
|
|
34
|
-
mm_eth/cli/cmd/rpc_cmd.py,sha256=02q82YqgbPezfEBmV_QBCIeNReE7ktkPych8Xr9ann8,2186
|
|
35
|
-
mm_eth/cli/cmd/solc_cmd.py,sha256=tBpeMdPfGs2iQIMaIJAAhMh1a3KyXHwyninfXPVpsgs,677
|
|
36
|
-
mm_eth/cli/cmd/token_cmd.py,sha256=4y6ZQpLOJ33_iNuKpm9tZXh4RntWhmPUcizgaNNBzaw,1102
|
|
37
|
-
mm_eth/cli/cmd/transfer_cmd.py,sha256=5Twb94GSZ0NiOj1PgY_1TdIHDoGN3Eq6qACelnCQPWQ,13298
|
|
38
|
-
mm_eth/cli/cmd/tx_cmd.py,sha256=PIenXYTT60Z2fqsivpzybCLI2Z_tlcz-asm3B0JLHgI,517
|
|
39
|
-
mm_eth/cli/cmd/vault_cmd.py,sha256=MOM1CILIaaqown1I-Fgo22ckqIMLtFt8t2D3fWNp798,606
|
|
40
|
-
mm_eth/cli/cmd/wallet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
-
mm_eth/cli/cmd/wallet/mnemonic_cmd.py,sha256=xE-5Ux9BdYsTZYBy0dMn9jupGhW4ced-AgYscy_wU_4,1007
|
|
42
|
-
mm_eth/cli/cmd/wallet/private_key_cmd.py,sha256=Fv_2OLog1h32pIP7PJITwl_pHdy3BXvaDRcXZsxY1xo,241
|
|
43
|
-
mm_eth/cli/examples/balances.toml,sha256=i_ALpiEcf8-0TFiUg1cgJhxxfHYeBl9x0b3tnUWjswU,421
|
|
44
|
-
mm_eth/cli/examples/call_contract.toml,sha256=ZQWK-409V_vLIZ2bsRD5RCWPPzShPz2KJTTRQY4YaGw,248
|
|
45
|
-
mm_eth/cli/examples/transfer.toml,sha256=8mWuphDquoSDJw-hb9VJqtonjmv3kJ5Ip8jJ4t5YnfM,1810
|
|
46
|
-
mm_eth-0.5.17.dist-info/METADATA,sha256=lWZylDIZ_1vZg3w6UCDqE7Dx-vvxRrJSPCnYUD9np2o,247
|
|
47
|
-
mm_eth-0.5.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
48
|
-
mm_eth-0.5.17.dist-info/entry_points.txt,sha256=aGhpsozl8NIrkuUcX5fSgURCcDhr3ShUdeTSIrJq4oc,46
|
|
49
|
-
mm_eth-0.5.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|