mm-eth 0.2.0__py3-none-any.whl → 0.2.2__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/cli/__init__.py +0 -0
- mm_eth/cli/calcs.py +112 -0
- mm_eth/cli/cli.py +237 -0
- mm_eth/cli/cli_utils.py +105 -0
- mm_eth/cli/cmd/__init__.py +0 -0
- mm_eth/cli/cmd/balance_cmd.py +51 -0
- mm_eth/cli/cmd/balances_cmd.py +124 -0
- mm_eth/cli/cmd/call_contract_cmd.py +43 -0
- mm_eth/cli/cmd/config_example_cmd.py +9 -0
- mm_eth/cli/cmd/deploy_cmd.py +43 -0
- mm_eth/cli/cmd/encode_input_data_cmd.py +10 -0
- mm_eth/cli/cmd/mnemonic_cmd.py +26 -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 +78 -0
- mm_eth/cli/cmd/send_contract_cmd.py +251 -0
- mm_eth/cli/cmd/solc_cmd.py +24 -0
- mm_eth/cli/cmd/token_cmd.py +29 -0
- mm_eth/cli/cmd/transfer_erc20_cmd.py +277 -0
- mm_eth/cli/cmd/transfer_eth_cmd.py +254 -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/print_helpers.py +37 -0
- mm_eth/cli/rpc_helpers.py +140 -0
- mm_eth/cli/validators.py +85 -0
- 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.2.dist-info/METADATA +9 -0
- mm_eth-0.2.2.dist-info/RECORD +47 -0
- mm_eth-0.2.2.dist-info/entry_points.txt +2 -0
- mm_eth-0.2.0.dist-info/METADATA +0 -7
- mm_eth-0.2.0.dist-info/RECORD +0 -18
- {mm_eth-0.2.0.dist-info → mm_eth-0.2.2.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from loguru import logger
|
|
2
|
+
from mm_std import Err, fatal
|
|
3
|
+
|
|
4
|
+
from mm_eth import erc20, rpc
|
|
5
|
+
from mm_eth.utils import from_wei_str
|
|
6
|
+
|
|
7
|
+
from . import calcs
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_nonce(nodes: list[str] | str, address: str, log_prefix: str | None = None) -> int | None:
|
|
11
|
+
res = rpc.eth_get_transaction_count(nodes, address, attempts=5)
|
|
12
|
+
prefix = log_prefix or address
|
|
13
|
+
logger.debug(f"{prefix}: nonce={res.ok_or_err()}")
|
|
14
|
+
if isinstance(res, Err):
|
|
15
|
+
logger.info(f"{prefix}: nonce error, {res.err}")
|
|
16
|
+
return None
|
|
17
|
+
return res.ok
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def check_nodes_for_chain_id(nodes: list[str], chain_id: int) -> None:
|
|
21
|
+
for node in nodes:
|
|
22
|
+
res = rpc.eth_chain_id(node, timeout=7)
|
|
23
|
+
if isinstance(res, Err):
|
|
24
|
+
fatal(f"can't get chain_id for {node}, error={res.err}")
|
|
25
|
+
if res.ok != chain_id:
|
|
26
|
+
fatal(f"node {node} has a wrong chain_id: {res.ok}")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_base_fee(nodes: list[str], log_prefix: str | None = None) -> int | None:
|
|
30
|
+
res = rpc.get_base_fee_per_gas(nodes)
|
|
31
|
+
prefix = _get_prefix(log_prefix)
|
|
32
|
+
logger.debug(f"{prefix}base_fee={res.ok_or_err()}")
|
|
33
|
+
if isinstance(res, Err):
|
|
34
|
+
logger.info(f"{prefix}base_fee error, {res.err}")
|
|
35
|
+
return None
|
|
36
|
+
return res.ok
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def calc_max_fee_per_gas(nodes: list[str], max_fee_per_gas: str, log_prefix: str | None = None) -> int | None:
|
|
40
|
+
if "base" in max_fee_per_gas.lower():
|
|
41
|
+
base_fee = get_base_fee(nodes, log_prefix)
|
|
42
|
+
if base_fee is None:
|
|
43
|
+
return None
|
|
44
|
+
return calcs.calc_var_wei_value(max_fee_per_gas, var_name="base", var_value=base_fee)
|
|
45
|
+
return calcs.calc_var_wei_value(max_fee_per_gas)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def is_max_fee_per_gas_limit_exceeded(
|
|
49
|
+
max_fee_per_gas: int,
|
|
50
|
+
max_fee_per_gas_limit: str | None,
|
|
51
|
+
log_prefix: str | None = None,
|
|
52
|
+
) -> bool:
|
|
53
|
+
if max_fee_per_gas_limit is None:
|
|
54
|
+
return False
|
|
55
|
+
max_fee_per_gas_limit_value = calcs.calc_var_wei_value(max_fee_per_gas_limit)
|
|
56
|
+
if max_fee_per_gas > max_fee_per_gas_limit_value:
|
|
57
|
+
prefix = _get_prefix(log_prefix)
|
|
58
|
+
logger.info(
|
|
59
|
+
"{}max_fee_per_gas_limit is exeeded, max_fee_per_gas={}, max_fee_per_gas_limit={}",
|
|
60
|
+
prefix,
|
|
61
|
+
from_wei_str(max_fee_per_gas, "gwei"),
|
|
62
|
+
from_wei_str(max_fee_per_gas_limit_value, "gwei"),
|
|
63
|
+
)
|
|
64
|
+
return True
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def calc_gas(
|
|
69
|
+
*,
|
|
70
|
+
nodes: list[str],
|
|
71
|
+
gas: str,
|
|
72
|
+
from_address: str,
|
|
73
|
+
to_address: str,
|
|
74
|
+
value: int | None = None,
|
|
75
|
+
data: str | None = None,
|
|
76
|
+
log_prefix: str | None = None,
|
|
77
|
+
) -> int | None:
|
|
78
|
+
estimate_value = None
|
|
79
|
+
if "estimate" in gas.lower():
|
|
80
|
+
prefix = _get_prefix(log_prefix)
|
|
81
|
+
res = rpc.eth_estimate_gas(nodes, from_address, to_address, data=data, value=value, attempts=5)
|
|
82
|
+
logger.debug(f"{prefix}gas_estimate={res.ok_or_err()}")
|
|
83
|
+
if isinstance(res, Err):
|
|
84
|
+
logger.info(f"{prefix}estimate_gas error, {res.err}")
|
|
85
|
+
return None
|
|
86
|
+
estimate_value = res.ok
|
|
87
|
+
return calcs.calc_var_wei_value(gas, var_name="estimate", var_value=estimate_value)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def calc_eth_value(
|
|
91
|
+
*,
|
|
92
|
+
nodes: list[str],
|
|
93
|
+
value_str: str,
|
|
94
|
+
address: str,
|
|
95
|
+
gas: int | None = None,
|
|
96
|
+
max_fee_per_gas: int | None = None,
|
|
97
|
+
log_prefix: str | None = None,
|
|
98
|
+
) -> int | None:
|
|
99
|
+
balance_value = None
|
|
100
|
+
if "balance" in value_str.lower():
|
|
101
|
+
prefix = _get_prefix(log_prefix)
|
|
102
|
+
res = rpc.eth_get_balance(nodes, address, attempts=5)
|
|
103
|
+
logger.debug(f"{prefix}balance={res.ok_or_err()}")
|
|
104
|
+
if isinstance(res, Err):
|
|
105
|
+
logger.info(f"{prefix}balance error, {res.err}")
|
|
106
|
+
return None
|
|
107
|
+
balance_value = res.ok
|
|
108
|
+
value = calcs.calc_var_wei_value(value_str, var_name="balance", var_value=balance_value)
|
|
109
|
+
if "balance" in value_str.lower() and gas is not None and max_fee_per_gas is not None:
|
|
110
|
+
value = value - gas * max_fee_per_gas
|
|
111
|
+
return value
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def calc_erc20_value(
|
|
115
|
+
*,
|
|
116
|
+
nodes: list[str],
|
|
117
|
+
value_str: str,
|
|
118
|
+
wallet_address: str,
|
|
119
|
+
token_address: str,
|
|
120
|
+
decimals: int,
|
|
121
|
+
log_prefix: str | None = None,
|
|
122
|
+
) -> int | None:
|
|
123
|
+
value_str = value_str.lower()
|
|
124
|
+
balance_value = None
|
|
125
|
+
if "balance" in value_str:
|
|
126
|
+
prefix = _get_prefix(log_prefix)
|
|
127
|
+
res = erc20.get_balance(nodes, token_address, wallet_address, attempts=5)
|
|
128
|
+
logger.debug(f"{prefix}balance={res.ok_or_err()}")
|
|
129
|
+
if isinstance(res, Err):
|
|
130
|
+
logger.info(f"{prefix}balance error, {res.err}")
|
|
131
|
+
return None
|
|
132
|
+
balance_value = res.ok
|
|
133
|
+
return calcs.calc_var_wei_value(value_str, var_name="balance", var_value=balance_value, decimals=decimals)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _get_prefix(log_prefix: str | None) -> str:
|
|
137
|
+
prefix = log_prefix or ""
|
|
138
|
+
if prefix:
|
|
139
|
+
prefix += ": "
|
|
140
|
+
return prefix
|
mm_eth/cli/validators.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
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.utils import to_wei
|
|
8
|
+
|
|
9
|
+
from . import calcs
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def wei_validator(v: str | None) -> int | None:
|
|
13
|
+
if v is None:
|
|
14
|
+
return None
|
|
15
|
+
return to_wei(v)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def log_validator(v: str | None) -> str | None:
|
|
19
|
+
if v is None:
|
|
20
|
+
return None
|
|
21
|
+
log_file = Path(v).expanduser()
|
|
22
|
+
log_file.touch(exist_ok=True)
|
|
23
|
+
if not (log_file.is_file() and os.access(log_file, os.W_OK)):
|
|
24
|
+
raise ValueError(f"wrong log path: {v}")
|
|
25
|
+
return v
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def nodes_validator(v: str | list[str] | None) -> list[str]:
|
|
29
|
+
if v is None:
|
|
30
|
+
return []
|
|
31
|
+
if isinstance(v, str):
|
|
32
|
+
return str_to_list(v, unique=True, remove_comments=True, split_line=True)
|
|
33
|
+
return v
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def addresses_validator(v: str | list[str] | None) -> list[str]:
|
|
37
|
+
if v is None:
|
|
38
|
+
return []
|
|
39
|
+
if isinstance(v, str):
|
|
40
|
+
return str_to_list(v, unique=True, remove_comments=True, split_line=True, lower=True)
|
|
41
|
+
return v
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def delay_validator(v: str | Decimal) -> Decimal | tuple[Decimal, Decimal]:
|
|
45
|
+
if isinstance(v, int | float):
|
|
46
|
+
return Decimal(str(v))
|
|
47
|
+
if isinstance(v, str):
|
|
48
|
+
arr = [a.strip() for a in v.split("-")]
|
|
49
|
+
if len(arr) != 2:
|
|
50
|
+
raise ValueError("wrong delay value")
|
|
51
|
+
try:
|
|
52
|
+
return Decimal(arr[0]), Decimal(arr[1])
|
|
53
|
+
except InvalidOperation:
|
|
54
|
+
raise ValueError("wrong delay value") from None
|
|
55
|
+
raise ValueError("wrong delay value")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def is_valid_calc_var_wei_value(value: str | None, base_name: str = "var", decimals: int | None = None) -> bool:
|
|
59
|
+
if value is None:
|
|
60
|
+
return True # check for None on BaseModel.field type level
|
|
61
|
+
try:
|
|
62
|
+
calcs.calc_var_wei_value(value, var_value=123, var_name=base_name, decimals=decimals)
|
|
63
|
+
return True # noqa: TRY300
|
|
64
|
+
except ValueError:
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def is_valid_calc_decimal_value(value: str | None) -> bool:
|
|
69
|
+
if value is None:
|
|
70
|
+
return True # check for None on BaseModel.field type level
|
|
71
|
+
try:
|
|
72
|
+
calcs.calc_decimal_value(value)
|
|
73
|
+
return True # noqa: TRY300
|
|
74
|
+
except ValueError:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def is_valid_calc_function_args(value: str | None) -> bool:
|
|
79
|
+
if value is None:
|
|
80
|
+
return True
|
|
81
|
+
try:
|
|
82
|
+
calcs.calc_function_args(value)
|
|
83
|
+
return True # noqa: TRY300
|
|
84
|
+
except ValueError:
|
|
85
|
+
return False
|
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,47 @@
|
|
|
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/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
mm_eth/cli/calcs.py,sha256=yDx3VgFmCLRYWpZVqyk_-mXIgTVFDPKGTktx-7H2XDw,4426
|
|
18
|
+
mm_eth/cli/cli.py,sha256=FfEgosWXa9gIGRuwSbPILvaDQ2Crh9hqTU50CqatvgY,8956
|
|
19
|
+
mm_eth/cli/cli_utils.py,sha256=H8aasm4CBuKoXPA_ZGBTdTBs2ABAR7niFGqQjXOrYOU,3706
|
|
20
|
+
mm_eth/cli/print_helpers.py,sha256=yOiOFjTKloumwf07AqNIHQswUo8t0yuT9bpeGBGl60Q,1470
|
|
21
|
+
mm_eth/cli/rpc_helpers.py,sha256=tAJBUHwpH0jPvg0Hrm-cwpOrpp9GUb91e4ZdwixNUQg,4723
|
|
22
|
+
mm_eth/cli/validators.py,sha256=CWOXorI2HGajhLvQ354d2cCl5S3JC7tKUxWmmVn6j4Y,2417
|
|
23
|
+
mm_eth/cli/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
mm_eth/cli/cmd/balance_cmd.py,sha256=SjRSbGF8QFMBHncEyzPdVUi_rjOwNW2wxfbFsRBV2A0,2097
|
|
25
|
+
mm_eth/cli/cmd/balances_cmd.py,sha256=XOjcyqJLSQL1Jj40fnw3la2ZRGwpZU-NIBpZbCEQNhc,4328
|
|
26
|
+
mm_eth/cli/cmd/call_contract_cmd.py,sha256=lI1QnFTydYTFhZmxmQWvyMCnhTodckck9KGXUBq5PU4,1250
|
|
27
|
+
mm_eth/cli/cmd/config_example_cmd.py,sha256=hMfrFxjIP0xZllJRRxi99qhF8tzJI3lrp-XudFEWF1g,270
|
|
28
|
+
mm_eth/cli/cmd/deploy_cmd.py,sha256=543wR11I5GchYzTqWbUuyxcWMvZs0ZbcT0V_OLdwsew,1260
|
|
29
|
+
mm_eth/cli/cmd/encode_input_data_cmd.py,sha256=9UQ1MKPEFQJ8j_COsP3KGKhwOf9tT3feBezI8vvxTlw,267
|
|
30
|
+
mm_eth/cli/cmd/mnemonic_cmd.py,sha256=Mb0H0inSNCa93GEPxGYQi7BnPe11mnLjnspfe7h54I4,972
|
|
31
|
+
mm_eth/cli/cmd/node_cmd.py,sha256=mUqixPGNHzuKCJvk1Fd5VaUGumpbR2AE7raGmXZscg4,1943
|
|
32
|
+
mm_eth/cli/cmd/private_key_cmd.py,sha256=Fv_2OLog1h32pIP7PJITwl_pHdy3BXvaDRcXZsxY1xo,241
|
|
33
|
+
mm_eth/cli/cmd/rpc_cmd.py,sha256=02q82YqgbPezfEBmV_QBCIeNReE7ktkPych8Xr9ann8,2186
|
|
34
|
+
mm_eth/cli/cmd/send_contract_cmd.py,sha256=fknRzpCKV4nWMus26npeCVCb2JfiHTsSMFGPnP-465o,9141
|
|
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_erc20_cmd.py,sha256=EXOy303fKd3cuC8QEVghAjJw0qrLoO4GFfT37whMt2E,10296
|
|
38
|
+
mm_eth/cli/cmd/transfer_eth_cmd.py,sha256=rBFZzDntXX0ouGb1B15rRaEiEoF88C-It2aiJhSVfzQ,9470
|
|
39
|
+
mm_eth/cli/cmd/vault_cmd.py,sha256=rRTclz0U6N_87KFsssdvZhi3_f6tmkHiYxMsIoVeBGc,532
|
|
40
|
+
mm_eth/cli/config_examples/balances.yml,sha256=fpXJfoOSqOrkoWpqO7-HrTTb5OBs9PM-PpZ1ulqWUnY,399
|
|
41
|
+
mm_eth/cli/config_examples/call_contract.yml,sha256=E0XuWuBnbhyTYfxNqaoxH6Cy7UCYqMLDwoEu_4OSV3M,233
|
|
42
|
+
mm_eth/cli/config_examples/transfer_erc20.yml,sha256=mCUpUfqzZjTvst8kqd3EGzJY0S7cr48ySqS6yZhzLdI,1324
|
|
43
|
+
mm_eth/cli/config_examples/transfer_eth.yml,sha256=i4hh3-LTcpl7JsrSuuM6gDi23B5ZnCeEnaYazQO3bzQ,1181
|
|
44
|
+
mm_eth-0.2.2.dist-info/METADATA,sha256=PUaC1zS4IBlq6bwzjhGXqykcW2hyD9sQ0PKwTHM2DQY,228
|
|
45
|
+
mm_eth-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
46
|
+
mm_eth-0.2.2.dist-info/entry_points.txt,sha256=aGhpsozl8NIrkuUcX5fSgURCcDhr3ShUdeTSIrJq4oc,46
|
|
47
|
+
mm_eth-0.2.2.dist-info/RECORD,,
|
mm_eth-0.2.0.dist-info/METADATA
DELETED
mm_eth-0.2.0.dist-info/RECORD
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
mm_eth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
mm_eth/abi.py,sha256=_xw2c7Eht3-g-fA6yvakHMcehzsRRK_LqTO7Q8Ppkj4,4794
|
|
3
|
-
mm_eth/account.py,sha256=6iiCZ70ojptiaDI1UMaUdJT-SghuOexLeH0YJhCWQ8g,2022
|
|
4
|
-
mm_eth/anvil.py,sha256=9CWXWyK7tVrvs6NeOy7QnVwMTrUke-nBMFwZeJ4VULA,1582
|
|
5
|
-
mm_eth/deploy.py,sha256=SB3ruY808_5UnG8kHR34uVP66P3zOWZu0ImKD7UUv2s,691
|
|
6
|
-
mm_eth/ens.py,sha256=WMxqC1v3zwDDuLH_oWekm22qrNYxCNcvZumQMT7SYds,623
|
|
7
|
-
mm_eth/erc20.py,sha256=m62j00zizSZzXgLUEih2Q1iVFehU9qrqOMfc3gyix7k,6844
|
|
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=kQnYllp5ZHGuIUUX2Vw-ejLwL3zXQgka3ukwDndv4AM,13881
|
|
11
|
-
mm_eth/solc.py,sha256=JcBCel-LO-lBvz3_1f7s3pVhECsUittavBvXqxYIP2c,1066
|
|
12
|
-
mm_eth/tx.py,sha256=qSkaPgow3QEZR0vbnQouw8X4G-Hw9RedPrDB10aZhU0,4046
|
|
13
|
-
mm_eth/types.py,sha256=vXXP5Dc72BpHv5tsyws0KDZebG1W1-5HH0UjL7N2Mgc,113
|
|
14
|
-
mm_eth/utils.py,sha256=kjCIsXtRTG0dL6A1bCCuWctqd0Qh8K8N-ZocQRppYj0,7861
|
|
15
|
-
mm_eth/vault.py,sha256=1ahABcgyYAMUlE4ELMGdlG_uV4irmwzrfzUZTpo3rdY,1500
|
|
16
|
-
mm_eth-0.2.0.dist-info/METADATA,sha256=J5m9XoyV6IPyS5YDrRpTmzTFR_pnEESJXJhuhzeAHB8,169
|
|
17
|
-
mm_eth-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
18
|
-
mm_eth-0.2.0.dist-info/RECORD,,
|
|
File without changes
|