mm-eth 0.7.2__tar.gz → 0.7.4__tar.gz

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 (58) hide show
  1. mm_eth-0.7.4/.claude/settings.local.json +10 -0
  2. mm_eth-0.7.4/CLAUDE.md +13 -0
  3. mm_eth-0.7.4/PKG-INFO +7 -0
  4. mm_eth-0.7.4/pyproject.toml +84 -0
  5. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/abi.py +1 -2
  6. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cli_utils.py +1 -1
  7. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/balances_cmd.py +2 -2
  8. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/solc_cmd.py +1 -1
  9. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/transfer_cmd.py +6 -4
  10. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/wallet/private_key_cmd.py +1 -1
  11. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/rpc.py +1 -1
  12. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/solc.py +1 -1
  13. mm_eth-0.7.4/uv.lock +1939 -0
  14. mm_eth-0.7.2/PKG-INFO +0 -7
  15. mm_eth-0.7.2/pyproject.toml +0 -88
  16. mm_eth-0.7.2/uv.lock +0 -1484
  17. {mm_eth-0.7.2 → mm_eth-0.7.4}/.gitignore +0 -0
  18. {mm_eth-0.7.2 → mm_eth-0.7.4}/.pre-commit-config.yaml +0 -0
  19. {mm_eth-0.7.2 → mm_eth-0.7.4}/README.md +0 -0
  20. {mm_eth-0.7.2 → mm_eth-0.7.4}/dict.dic +0 -0
  21. {mm_eth-0.7.2 → mm_eth-0.7.4}/justfile +0 -0
  22. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/__init__.py +0 -0
  23. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/account.py +0 -0
  24. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/anvil.py +0 -0
  25. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/__init__.py +0 -0
  26. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/calcs.py +0 -0
  27. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cli.py +0 -0
  28. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/__init__.py +0 -0
  29. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/balance_cmd.py +0 -0
  30. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/deploy_cmd.py +0 -0
  31. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/node_cmd.py +0 -0
  32. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/wallet/__init__.py +0 -0
  33. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/cmd/wallet/mnemonic_cmd.py +0 -0
  34. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/rpc_helpers.py +0 -0
  35. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/cli/validators.py +0 -0
  36. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/converters.py +0 -0
  37. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/deploy.py +0 -0
  38. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/erc20.py +0 -0
  39. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/py.typed +0 -0
  40. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/retry.py +0 -0
  41. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/tx.py +0 -0
  42. {mm_eth-0.7.2 → mm_eth-0.7.4}/src/mm_eth/utils.py +0 -0
  43. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/__init__.py +0 -0
  44. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/cli/__init__.py +0 -0
  45. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/cli/cmd/__init__.py +0 -0
  46. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/cli/cmd/test_balance_cmd.py +0 -0
  47. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/cli/cmd/test_node_cmd.py +0 -0
  48. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/cli/cmd/test_solc_cmd.py +0 -0
  49. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/cli/cmd/wallet/__init__.py +0 -0
  50. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/cli/cmd/wallet/test_mnemonic_cmd.py +0 -0
  51. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/cli/cmd/wallet/test_private_key_cmd.py +0 -0
  52. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/conftest.py +0 -0
  53. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/contracts/ERC20.sol +0 -0
  54. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/test_abi.py +0 -0
  55. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/test_account.py +0 -0
  56. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/test_converters.py +0 -0
  57. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/test_rpc.py +0 -0
  58. {mm_eth-0.7.2 → mm_eth-0.7.4}/tests/test_tx.py +0 -0
