mm-eth 0.7.1__tar.gz → 0.7.3__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 (57) hide show
  1. mm_eth-0.7.3/.claude/settings.local.json +10 -0
  2. mm_eth-0.7.3/.pre-commit-config.yaml +10 -0
  3. mm_eth-0.7.3/CLAUDE.md +13 -0
  4. mm_eth-0.7.3/PKG-INFO +7 -0
  5. {mm_eth-0.7.1 → mm_eth-0.7.3}/justfile +8 -2
  6. {mm_eth-0.7.1 → mm_eth-0.7.3}/pyproject.toml +14 -13
  7. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/abi.py +1 -2
  8. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cli_utils.py +1 -1
  9. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/balances_cmd.py +2 -2
  10. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/solc_cmd.py +1 -1
  11. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/transfer_cmd.py +6 -4
  12. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/wallet/private_key_cmd.py +1 -1
  13. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/rpc.py +1 -1
  14. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/conftest.py +4 -1
  15. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/test_converters.py +3 -3
  16. mm_eth-0.7.3/uv.lock +1834 -0
  17. mm_eth-0.7.1/PKG-INFO +0 -7
  18. mm_eth-0.7.1/uv.lock +0 -1407
  19. {mm_eth-0.7.1 → mm_eth-0.7.3}/.gitignore +0 -0
  20. {mm_eth-0.7.1 → mm_eth-0.7.3}/README.md +0 -0
  21. {mm_eth-0.7.1 → mm_eth-0.7.3}/dict.dic +0 -0
  22. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/__init__.py +0 -0
  23. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/account.py +0 -0
  24. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/anvil.py +0 -0
  25. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/__init__.py +0 -0
  26. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/calcs.py +0 -0
  27. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cli.py +0 -0
  28. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/__init__.py +0 -0
  29. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/balance_cmd.py +0 -0
  30. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/deploy_cmd.py +0 -0
  31. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/node_cmd.py +0 -0
  32. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/wallet/__init__.py +0 -0
  33. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/cmd/wallet/mnemonic_cmd.py +0 -0
  34. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/rpc_helpers.py +0 -0
  35. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/cli/validators.py +0 -0
  36. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/converters.py +0 -0
  37. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/deploy.py +0 -0
  38. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/erc20.py +0 -0
  39. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/py.typed +0 -0
  40. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/retry.py +0 -0
  41. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/solc.py +0 -0
  42. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/tx.py +0 -0
  43. {mm_eth-0.7.1 → mm_eth-0.7.3}/src/mm_eth/utils.py +0 -0
  44. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/__init__.py +0 -0
  45. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/cli/__init__.py +0 -0
  46. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/cli/cmd/__init__.py +0 -0
  47. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/cli/cmd/test_balance_cmd.py +0 -0
  48. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/cli/cmd/test_node_cmd.py +0 -0
  49. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/cli/cmd/test_solc_cmd.py +0 -0
  50. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/cli/cmd/wallet/__init__.py +0 -0
  51. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/cli/cmd/wallet/test_mnemonic_cmd.py +0 -0
  52. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/cli/cmd/wallet/test_private_key_cmd.py +0 -0
  53. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/contracts/ERC20.sol +0 -0
  54. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/test_abi.py +0 -0
  55. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/test_account.py +0 -0
  56. {mm_eth-0.7.1 → mm_eth-0.7.3}/tests/test_rpc.py +0 -0
  57. {mm_eth-0.7.1 → mm_eth-0.7.3}/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
+ }
@@ -0,0 +1,10 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v6.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-toml
9
+ - id: check-json
10
+ - id: check-added-large-files
mm_eth-0.7.3/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.3/PKG-INFO ADDED
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: mm-eth
3
+ Version: 0.7.3
4
+ Requires-Python: >=3.13
5
+ Requires-Dist: mm-web3~=0.5.4
6
+ Requires-Dist: typer~=0.20.0
7
+ Requires-Dist: web3~=7.14.0
@@ -15,8 +15,8 @@ format:
15
15
  test:
