mm-eth 0.1.5__py3-none-any.whl → 0.2.1__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 +3 -3
- mm_eth/account.py +1 -1
- mm_eth/anvil.py +2 -2
- mm_eth/erc20.py +8 -7
- mm_eth/rpc.py +8 -8
- mm_eth/solc.py +2 -3
- mm_eth/tx.py +3 -5
- mm_eth/utils.py +11 -16
- mm_eth/vault.py +5 -5
- mm_eth-0.2.1.dist-info/METADATA +7 -0
- mm_eth-0.2.1.dist-info/RECORD +18 -0
- {mm_eth-0.1.5.dist-info → mm_eth-0.2.1.dist-info}/WHEEL +1 -1
- mm_eth/abi/zksync.json +0 -2092
- mm_eth/cli/__init__.py +0 -0
- mm_eth/cli/calcs.py +0 -88
- mm_eth/cli/cli.py +0 -237
- mm_eth/cli/cli_helpers.py +0 -195
- mm_eth/cli/cli_utils.py +0 -148
- mm_eth/cli/cmd/__init__.py +0 -0
- mm_eth/cli/cmd/balance_cmd.py +0 -59
- mm_eth/cli/cmd/balances_cmd.py +0 -121
- mm_eth/cli/cmd/call_contract_cmd.py +0 -44
- mm_eth/cli/cmd/config_example_cmd.py +0 -9
- mm_eth/cli/cmd/deploy_cmd.py +0 -41
- mm_eth/cli/cmd/encode_input_data_cmd.py +0 -10
- mm_eth/cli/cmd/mnemonic_cmd.py +0 -27
- mm_eth/cli/cmd/node_cmd.py +0 -47
- mm_eth/cli/cmd/private_key_cmd.py +0 -10
- mm_eth/cli/cmd/rpc_cmd.py +0 -81
- mm_eth/cli/cmd/send_contract_cmd.py +0 -247
- mm_eth/cli/cmd/solc_cmd.py +0 -25
- mm_eth/cli/cmd/token_cmd.py +0 -29
- mm_eth/cli/cmd/transfer_erc20_cmd.py +0 -275
- mm_eth/cli/cmd/transfer_eth_cmd.py +0 -252
- mm_eth/cli/cmd/vault_cmd.py +0 -16
- mm_eth/cli/config_examples/balances.yml +0 -15
- mm_eth/cli/config_examples/call_contract.yml +0 -5
- mm_eth/cli/config_examples/transfer_erc20.yml +0 -26
- mm_eth/cli/config_examples/transfer_eth.yml +0 -24
- mm_eth/cli/validators.py +0 -84
- mm_eth/services/__init__.py +0 -0
- mm_eth/zksync.py +0 -203
- mm_eth-0.1.5.dist-info/METADATA +0 -9
- mm_eth-0.1.5.dist-info/RECORD +0 -49
- mm_eth-0.1.5.dist-info/entry_points.txt +0 -2
mm_eth/abi.py
CHANGED
|
@@ -64,7 +64,7 @@ def decode_function_input(contract_abi: ABI, tx_input: str) -> FunctionInput:
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
def get_function_abi(contr_abi: ABI, fn_name: str) -> ABIFunction:
|
|
67
|
-
abi = pydash.find(contr_abi, lambda x: x.get("name", None) == fn_name and x.get("type", None) == "function") # type:ignore
|
|
67
|
+
abi = pydash.find(contr_abi, lambda x: x.get("name", None) == fn_name and x.get("type", None) == "function") # type: ignore[call-overload, attr-defined]
|
|
68
68
|
if not abi:
|
|
69
69
|
raise ValueError("can't find abi for function: " + fn_name)
|
|
70
70
|
return cast(ABIFunction, abi)
|
|
@@ -122,8 +122,8 @@ def parse_function_signatures(contract_abi: ABI) -> dict[str, str]:
|
|
|
122
122
|
result: dict[str, str] = {}
|
|
123
123
|
for item in contract_abi:
|
|
124
124
|
if item.get("type", None) == "function":
|
|
125
|
-
function_name = item["name"] # type: ignore
|
|
126
|
-
types = ",".join([i["type"] for i in item["inputs"]]) # type: ignore
|
|
125
|
+
function_name = item["name"] # type: ignore[typeddict-item]
|
|
126
|
+
types = ",".join([i["type"] for i in item["inputs"]]) # type: ignore[typeddict-item]
|
|
127
127
|
function_name_and_types = f"{function_name}({types})"
|
|
128
128
|
result[function_name_and_types] = encode_function_signature(function_name_and_types)
|
|
129
129
|
return result
|
mm_eth/account.py
CHANGED
|
@@ -67,6 +67,6 @@ def create_private_keys_dict(private_keys: list[str]) -> dict[str, str]: # addr
|
|
|
67
67
|
def is_private_key(private_key: str) -> bool:
|
|
68
68
|
try:
|
|
69
69
|
key_api.PrivateKey(decode_hex(private_key)).public_key.to_address()
|
|
70
|
-
return True
|
|
70
|
+
return True # noqa: TRY300
|
|
71
71
|
except Exception:
|
|
72
72
|
return False
|
mm_eth/anvil.py
CHANGED
|
@@ -10,7 +10,7 @@ from mm_eth import account, rpc
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class Anvil:
|
|
13
|
-
def __init__(self, *, chain_id: int, port: int, mnemonic: str):
|
|
13
|
+
def __init__(self, *, chain_id: int, port: int, mnemonic: str) -> None:
|
|
14
14
|
self.chain_id = chain_id
|
|
15
15
|
self.port = port
|
|
16
16
|
self.mnemonic = mnemonic
|
|
@@ -18,7 +18,7 @@ class Anvil:
|
|
|
18
18
|
|
|
19
19
|
def start_process(self) -> None:
|
|
20
20
|
cmd = f"anvil -m '{self.mnemonic}' -p {self.port} --chain-id {self.chain_id}"
|
|
21
|
-
self.process = Popen(cmd, shell=True) # nosec
|
|
21
|
+
self.process = Popen(cmd, shell=True) # noqa: S602 # nosec
|
|
22
22
|
time.sleep(3)
|
|
23
23
|
|
|
24
24
|
def stop(self) -> None:
|
mm_eth/erc20.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import string
|
|
4
|
-
from collections.abc import Sequence
|
|
5
4
|
from dataclasses import dataclass
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
import eth_abi
|
|
8
8
|
import eth_utils
|
|
@@ -11,11 +11,15 @@ from eth_utils import to_checksum_address, to_hex
|
|
|
11
11
|
from mm_std import Err, Ok, Result
|
|
12
12
|
|
|
13
13
|
from mm_eth import rpc
|
|
14
|
-
from mm_eth.rpc import Log
|
|
15
14
|
from mm_eth.tx import SignedTx, sign_legacy_tx, sign_tx
|
|
16
|
-
from mm_eth.types import Nodes, Proxies
|
|
17
15
|
from mm_eth.utils import hex_str_to_int, hex_to_bytes, log_topic_to_address
|
|
18
16
|
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Sequence
|
|
19
|
+
|
|
20
|
+
from mm_eth.rpc import Log
|
|
21
|
+
from mm_eth.types import Nodes, Proxies
|
|
22
|
+
|
|
19
23
|
TRANSFER_METHOD = "0xa9059cbb"
|
|
20
24
|
TRANSFER_TOPIC = HexStr("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")
|
|
21
25
|
|
|
@@ -108,10 +112,7 @@ def get_decimals(rpc_urls: Nodes, address: str, timeout: int = 10, proxies: Prox
|
|
|
108
112
|
if res.ok == "0x":
|
|
109
113
|
return Err("no_decimals", data=res.data)
|
|
110
114
|
|
|
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
|
+
result = eth_utils.to_int(hexstr=res.ok[0:66]) if len(res.ok) > 66 else eth_utils.to_int(hexstr=res.ok)
|
|
115
116
|
return Ok(result, data=res.data)
|
|
116
117
|
|
|
117
118
|
except Exception as e:
|
mm_eth/rpc.py
CHANGED
|
@@ -2,16 +2,19 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from typing import Any, Literal, cast
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Literal, cast
|
|
6
6
|
|
|
7
7
|
import websocket
|
|
8
8
|
from mm_std import Err, Ok, Result, hr, random_choice
|
|
9
9
|
from pydantic import BaseModel
|
|
10
|
-
from web3.types import BlockIdentifier
|
|
11
10
|
|
|
12
|
-
from mm_eth.types import Nodes, Proxies
|
|
13
11
|
from mm_eth.utils import hex_str_to_int, random_node, random_proxy
|
|
14
12
|
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from web3.types import BlockIdentifier
|
|
15
|
+
|
|
16
|
+
from mm_eth.types import Nodes, Proxies
|
|
17
|
+
|
|
15
18
|
|
|
16
19
|
@dataclass
|
|
17
20
|
class TxReceipt:
|
|
@@ -85,10 +88,7 @@ def rpc_call(
|
|
|
85
88
|
res: Result[Any] = Err("not started yet")
|
|
86
89
|
for _ in range(attempts):
|
|
87
90
|
node = random_node(nodes)
|
|
88
|
-
if node.startswith("http")
|
|
89
|
-
res = _http_call(node, data, timeout, random_proxy(proxies))
|
|
90
|
-
else:
|
|
91
|
-
res = _ws_call(node, data, timeout)
|
|
91
|
+
res = _http_call(node, data, timeout, random_proxy(proxies)) if node.startswith("http") else _ws_call(node, data, timeout)
|
|
92
92
|
if isinstance(res, Ok):
|
|
93
93
|
return res
|
|
94
94
|
return res
|
|
@@ -444,7 +444,7 @@ def eth_syncing(rpc_urls: Nodes, timeout: int = 10, proxies: Proxies = None, att
|
|
|
444
444
|
result[k] = int(v, 16)
|
|
445
445
|
else:
|
|
446
446
|
result[k] = v
|
|
447
|
-
if result.get("currentBlock"
|
|
447
|
+
if result.get("currentBlock") and result.get("highestBlock"):
|
|
448
448
|
result["remaining"] = result["highestBlock"] - result["currentBlock"]
|
|
449
449
|
return Ok(result, res.data)
|
|
450
450
|
|
mm_eth/solc.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import os.path
|
|
2
1
|
import random
|
|
3
2
|
import shutil
|
|
4
3
|
from dataclasses import dataclass
|
|
@@ -15,9 +14,9 @@ class SolcResult:
|
|
|
15
14
|
|
|
16
15
|
def solc(contract_name: str, contract_path: str, tmp_dir: str) -> Result[SolcResult]:
|
|
17
16
|
if tmp_dir.startswith("~"):
|
|
18
|
-
tmp_dir =
|
|
17
|
+
tmp_dir = Path(tmp_dir).expanduser().as_posix()
|
|
19
18
|
if contract_path.startswith("~"):
|
|
20
|
-
contract_path =
|
|
19
|
+
contract_path = Path(contract_path).expanduser().as_posix()
|
|
21
20
|
work_dir = f"{tmp_dir}/solc_{contract_name}_{random.randint(0, 100_000_000)}"
|
|
22
21
|
abi_path = f"{work_dir}/{contract_name}.abi"
|
|
23
22
|
bin_path = f"{work_dir}/{contract_name}.bin"
|
mm_eth/tx.py
CHANGED
|
@@ -44,14 +44,12 @@ class RPLTransaction(rlp.Serializable): # type: ignore[misc]
|
|
|
44
44
|
to: str | None = None,
|
|
45
45
|
) -> RPLTransaction:
|
|
46
46
|
if to:
|
|
47
|
-
to = hex_to_bytes(to) # type:ignore
|
|
47
|
+
to = hex_to_bytes(to) # type: ignore[assignment]
|
|
48
48
|
if data:
|
|
49
|
-
data = hex_to_bytes(data) # type:ignore
|
|
49
|
+
data = hex_to_bytes(data) # type: ignore[assignment]
|
|
50
50
|
if not value:
|
|
51
51
|
value = 0
|
|
52
|
-
|
|
53
|
-
s = int(s, 16) # type:ignore
|
|
54
|
-
return RPLTransaction(nonce, gas_price, gas, to, value, data, v, r, s)
|
|
52
|
+
return RPLTransaction(nonce, gas_price, gas, to, value, data, v, int(r, 16), int(s, 16))
|
|
55
53
|
|
|
56
54
|
|
|
57
55
|
class DecodedRawTx(BaseModel):
|
mm_eth/utils.py
CHANGED
|
@@ -16,23 +16,22 @@ from mm_eth.types import Nodes, Proxies
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
def parse_addresses(data: str) -> list[str]:
|
|
19
|
-
data = data.lower()
|
|
20
19
|
result = []
|
|
21
|
-
for word in data.split():
|
|
20
|
+
for word in data.lower().split():
|
|
22
21
|
if len(word) == 42 and re.match("0x[a-f0-9]{40}", word):
|
|
23
|
-
result.append(word)
|
|
22
|
+
result.append(word) # noqa: PERF401
|
|
24
23
|
return pydash.uniq(result)
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
def to_token_wei(value: str | int, decimals: int) -> int:
|
|
28
27
|
if isinstance(value, int):
|
|
29
28
|
return value
|
|
30
|
-
|
|
29
|
+
if isinstance(value, str):
|
|
31
30
|
value = value.lower().replace(" ", "").strip()
|
|
32
31
|
if value.endswith("t"):
|
|
33
32
|
value = value.replace("t", "")
|
|
34
33
|
return int(Decimal(value) * 10**decimals)
|
|
35
|
-
|
|
34
|
+
if value.isdigit():
|
|
36
35
|
return int(value)
|
|
37
36
|
|
|
38
37
|
raise ValueError("wrong value" + value)
|
|
@@ -41,11 +40,11 @@ def to_token_wei(value: str | int, decimals: int) -> int:
|
|
|
41
40
|
def to_wei(value: str | int | Decimal, decimals: int | None = None) -> Wei:
|
|
42
41
|
if isinstance(value, int):
|
|
43
42
|
return Wei(value)
|
|
44
|
-
|
|
43
|
+
if isinstance(value, Decimal):
|
|
45
44
|
if value != value.to_integral_value():
|
|
46
45
|
raise ValueError(f"value must be integral number: {value}")
|
|
47
46
|
return Wei(int(value))
|
|
48
|
-
|
|
47
|
+
if isinstance(value, str):
|
|
49
48
|
value = value.lower().replace(" ", "").strip()
|
|
50
49
|
if value.endswith("navax"): # https://snowtrace.io/unitconverter
|
|
51
50
|
value = value.replace("navax", "")
|
|
@@ -68,8 +67,7 @@ def to_wei(value: str | int | Decimal, decimals: int | None = None) -> Wei:
|
|
|
68
67
|
return Wei(int(value))
|
|
69
68
|
raise ValueError("wrong value " + value)
|
|
70
69
|
|
|
71
|
-
|
|
72
|
-
raise ValueError(f"value has a wrong type: {type(value)}")
|
|
70
|
+
raise ValueError(f"value has a wrong type: {type(value)}")
|
|
73
71
|
|
|
74
72
|
|
|
75
73
|
def from_wei(
|
|
@@ -125,11 +123,11 @@ def from_token_wei_str(value: int, decimals: int, symbol: str = "", round_ndigit
|
|
|
125
123
|
def to_wei_token(value: str | int | Decimal, symbol: str, decimals: int) -> int:
|
|
126
124
|
if isinstance(value, int):
|
|
127
125
|
return value
|
|
128
|
-
|
|
126
|
+
if isinstance(value, Decimal):
|
|
129
127
|
if value != value.to_integral_value():
|
|
130
128
|
raise ValueError(f"value must be integral number: {value}")
|
|
131
129
|
return int(value)
|
|
132
|
-
|
|
130
|
+
if isinstance(value, str):
|
|
133
131
|
value = value.lower().replace(" ", "").strip()
|
|
134
132
|
if value.isdigit():
|
|
135
133
|
return int(value)
|
|
@@ -138,7 +136,7 @@ def to_wei_token(value: str | int | Decimal, symbol: str, decimals: int) -> int:
|
|
|
138
136
|
except Exception as e:
|
|
139
137
|
raise ValueError from e
|
|
140
138
|
else:
|
|
141
|
-
raise
|
|
139
|
+
raise TypeError(f"value has a wrong type: {type(value)}")
|
|
142
140
|
|
|
143
141
|
|
|
144
142
|
def to_checksum_address(address: str) -> str:
|
|
@@ -185,10 +183,7 @@ def truncate_hex_str(hex_str: str, digits: int = 4, replace_str: str = "...") ->
|
|
|
185
183
|
|
|
186
184
|
|
|
187
185
|
def log_topic_to_address(topic: HexBytes | str) -> str:
|
|
188
|
-
if isinstance(topic, HexBytes):
|
|
189
|
-
result = topic.hex()[-40:]
|
|
190
|
-
else:
|
|
191
|
-
result = topic[-40:]
|
|
186
|
+
result = topic.hex()[-40:] if isinstance(topic, HexBytes) else topic[-40:]
|
|
192
187
|
if not result.startswith("0x"):
|
|
193
188
|
result = f"0x{result}"
|
|
194
189
|
return result
|
mm_eth/vault.py
CHANGED
|
@@ -15,7 +15,8 @@ def read_keys_from_vault(keys_url: str, token: str) -> Result[dict[str, str]]:
|
|
|
15
15
|
|
|
16
16
|
|
|
17
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!!!"""
|
|
18
|
+
"""It works with KV version=1 only!!!"""
|
|
19
|
+
# TODO: check that keys_url is kv1 version and error if it's kv2
|
|
19
20
|
data = None
|
|
20
21
|
try:
|
|
21
22
|
# keys_url example, https://vault.site.com:8200/v1/kv/keys1
|
|
@@ -23,16 +24,15 @@ def set_keys_from_vault(keys_url: str, token: str, private_keys: list[str], veri
|
|
|
23
24
|
for private_key in private_keys:
|
|
24
25
|
address = private_to_address(private_key)
|
|
25
26
|
if address is None:
|
|
26
|
-
|
|
27
|
+
return Err("wrong private key", data=data)
|
|
27
28
|
keys[address] = private_key
|
|
28
29
|
|
|
29
30
|
res = hr(keys_url, method="post", headers={"X-Vault-Token": token}, params=keys, verify=verify_tls)
|
|
30
31
|
data = res.json
|
|
31
32
|
if res.code == 204:
|
|
32
33
|
return Ok(res.code == 204, data=data)
|
|
33
|
-
|
|
34
|
+
if res.code == 403:
|
|
34
35
|
return Err("permission denied", data=data)
|
|
35
|
-
|
|
36
|
-
return Err(res.error or "error", data=data)
|
|
36
|
+
return Err(res.error or "error", data=data)
|
|
37
37
|
except Exception as e:
|
|
38
38
|
return Err(f"exception: {e}", data=data)
|
|
@@ -0,0 +1,18 @@
|
|
|
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=IsajQ7156-CxqRFt2ntv_3nJKwi6tCZz0TmtjUCgmIg,2038
|
|
4
|
+
mm_eth/anvil.py,sha256=98RCfI7dEpxFBTV6UErYvubWVP3n0ctUFn1--4kZ84U,1603
|
|
5
|
+
mm_eth/deploy.py,sha256=SB3ruY808_5UnG8kHR34uVP66P3zOWZu0ImKD7UUv2s,691
|
|
6
|
+
mm_eth/ens.py,sha256=WMxqC1v3zwDDuLH_oWekm22qrNYxCNcvZumQMT7SYds,623
|
|
7
|
+
mm_eth/erc20.py,sha256=m0_yYzPYgOBx0oh4V330XGjRQBjMd5XlWhRL5qChvQE,6866
|
|
8
|
+
mm_eth/ethernodes.py,sha256=9y_poTmFUj6cnWaT9mtfc6S9lAfVXTwLGRqxMQ8hT0Y,3080
|
|
9
|
+
mm_eth/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
mm_eth/rpc.py,sha256=F0jBzYw4wkh9LKwynXrzyhA08r5JTGrYAbwTH6C2Z7w,13872
|
|
11
|
+
mm_eth/solc.py,sha256=dYRvT8PjZlLDZhNsc_-0790Eug_ZwU2G-iBfIdGj6wQ,1071
|
|
12
|
+
mm_eth/tx.py,sha256=J5Wky8rmV6NlMFmAYF4997PscRYlUdkYFpEldH3BCNg,4014
|
|
13
|
+
mm_eth/types.py,sha256=vXXP5Dc72BpHv5tsyws0KDZebG1W1-5HH0UjL7N2Mgc,113
|
|
14
|
+
mm_eth/utils.py,sha256=NICgROKU5ZTrkY6nlSDLHjbbpYYswLoofyc3ozPdwBs,7804
|
|
15
|
+
mm_eth/vault.py,sha256=h8NyiOQh5YFskh1lZA3KyvnJUnxl9769ME2ChplG0CM,1477
|
|
16
|
+
mm_eth-0.2.1.dist-info/METADATA,sha256=VZ2A_K7tjKAWxIzkq6K3zk3PQv6hXDRs89oBvGgApB0,170
|
|
17
|
+
mm_eth-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
18
|
+
mm_eth-0.2.1.dist-info/RECORD,,
|