@@ -0,0 +1,10 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(just lint:*)",
5
+ "mcp__ide__getDiagnostics"
6
+ ],
7
+ "deny": [],
8
+ "ask": []
9
+ }
10
+ }
mm_eth-0.7.4/CLAUDE.md ADDED
@@ -0,0 +1,13 @@
1
+ # Claude Guidelines
2
+
3
+ ## Critical Guidelines
4
+
5
+ 1. **Always communicate in English** - Regardless of the language the user speaks, always respond in English. All code, comments, and documentation must be in English.
6
+
7
+ 2. **Minimal documentation** - Only add comments/documentation when it simplifies understanding and isn't obvious from the code itself. Keep it strictly relevant and concise.
8
+
9
+ 3. **Critical thinking** - Always critically evaluate user ideas. Users can make mistakes. Think first about whether the user's idea is good before implementing.
10
+
11
+ 4. **Lint after changes** - After making code changes, always run `just lint` to verify code quality and fix any linter issues.
12
+
13
+ 5. **No disabling linter rules** - Never use special disabling comments (like `# noqa`, `# type: ignore`, `# ruff: noqa`, etc.) to turn off linter rules without explicit permission. If you believe a rule should be disabled, ask first.
mm_eth-0.7.4/PKG-INFO ADDED
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: mm-eth
3
+ Version: 0.7.4
4
+ Requires-Python: >=3.13
5
+ Requires-Dist: mm-web3~=0.5.6
6
+ Requires-Dist: typer~=0.21.1
7
+ Requires-Dist: web3~=7.14.0
@@ -0,0 +1,84 @@
1
+ [project]
2
+ name = "mm-eth"
3
+ version = "0.7.4"
4
+ description = ""
5
+ requires-python = ">=3.13"
6
+ dependencies = ["mm-web3~=0.5.6", "web3~=7.14.0", "typer~=0.21.1"]
7
+ [project.scripts]
8
+ mm-eth = "mm_eth.cli.cli:app"
9
+
10
+ [build-system]
11
+ requires = ["hatchling"]
12
+ build-backend = "hatchling.build"
13
+
14
+ [dependency-groups]
15
+ dev = [
16
+ "bandit~=1.9.2",
17
+ "mypy~=1.19.1",
18
+ "pip-audit~=2.10.0",
19
+ "pre-commit~=4.5.1",
20
+ "pytest~=9.0.2",
21
+ "pytest-asyncio~=1.3.0",
22
+ "pytest-xdist~=3.8.0",
23
+ "ruff~=0.14.11",
24
+ "python-dotenv~=1.2.0",
25
+ ]
26
+
27
+ [tool.mypy]
28
+ python_version = "3.13"
29
+ mypy_path = "stubs"
30
+ warn_no_return = false
31
+ implicit_reexport = true
32
+ strict = true
33
+ enable_error_code = ["truthy-bool", "possibly-undefined"]
34
+ exclude = ["^tests/", "^tmp/"]
35
+ [[tool.mypy.overrides]]
36
+ module = ["rlp", "rlp.sedes", "ens.utils", "ens"]
37
+ ignore_missing_imports = true
38
+
39
+ [tool.ruff]
40
+ line-length = 130
41
+ target-version = "py313"
42
+ [tool.ruff.lint]
43
+ select = ["ALL"]
44
+ # fixable = ["F401"]
45
+ ignore = [
46
+ "TC", # flake8-type-checking, TYPE_CHECKING is dangerous, for example it doesn't work with pydantic
47
+ "A005", # flake8-builtins: stdlib-module-shadowing
48
+ "ERA001", # eradicate: commented-out-code
49
+ "PT", # flake8-pytest-style
50
+ "D", # pydocstyle
51
+ "FIX", # flake8-fixme
52
+ "PLR0911", # pylint: too-many-return-statements
53
+ "PLR0912", # pylint: too-many-branches
54
+ "PLR0913", # pylint: too-many-arguments
55
+ "PLR2004", # pylint: magic-value-comparison
56
+ "PLC0414", # pylint: useless-import-alias
57
+ "FBT", # flake8-boolean-trap
58
+ "EM", # flake8-errmsg
59
+ "TRY003", # tryceratops: raise-vanilla-args
60
+ "C901", # mccabe: complex-structure,
61
+ "BLE001", # flake8-blind-except
62
+ "S311", # bandit: suspicious-non-cryptographic-random-usage
63
+ "TD002", # flake8-todos: missing-todo-author
64
+ "TD003", # flake8-todos: missing-todo-link
65
+ "RET503", # flake8-return: implicit-return
66
+ "COM812", # it's used in ruff formatter
67
+ "ASYNC109", # flake8-async: async-function-with-timeout
68
+ "G004",
69
+ ]
70
+ [tool.ruff.lint.pep8-naming]
71
+ classmethod-decorators = ["field_validator"]
72
+ [tool.ruff.lint.per-file-ignores]
73
+ "tests/*.py" = ["ANN", "S"]
74
+ [tool.ruff.format]
75
+ quote-style = "double"
76
+ indent-style = "space"
77
+
78
+ [tool.bandit]
79
+ exclude_dirs = ["tests"]
80
+ skips = ["B311"]
81
+
82
+ [tool.pytest.ini_options]
83
+ asyncio_mode = "auto"
84
+ asyncio_default_fixture_loop_scope = "function"
@@ -5,7 +5,6 @@ from typing import Any, cast
5
5
 
6
6
  import eth_abi
7
7
  import eth_utils
8
- import pydash
9
8
  from eth_typing import ABI, ABIFunction, HexStr
10
9
  from pydantic import BaseModel
11
10
  from web3 import Web3
@@ -62,7 +61,7 @@ def decode_function_input(contract_abi: ABI, tx_input: str) -> FunctionInput:
62
61
 
63
62
 
64
63
  def get_function_abi(contr_abi: ABI, fn_name: str) -> ABIFunction:
65
- 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]
64
+ abi = next((x for x in contr_abi if x.get("name", None) == fn_name and x.get("type", None) == "function"), None)
66
65
  if not abi:
67
66
  raise ValueError("can't find abi for function: " + fn_name)
68
67
  return cast(ABIFunction, abi)
@@ -46,7 +46,7 @@ async def check_nodes_for_chain_id(nodes: list[str], chain_id: int) -> None:
46
46
  for node in nodes:
