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.
Files changed (43) hide show
  1. mm_eth/abi.py +3 -3
  2. mm_eth/account.py +1 -1
  3. mm_eth/anvil.py +2 -2
  4. mm_eth/cli/__init__.py +0 -0
  5. mm_eth/cli/calcs.py +112 -0
  6. mm_eth/cli/cli.py +237 -0
  7. mm_eth/cli/cli_utils.py +105 -0
  8. mm_eth/cli/cmd/__init__.py +0 -0
  9. mm_eth/cli/cmd/balance_cmd.py +51 -0
  10. mm_eth/cli/cmd/balances_cmd.py +124 -0
  11. mm_eth/cli/cmd/call_contract_cmd.py +43 -0
  12. mm_eth/cli/cmd/config_example_cmd.py +9 -0
  13. mm_eth/cli/cmd/deploy_cmd.py +43 -0
  14. mm_eth/cli/cmd/encode_input_data_cmd.py +10 -0
  15. mm_eth/cli/cmd/mnemonic_cmd.py +26 -0
  16. mm_eth/cli/cmd/node_cmd.py +47 -0
  17. mm_eth/cli/cmd/private_key_cmd.py +10 -0
  18. mm_eth/cli/cmd/rpc_cmd.py +78 -0
  19. mm_eth/cli/cmd/send_contract_cmd.py +251 -0
  20. mm_eth/cli/cmd/solc_cmd.py +24 -0
  21. mm_eth/cli/cmd/token_cmd.py +29 -0
  22. mm_eth/cli/cmd/transfer_erc20_cmd.py +277 -0
  23. mm_eth/cli/cmd/transfer_eth_cmd.py +254 -0
  24. mm_eth/cli/cmd/vault_cmd.py +16 -0
  25. mm_eth/cli/config_examples/balances.yml +15 -0
  26. mm_eth/cli/config_examples/call_contract.yml +5 -0
  27. mm_eth/cli/config_examples/transfer_erc20.yml +26 -0
  28. mm_eth/cli/config_examples/transfer_eth.yml +24 -0
  29. mm_eth/cli/print_helpers.py +37 -0
  30. mm_eth/cli/rpc_helpers.py +140 -0
  31. mm_eth/cli/validators.py +85 -0
  32. mm_eth/erc20.py +8 -7
  33. mm_eth/rpc.py +8 -8
  34. mm_eth/solc.py +2 -3
  35. mm_eth/tx.py +3 -5
  36. mm_eth/utils.py +11 -16
  37. mm_eth/vault.py +5 -5
  38. mm_eth-0.2.2.dist-info/METADATA +9 -0
  39. mm_eth-0.2.2.dist-info/RECORD +47 -0
  40. mm_eth-0.2.2.dist-info/entry_points.txt +2 -0
  41. mm_eth-0.2.0.dist-info/METADATA +0 -7
  42. mm_eth-0.2.0.dist-info/RECORD +0 -18
  43. {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
@@ -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", None) and result.get("highestBlock", None):
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 = os.path.expanduser(tmp_dir)
17
+ tmp_dir = Path(tmp_dir).expanduser().as_posix()
19
18
  if contract_path.startswith("~"):
20
- contract_path = os.path.expanduser(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
- r = int(r, 16) # type:ignore
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
- elif isinstance(value, str):
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
- elif value.isdigit():
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
- elif isinstance(value, Decimal):
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
- elif isinstance(value, str):
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
- else:
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
- elif isinstance(value, Decimal):
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
- elif isinstance(value, str):
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 ValueError(f"value has a wrong type: {type(value)}")
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!!!""" # TODO: check that keys_url is kv1 version and error if it's kv2
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
- raise ValueError("there is a wrong private key")
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
- elif res.code == 403:
34
+ if res.code == 403:
34
35
  return Err("permission denied", data=data)
35
- else:
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,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: mm-eth
3
+ Version: 0.2.2
4
+ Requires-Python: >=3.12
5
+ Requires-Dist: loguru~=0.7.3
6
+ Requires-Dist: mm-std~=0.1.11
7
+ Requires-Dist: typer>=0.15.1
8
+ Requires-Dist: web3~=7.7.0
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,7 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: mm-eth
3
- Version: 0.2.0
4
- Requires-Python: >=3.12
5
- Requires-Dist: mm-std~=0.1.9
6
- Requires-Dist: web3~=7.6.1
7
- Requires-Dist: websocket-client~=1.8.0
@@ -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