16
16
  uv run pytest -n auto tests
17
17
 
18
- lint: format
19
- uv run ruff check src tests
18
+ lint *args: format pre-commit
19
+ uv run ruff check {{args}} src tests
20
20
  uv run mypy src
21
21
 
22
22
  audit:
@@ -36,3 +36,9 @@ sync:
36
36
 
37
37
  anvil:
38
38
  anvil -m "$MNEMONIC"
39
+
40
+ pre-commit:
41
+ uv run pre-commit run --all-files
42
+
43
+ pre-commit-autoupdate:
44
+ uv run pre-commit autoupdate
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "mm-eth"
3
- version = "0.7.1"
3
+ version = "0.7.3"
4
4
  description = ""
5
5
  requires-python = ">=3.13"
6
6
  dependencies = [
7
- "mm-web3~=0.5.1",
8
- "web3~=7.12.0",
9
- "typer~=0.16.0",
7
+ "mm-web3~=0.5.4",
8
+ "web3~=7.14.0",
9
+ "typer~=0.20.0",
10
10
  ]
11
11
  [project.scripts]
12
12
  mm-eth = "mm_eth.cli.cli:app"
@@ -15,16 +15,17 @@ mm-eth = "mm_eth.cli.cli:app"
15
15
  requires = ["hatchling"]
16
16
  build-backend = "hatchling.build"
17
17
 
18
- [tool.uv]
19
- dev-dependencies = [
20
- "pytest~=8.4.0",
21
- "pytest-asyncio~=1.0.0",
22
- "pytest-xdist~=3.7.0",
23
- "ruff~=0.11.13",
18
+ [dependency-groups]
19
+ dev = [
20
+ "pytest~=8.4.2",
21
+ "pytest-asyncio~=1.2.0",
22
+ "pytest-xdist~=3.8.0",
23
+ "ruff~=0.14.2",
24
24
  "pip-audit~=2.9.0",
25
- "bandit~=1.8.3",
26
- "mypy~=1.16.0",
27
- "python-dotenv>=1.1.0",
25
+ "bandit~=1.8.6",
26
+ "mypy~=1.18.2",
27
+ "pre-commit~=4.3.0",
28
+ "python-dotenv~=1.2.1",
28
29
  ]
29
30
 
30
31
  [tool.mypy]
@@ -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}")
@@ -96,7 +96,10 @@ def erc20_token_bin() -> str:
96
96
 
97
97
  @pytest.fixture(scope="session")
98
98
  def proxies() -> list[str]:
99
- return fetch_proxies_sync(os.getenv("PROXIES_URL")).unwrap()
99
+ proxies_url = os.getenv("PROXIES_URL")
100
+ if not proxies_url:
101
+ raise RuntimeError("PROXIES_URL environment variable is not set")
102
+ return fetch_proxies_sync(proxies_url).unwrap()
100
103
 
101
104
 
102
105
  @pytest.fixture()
@@ -7,7 +7,7 @@ from mm_eth import converters
7
7
 
8
8
  def test_to_wei():
9
9
  assert converters.to_wei(123) == 123
10
- assert converters.to_wei(Decimal("123")) == 123
10
+ assert converters.to_wei(Decimal(123)) == 123
11
11
  assert converters.to_wei("11gwei") == 11000000000
12
12
  assert converters.to_wei("12.1t", decimals=6) == 12.1 * 10**6
13
13
 
@@ -19,5 +19,5 @@ def test_to_wei():
19
19
 
20
20
  def test_from_wei():
21
21
  assert converters.from_wei(123000000000000000, "ether") == Decimal("0.123")
22
- assert converters.from_wei(0, "ether") == Decimal("0")
23
- assert converters.from_wei(12.1 * 10**6, "t", decimals=6) == Decimal("12.1")
22
+ assert converters.from_wei(0, "ether") == Decimal(0)
23
+ assert converters.from_wei(int(12.1 * 10**6), "t", decimals=6) == Decimal("12.1")