mm-eth 0.2.1__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.
@@ -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
@@ -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
@@ -1,7 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mm-eth
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Requires-Python: >=3.12
5
- Requires-Dist: mm-std~=0.1.10
5
+ Requires-Dist: loguru~=0.7.3
6
+ Requires-Dist: mm-std~=0.1.11
7
+ Requires-Dist: typer>=0.15.1
6
8
  Requires-Dist: web3~=7.7.0
7
9
  Requires-Dist: websocket-client~=1.8.0
@@ -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,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mm-eth = mm_eth.cli.cli:app
@@ -1,18 +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=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,,
File without changes