47
47
  res = (await rpc.eth_chain_id(node)).unwrap("can't get chain_id")
48
48
  if res != chain_id:
49
- mm_print.fatal(f"node {node} has a wrong chain_id: {res}")
49
+ mm_print.exit_with_error(f"node {node} has a wrong chain_id: {res}")
50
50
 
51
51
 
52
52
  def add_table_raw(table: Table, *row: object) -> None:
@@ -105,12 +105,12 @@ async def _get_tokens_info(config: Config) -> list[Token]:
105
105
  for address in config.tokens:
106
106
  decimals_res = await retry.erc20_decimals(5, config.nodes, None, token=address)
107
107
  if decimals_res.is_err():
108
- mm_print.fatal(f"can't get token {address} decimals: {decimals_res.unwrap_err()}")
108
+ mm_print.exit_with_error(f"can't get token {address} decimals: {decimals_res.unwrap_err()}")
109
109
  decimal = decimals_res.unwrap()
110
110
 
111
111
  symbols_res = await retry.erc20_symbol(5, config.nodes, None, token=address)
112
112
  if symbols_res.is_err():
113
- mm_print.fatal(f"can't get token {address} symbol: {symbols_res.unwrap_err()}")
113
+ mm_print.exit_with_error(f"can't get token {address} symbol: {symbols_res.unwrap_err()}")
114
114
  symbol = symbols_res.unwrap()
115
115
 
116
116
  result.append(Token(address=address, decimals=decimal, symbol=symbol))
@@ -11,7 +11,7 @@ def run(contract_path: Path, tmp_dir: Path, print_format: PrintFormat) -> None:
11
11
  contract_name = contract_path.stem
12
12
  res = solc(contract_name, contract_path, tmp_dir)
13
13
  if res.is_err():
14
- mm_print.fatal(res.unwrap_err())
14
+ mm_print.exit_with_error(res.unwrap_err())
15
15
 
16
16
  bin_ = res.unwrap().bin
17
17
  abi = res.unwrap().abi
@@ -43,8 +43,8 @@ class Config(Web3CliConfig):
43
43
  def from_addresses(self) -> list[str]:
44
44
  return [r.from_address for r in self.transfers]
45
45
 
46
- @model_validator(mode="after") # type:ignore[misc]
47
- async def final_validator(self) -> Self:
46
+ @model_validator(mode="after")
47
+ def final_validator(self) -> Self:
48
48
  if not self.private_keys.contains_all_addresses(self.from_addresses):
49
49
  raise ValueError("private keys are not set for all addresses")
50
50
 
@@ -66,13 +66,14 @@ class Config(Web3CliConfig):
66
66
  if self.value_min_limit:
67
67
  Validators.valid_eth_expression()(self.value_min_limit)
68
68
 
69
+ return self
70
+
71
+ async def async_init(self) -> None:
69
72
  if self.token:
70
73
  self.token_decimals = (await retry.erc20_decimals(5, self.nodes, self.proxies, token=self.token)).unwrap(
71
74
  "can't get token decimals"
72
75
  )
73
76
 
74
- return self
75
-
76
77
 
77
78
  class TransferCmdParams(BaseConfigParams):
78
79
  print_balances: bool
@@ -84,6 +85,7 @@ class TransferCmdParams(BaseConfigParams):
84
85
 
85
86
  async def run(params: TransferCmdParams) -> None:
86
87
  config = await Config.read_toml_config_or_exit_async(params.config_path)
88
+ await config.async_init()
87
89
  if params.print_config:
88
90
  config.print_and_exit(exclude={"private_keys"})
89
91
 
@@ -8,4 +8,4 @@ def run(private_key: str) -> None:
8
8
  if res.is_ok():
9
9
  mm_print.plain(res.unwrap())
10
10
  else:
11
- mm_print.fatal(f"invalid private key: '{private_key}'")
11
+ mm_print.exit_with_error(f"invalid private key: '{private_key}'")
@@ -35,7 +35,7 @@ async def _http_call(node: str, data: dict[str, object], timeout: float, proxy:
35
35
  if res.is_err():
36
36
  return res.to_result_err()
37
37
  try:
38
- parsed_body = res.parse_json_body()
38
+ parsed_body = res.parse_json()
39
39
  err = parsed_body.get("error", {}).get("message", "")
40
40
  if err:
41
41
  return res.to_result_err(f"service_error: {err}")
@@ -32,7 +32,7 @@ def solc(contract_name: str, contract_path: Path, tmp_dir: Path) -> Result[SolcR
32
32
  work_dir_created = True
33
33
 
34
34
  cmd = f"solc -o '{work_dir}' --abi --bin --optimize '{contract_path}'"
35
- result = mm_std.shell(cmd)
35
+ result = mm_std.run_cmd(cmd)
36
36
  if result.code != 0:
37
37
  return Result.err(f"solc error: {result.stderr}")
38
38