mm-eth 0.6.2__py3-none-any.whl → 0.7.1__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 CHANGED
@@ -72,7 +72,7 @@ def encode_function_input_by_abi(abi: ABI | ABIFunction, fn_name: str, args: lis
72
72
  # if abi is contract_abi, get function_abi
73
73
  if isinstance(abi, Sequence):
74
74
  abi = get_function_abi(abi, fn_name)
75
- abi = cast(ABIFunction, abi)
75
+ # abi = cast(ABIFunction, abi)
76
76
 
77
77
  # need update all address values to checkSum version
78
78
  processed_args = []
mm_eth/account.py CHANGED
@@ -1,12 +1,12 @@
1
1
  from dataclasses import dataclass
2
2
 
3
+ import eth_utils
3
4
  from eth_account import Account
4
5
  from eth_account.hdaccount import Mnemonic
5
6
  from eth_account.signers.local import LocalAccount
6
7
  from eth_account.types import Language
7
8
  from eth_keys import KeyAPI
8
- from eth_utils import decode_hex
9
- from mm_std import Result
9
+ from mm_result import Result
10
10
 
11
11
  Account.enable_unaudited_hdwallet_features()
12
12
 
@@ -98,7 +98,7 @@ def is_private_key(private_key: str) -> bool:
98
98
  bool: True if valid, False otherwise.
99
99
  """
100
100
  try:
101
- key_api.PrivateKey(decode_hex(private_key)).public_key.to_address()
101
+ key_api.PrivateKey(eth_utils.decode_hex(private_key)).public_key.to_address()
102
102
  return True # noqa: TRY300
103
103
  except Exception:
104
104
  return False
mm_eth/anvil.py CHANGED
@@ -1,10 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import socket
3
4
  import time
4
5
  from subprocess import Popen # nosec
6
+ from typing import cast
5
7
 
6
- from mm_std import Result
7
- from mm_std.net import get_free_local_port
8
+ from mm_result import Result
8
9
 
9
10
  from mm_eth import account, rpc
10
11
 
@@ -54,3 +55,11 @@ class Anvil:
54
55
  port = get_free_local_port()
55
56
 
56
57
  return Result.err("can't launch anvil")
58
+
59
+
60
+ def get_free_local_port() -> int:
61
+ sock = socket.socket()
62
+ sock.bind(("", 0))
63
+ port = sock.getsockname()[1]
64
+ sock.close()
65
+ return cast(int, port)
mm_eth/cli/calcs.py CHANGED
@@ -1,12 +1,11 @@
1
- import mm_crypto_utils
2
- from mm_crypto_utils import VarInt
1
+ import mm_web3
3
2
 
4
3
  from mm_eth.cli.validators import SUFFIX_DECIMALS
5
4
 
6
5
 
7
- def calc_eth_expression(expression: str, var: VarInt | None = None) -> int:
8
- return mm_crypto_utils.calc_int_expression(expression, var=var, suffix_decimals=SUFFIX_DECIMALS)
6
+ def calc_eth_expression(expression: str, variables: dict[str, int] | None = None) -> int:
7
+ return mm_web3.calc_expression_with_vars(expression, variables, unit_decimals=SUFFIX_DECIMALS)
9
8
 
10
9
 
11
- def calc_token_expression(expression: str, token_decimals: int, var: VarInt | None = None) -> int:
12
- return mm_crypto_utils.calc_int_expression(expression, var=var, suffix_decimals={"t": token_decimals})
10
+ def calc_token_expression(expression: str, token_decimals: int, variables: dict[str, int] | None = None) -> int:
11
+ return mm_web3.calc_expression_with_vars(expression, variables, unit_decimals={"t": token_decimals})
mm_eth/cli/cli.py CHANGED
@@ -3,10 +3,11 @@ import importlib.metadata
3
3
  from pathlib import Path
4
4
  from typing import Annotated
5
5
 
6
+ import mm_print
6
7
  import typer
7
- from mm_std import PrintFormat, print_plain
8
8
 
9
9
  from mm_eth.account import DEFAULT_DERIVATION_PATH
10
+ from mm_eth.cli.cli_utils import PrintFormat
10
11
  from mm_eth.cli.cmd import balance_cmd, balances_cmd, deploy_cmd, node_cmd, solc_cmd, transfer_cmd
11
12
  from mm_eth.cli.cmd.balances_cmd import BalancesCmdParams
12
13
  from mm_eth.cli.cmd.deploy_cmd import DeployCmdParams
@@ -124,7 +125,7 @@ def transfer_command(
124
125
 
125
126
  def version_callback(value: bool) -> None:
126
127
  if value:
127
- print_plain(f"mm-eth: {importlib.metadata.version('mm-eth')}")
128
+ mm_print.plain(f"mm-eth: {importlib.metadata.version('mm-eth')}")
128
129
  raise typer.Exit
129
130
 
130
131
 
mm_eth/cli/cli_utils.py CHANGED
@@ -1,13 +1,21 @@
1
1
  import importlib.metadata
2
+ from enum import Enum, unique
2
3
  from pathlib import Path
3
4
 
4
- from mm_std import fatal
5
+ import mm_print
5
6
  from pydantic import BaseModel
6
7
  from rich.table import Table
7
8
 
8
9
  from mm_eth import rpc
9
10
 
10
11
 
12
+ @unique
13
+ class PrintFormat(str, Enum):
14
+ PLAIN = "plain"
15
+ TABLE = "table"
16
+ JSON = "json"
17
+
18
+
11
19
  def public_rpc_url(url: str | None) -> str:
12
20
  if not url or url == "1":
13
21
  return "https://ethereum-rpc.publicnode.com"
@@ -36,9 +44,9 @@ class BaseConfigParams(BaseModel):
36
44
 
37
45
  async def check_nodes_for_chain_id(nodes: list[str], chain_id: int) -> None:
38
46
  for node in nodes:
39
- res = (await rpc.eth_chain_id(node)).unwrap_or_exit("can't get chain_id")
47
+ res = (await rpc.eth_chain_id(node)).unwrap("can't get chain_id")
40
48
  if res != chain_id:
41
- fatal(f"node {node} has a wrong chain_id: {res}")
49
+ mm_print.fatal(f"node {node} has a wrong chain_id: {res}")
42
50
 
43
51
 
44
52
  def add_table_raw(table: Table, *row: object) -> None:
@@ -1,8 +1,9 @@
1
1
  import eth_utils
2
- from mm_std import PrintFormat, print_json, print_plain
2
+ import mm_print
3
3
 
4
4
  from mm_eth import converters, rpc
5
5
  from mm_eth.cli import cli_utils
6
+ from mm_eth.cli.cli import PrintFormat
6
7
 
7
8
 
8
9
  async def run(rpc_url: str, wallet_address: str, token_address: str | None, wei: bool, print_format: PrintFormat) -> None:
@@ -12,7 +13,7 @@ async def run(rpc_url: str, wallet_address: str, token_address: str | None, wei:
12
13
  # nonce
13
14
  result["nonce"] = (await rpc.eth_get_transaction_count(rpc_url, wallet_address)).value_or_error()
14
15
  if print_format == PrintFormat.PLAIN:
15
- print_plain(f"nonce: {result['nonce']}")
16
+ mm_print.plain(f"nonce: {result['nonce']}")
16
17
 
17
18
  # eth balance
18
19
  result["eth_balance"] = (
@@ -21,18 +22,18 @@ async def run(rpc_url: str, wallet_address: str, token_address: str | None, wei:
21
22
  .value_or_error()
22
23
  )
23
24
  if print_format == PrintFormat.PLAIN:
24
- print_plain(f"eth_balance: {result['eth_balance']}")
25
+ mm_print.plain(f"eth_balance: {result['eth_balance']}")
25
26
 
26
27
  if token_address:
27
28
  # token decimal
28
29
  result["token_decimal"] = (await rpc.erc20_decimals(rpc_url, token_address)).value_or_error()
29
30
  if print_format == PrintFormat.PLAIN:
30
- print_plain(f"token_decimal: {result['token_decimal']}")
31
+ mm_print.plain(f"token_decimal: {result['token_decimal']}")
31
32
 
32
33
  # token symbol
33
34
  result["token_symbol"] = (await rpc.erc20_symbol(rpc_url, token_address)).value_or_error()
34
35
  if print_format == PrintFormat.PLAIN:
35
- print_plain(f"token_symbol: {result['token_symbol']}")
36
+ mm_print.plain(f"token_symbol: {result['token_symbol']}")
36
37
 
37
38
  # token balance
38
39
  result["token_balance"] = (await rpc.erc20_balance(rpc_url, token_address, wallet_address)).value_or_error()
@@ -40,7 +41,7 @@ async def run(rpc_url: str, wallet_address: str, token_address: str | None, wei:
40
41
  result["token_balance"] = converters.from_wei(result["token_balance"], "t", decimals=result["token_decimal"])
41
42
 
42
43
  if print_format == PrintFormat.PLAIN:
43
- print_plain(f"token_balance: {result['token_balance']}")
44
+ mm_print.plain(f"token_balance: {result['token_balance']}")
44
45
 
45
46
  if print_format == PrintFormat.JSON:
46
- print_json(data=result)
47
+ mm_print.json(data=result)
@@ -1,7 +1,8 @@
1
1
  from dataclasses import dataclass
2
2
  from typing import Annotated
3
3
 
4
- from mm_std import BaseConfig, fatal
4
+ import mm_print
5
+ from mm_web3 import Web3CliConfig
5
6
  from pydantic import BeforeValidator
6
7
  from rich.live import Live
7
8
  from rich.table import Table
@@ -11,7 +12,7 @@ from mm_eth.cli.cli_utils import BaseConfigParams
11
12
  from mm_eth.cli.validators import Validators
12
13
 
13
14
 
14
- class Config(BaseConfig):
15
+ class Config(Web3CliConfig):
15
16
  addresses: Annotated[list[str], BeforeValidator(Validators.eth_addresses(unique=True))]
16
17
  tokens: Annotated[list[str], BeforeValidator(Validators.eth_addresses(unique=True))]
17
18
  nodes: Annotated[list[str], BeforeValidator(Validators.nodes())]
@@ -63,7 +64,7 @@ async def run(params: BalancesCmdParams) -> None:
63
64
  else:
64
65
  row.append(str(converters.from_wei(balance, "eth", round_ndigits=config.round_ndigits)))
65
66
  else:
66
- row.append(base_balance_res.unwrap_error())
67
+ row.append(base_balance_res.unwrap_err())
67
68
 
68
69
  for t in tokens:
69
70
  token_balance_res = await retry.erc20_balance(5, config.nodes, None, token=t.address, wallet=address)
@@ -77,7 +78,7 @@ async def run(params: BalancesCmdParams) -> None:
77
78
  str(converters.from_wei(token_balance, "t", round_ndigits=config.round_ndigits, decimals=t.decimals))
78
79
  )
79
80
  else:
80
- row.append(token_balance_res.unwrap_error())
81
+ row.append(token_balance_res.unwrap_err())
81
82
 
82
83
  table.add_row(*row)
83
84
 
@@ -104,12 +105,12 @@ async def _get_tokens_info(config: Config) -> list[Token]:
104
105
  for address in config.tokens:
105
106
  decimals_res = await retry.erc20_decimals(5, config.nodes, None, token=address)
106
107
  if decimals_res.is_err():
107
- fatal(f"can't get token {address} decimals: {decimals_res.unwrap_error()}")
108
+ mm_print.fatal(f"can't get token {address} decimals: {decimals_res.unwrap_err()}")
108
109
  decimal = decimals_res.unwrap()
109
110
 
110
111
  symbols_res = await retry.erc20_symbol(5, config.nodes, None, token=address)
111
112
  if symbols_res.is_err():
112
- fatal(f"can't get token {address} symbol: {symbols_res.unwrap_error()}")
113
+ mm_print.fatal(f"can't get token {address} symbol: {symbols_res.unwrap_err()}")
113
114
  symbol = symbols_res.unwrap()
114
115
 
115
116
  result.append(Token(address=address, decimals=decimal, symbol=symbol))
@@ -1,13 +1,15 @@
1
1
  from typing import cast
2
2
 
3
- from mm_std import BaseConfig, print_console, toml_loads
3
+ import mm_print
4
+ import tomlkit
5
+ from mm_web3 import Web3CliConfig
4
6
  from pydantic import StrictStr
5
7
 
6
8
  from mm_eth import account, deploy, retry
7
9
  from mm_eth.cli.cli_utils import BaseConfigParams
8
10
 
9
11
 
10
- class Config(BaseConfig):
12
+ class Config(Web3CliConfig):
11
13
  private_key: StrictStr
12
14
  nonce: int | None = None
13
15
  gas: int
@@ -30,7 +32,7 @@ async def run(cli_params: DeployCmdParams) -> None:
30
32
  if cli_params.print_config:
31
33
  config.print_and_exit({"private_key"})
32
34
 
33
- parsed = toml_loads(f"constructor_types = {config.constructor_types}\nconstructor_values = {config.constructor_values}")
35
+ parsed = tomlkit.loads(f"constructor_types = {config.constructor_types}\nconstructor_values = {config.constructor_values}")
34
36
  constructor_types = cast(list[str], parsed["constructor_types"])
35
37
  constructor_values = cast(list[object], parsed["constructor_values"])
36
38
 
@@ -42,4 +44,4 @@ async def run(cli_params: DeployCmdParams) -> None:
42
44
  )
43
45
 
44
46
  res = deploy.get_deploy_contract_data(config.contract_bin, constructor_types, constructor_values)
45
- print_console(res)
47
+ mm_print.json(res)
@@ -1,13 +1,14 @@
1
1
  from decimal import Decimal
2
2
 
3
3
  import eth_utils
4
+ import mm_print
4
5
  import pydash
5
- from mm_std import PrintFormat, print_json
6
6
  from pydantic import BaseModel
7
7
  from rich.live import Live
8
8
  from rich.table import Table
9
9
 
10
10
  from mm_eth import rpc, utils
11
+ from mm_eth.cli.cli import PrintFormat
11
12
 
12
13
 
13
14
  class NodeInfo(BaseModel):
@@ -57,7 +58,7 @@ async def run(urls: list[str], proxy: str | None, print_format: PrintFormat) ->
57
58
  live_table.stop()
58
59
 
59
60
  if print_format == PrintFormat.JSON:
60
- print_json(data=result)
61
+ mm_print.json(data=result)
61
62
  # print_json(data=result)
62
63
  # table = Table(*["url", "chain_id", "chain_name", "block_number", "base_fee"], title="nodes")
63
64
 
@@ -1,25 +1,25 @@
1
1
  import json
2
2
  from pathlib import Path
3
3
 
4
- from mm_std import PrintFormat, fatal, print_json, print_plain
5
- from mm_std.fs import get_filename_without_extension
4
+ import mm_print
6
5
 
6
+ from mm_eth.cli.cli import PrintFormat
7
7
  from mm_eth.solc import solc
8
8
 
9
9
 
10
10
  def run(contract_path: Path, tmp_dir: Path, print_format: PrintFormat) -> None:
11
- contract_name = get_filename_without_extension(contract_path)
11
+ contract_name = contract_path.stem
12
12
  res = solc(contract_name, contract_path, tmp_dir)
13
13
  if res.is_err():
14
- fatal(res.unwrap_error())
14
+ mm_print.fatal(res.unwrap_err())
15
15
 
16
16
  bin_ = res.unwrap().bin
17
17
  abi = res.unwrap().abi
18
18
 
19
19
  if print_format == PrintFormat.JSON:
20
- print_json({"bin": bin_, "abi": json.loads(abi)})
20
+ mm_print.json({"bin": bin_, "abi": json.loads(abi)})
21
21
  else:
22
- print_plain("bin:")
23
- print_plain(bin_)
24
- print_plain("abi:")
25
- print_plain(abi)
22
+ mm_print.plain("bin:")
23
+ mm_print.plain(bin_)
24
+ mm_print.plain("abi:")
25
+ mm_print.plain(abi)
@@ -4,10 +4,9 @@ import time
4
4
  from pathlib import Path
5
5
  from typing import Annotated, Literal, Self, cast
6
6
 
7
- import mm_crypto_utils
8
7
  from loguru import logger
9
- from mm_crypto_utils import AddressToPrivate, Transfer, VarInt
10
- from mm_std import BaseConfig, utc_now
8
+ from mm_std import utc_now
9
+ from mm_web3 import PrivateKeyMap, Transfer, Web3CliConfig, calc_decimal_expression, init_loguru
11
10
  from pydantic import AfterValidator, BeforeValidator, Field, model_validator
12
11
  from rich.console import Console
13
12
  from rich.live import Live
@@ -20,11 +19,11 @@ from mm_eth.cli.validators import Validators
20
19
  from mm_eth.converters import from_wei
21
20
 
22
21
 
23
- class Config(BaseConfig):
22
+ class Config(Web3CliConfig):
24
23
  nodes: Annotated[list[str], BeforeValidator(Validators.nodes())]
25
24
  chain_id: int
26
25
  transfers: Annotated[list[Transfer], BeforeValidator(Validators.eth_transfers())]
27
- private_keys: Annotated[AddressToPrivate, BeforeValidator(Validators.eth_private_keys())]
26
+ private_keys: Annotated[PrivateKeyMap, BeforeValidator(Validators.eth_private_keys())]
28
27
  token: Annotated[str | None, AfterValidator(Validators.eth_address())] = None # if None, then eth transfer
29
28
  token_decimals: int = -1
30
29
  max_fee: Annotated[str, AfterValidator(Validators.valid_eth_expression("base_fee"))]
@@ -33,7 +32,7 @@ class Config(BaseConfig):
33
32
  default_value: Annotated[str | None, AfterValidator(Validators.valid_eth_or_token_expression("balance"))] = None
34
33
  value_min_limit: Annotated[str | None, AfterValidator(Validators.valid_eth_or_token_expression())] = None
35
34
  gas: Annotated[str, AfterValidator(Validators.valid_eth_expression("estimate"))]
36
- delay: Annotated[str | None, AfterValidator(Validators.valid_calc_decimal_value())] = None # in seconds
35
+ delay: Annotated[str | None, AfterValidator(Validators.decimal_expression())] = None # in seconds
37
36
  round_ndigits: int = 5
38
37
  proxies: Annotated[list[str], Field(default_factory=list), BeforeValidator(Validators.proxies())]
39
38
  wait_tx_timeout: int = 120
@@ -68,7 +67,7 @@ class Config(BaseConfig):
68
67
  Validators.valid_eth_expression()(self.value_min_limit)
69
68
 
70
69
  if self.token:
71
- self.token_decimals = (await retry.erc20_decimals(5, self.nodes, self.proxies, token=self.token)).unwrap_or_exit(
70
+ self.token_decimals = (await retry.erc20_decimals(5, self.nodes, self.proxies, token=self.token)).unwrap(
72
71
  "can't get token decimals"
73
72
  )
74
73
 
@@ -102,13 +101,13 @@ async def run(params: TransferCmdParams) -> None:
102
101
 
103
102
 
104
103
  async def _run_transfers(config: Config, cmd_params: TransferCmdParams) -> None:
105
- mm_crypto_utils.init_logger(cmd_params.debug, config.log_debug, config.log_info)
104
+ init_loguru(cmd_params.debug, config.log_debug, config.log_info)
106
105
  logger.info(f"transfer {cmd_params.config_path}: started at {utc_now()} UTC")
107
106
  logger.debug(f"config={config.model_dump(exclude={'private_keys'}) | {'version': cli_utils.get_version()}}")
108
107
  for i, transfer in enumerate(config.transfers):
109
108
  await _transfer(transfer, config, cmd_params)
110
109
  if config.delay is not None and i < len(config.transfers) - 1:
111
- delay_value = mm_crypto_utils.calc_decimal_value(config.delay)
110
+ delay_value = calc_decimal_expression(config.delay)
112
111
  logger.info(f"delay {delay_value} seconds")
113
112
  if not cmd_params.emulate:
114
113
  await asyncio.sleep(float(delay_value))
@@ -118,7 +117,7 @@ async def _run_transfers(config: Config, cmd_params: TransferCmdParams) -> None:
118
117
  async def _get_nonce(t: Transfer, config: Config) -> int | None:
119
118
  res = await retry.eth_get_transaction_count(5, config.nodes, config.proxies, address=t.from_address)
120
119
  if res.is_err():
121
- logger.error(f"{t.log_prefix}: nonce error: {res.unwrap_error()}")
120
+ logger.error(f"{t.log_prefix}: nonce error: {res.unwrap_err()}")
122
121
  return None
123
122
  logger.debug(f"{t.log_prefix}: nonce={res.unwrap()}")
124
123
  return res.unwrap()
@@ -128,10 +127,10 @@ async def _calc_max_fee(t: Transfer, config: Config) -> int | None:
128
127
  if "base_fee" in config.max_fee.lower():
129
128
  base_fee_res = await retry.get_base_fee_per_gas(5, config.nodes, config.proxies)
130
129
  if base_fee_res.is_err():
131
- logger.error(f"{t.log_prefix}: base_fee error: {base_fee_res.unwrap_error()}")
130
+ logger.error(f"{t.log_prefix}: base_fee error: {base_fee_res.unwrap_err()}")
132
131
  return None
133
132
  logger.debug(f"{t.log_prefix}: base_fee={base_fee_res.unwrap()}")
134
- return calcs.calc_eth_expression(config.max_fee, VarInt("base_fee", base_fee_res.unwrap()))
133
+ return calcs.calc_eth_expression(config.max_fee, {"base_fee": base_fee_res.unwrap()})
135
134
  return calcs.calc_eth_expression(config.max_fee)
136
135
 
137
136
 
@@ -147,7 +146,7 @@ def check_max_fee_limit(t: Transfer, config: Config, max_fee: int) -> bool:
147
146
 
148
147
 
149
148
  async def _calc_gas(t: Transfer, config: Config) -> int | None:
150
- var = None
149
+ variables: dict[str, int] | None = None
151
150
  if "estimate" in config.gas.lower():
152
151
  if config.token:
153
152
  res = await retry.eth_estimate_gas(
@@ -163,25 +162,25 @@ async def _calc_gas(t: Transfer, config: Config) -> int | None:
163
162
  5, config.nodes, config.proxies, from_=t.from_address, to=t.to_address, value=12345
164
163
  )
165
164
  if res.is_err():
166
- logger.error(f"{t.log_prefix}: gas estimate error: {res.unwrap_error()}")
165
+ logger.error(f"{t.log_prefix}: gas estimate error: {res.unwrap_err()}")
167
166
  return None
168
167
  logger.debug(f"{t.log_prefix}: gas estimate={res.unwrap()}")
169
- var = VarInt("estimate", res.unwrap())
170
- return calcs.calc_eth_expression(config.gas, var)
168
+ variables = {"estimate": res.unwrap()}
169
+ return calcs.calc_eth_expression(config.gas, variables)
171
170
 
172
171
 
173
172
  async def _calc_eth_value(t: Transfer, max_fee: int, gas: int, config: Config) -> int | None:
174
173
  value_expression = t.value.lower()
175
- var = None
174
+ variables: dict[str, int] | None = None
176
175
  if "balance" in value_expression:
177
176
  res = await retry.eth_get_balance(5, config.nodes, config.proxies, address=t.from_address)
178
177
  if res.is_err():
179
- logger.error(f"{t.log_prefix}: balance error: {res.unwrap_error()}")
178
+ logger.error(f"{t.log_prefix}: balance error: {res.unwrap_err()}")
180
179
  return None
181
180
  logger.debug(f"{t.log_prefix}: balance={res.unwrap()}")
182
- var = VarInt("balance", res.unwrap())
181
+ variables = {"balance": res.unwrap()}
183
182
 
184
- value = calcs.calc_eth_expression(value_expression, var)
183
+ value = calcs.calc_eth_expression(value_expression, variables)
185
184
  if "balance" in value_expression.lower():
186
185
  value = value - gas * max_fee
187
186
  return value
@@ -189,15 +188,15 @@ async def _calc_eth_value(t: Transfer, max_fee: int, gas: int, config: Config) -
189
188
 
190
189
  async def _calc_token_value(t: Transfer, config: Config) -> int | None:
191
190
  value_expression = t.value.lower()
192
- var = None
191
+ variables: dict[str, int] | None = None
193
192
  if "balance" in value_expression:
194
193
  res = await retry.erc20_balance(5, config.nodes, config.proxies, token=cast(str, config.token), wallet=t.from_address)
195
194
  if res.is_err():
196
- logger.error(f"{t.log_prefix}: balance error: {res.unwrap_error()}")
195
+ logger.error(f"{t.log_prefix}: balance error: {res.unwrap_err()}")
197
196
  return None
198
197
  logger.debug(f"{t.log_prefix}: balance={res.unwrap()}")
199
- var = VarInt("balance", res.unwrap())
200
- return calcs.calc_token_expression(value_expression, config.token_decimals, var)
198
+ variables = {"balance": res.unwrap()}
199
+ return calcs.calc_token_expression(value_expression, config.token_decimals, variables)
201
200
 
202
201
 
203
202
  async def _calc_value(t: Transfer, max_fee: int, gas: int, config: Config) -> int | None:
@@ -321,7 +320,7 @@ async def _send_tx(
321
320
  )
322
321
  res = await retry.eth_send_raw_transaction(5, config.nodes, config.proxies, raw_tx=signed_tx.raw_tx)
323
322
  if res.is_err():
324
- logger.error(f"{transfer.log_prefix}: send tx error={res.unwrap_error()}")
323
+ logger.error(f"{transfer.log_prefix}: send tx error={res.unwrap_err()}")
325
324
  return None
326
325
  logger.debug(f"{transfer.log_prefix}: tx_hash={res.unwrap()}")
327
326
 
@@ -1,7 +1,7 @@
1
1
  from pathlib import Path
2
2
  from typing import Any
3
3
 
4
- from mm_std import print_json
4
+ import mm_print
5
5
 
6
6
  from mm_eth.account import derive_accounts, generate_mnemonic
7
7
 
@@ -19,7 +19,7 @@ def run(mnemonic: str, passphrase: str, words: int, derivation_path: str, limit:
19
19
  if print_path:
20
20
  new_account["path"] = acc.path
21
21
  result["accounts"].append(new_account)
22
- print_json(result)
22
+ mm_print.json(result)
23
23
 
24
24
  if save_file:
25
25
  data = [acc["address"] + "\t" + acc["private"] for acc in result["accounts"]]
@@ -1,4 +1,4 @@
1
- from mm_std import fatal, print_plain
1
+ import mm_print
2
2
 
3
3
  from mm_eth import account
4
4
 
@@ -6,6 +6,6 @@ from mm_eth import account
6
6
  def run(private_key: str) -> None:
7
7
  res = account.private_to_address(private_key)
8
8
  if res.is_ok():
9
- print_plain(res.unwrap())
9
+ mm_print.plain(res.unwrap())
10
10
  else:
11
- fatal(f"invalid private key: '{private_key}'")
11
+ mm_print.fatal(f"invalid private key: '{private_key}'")
mm_eth/cli/rpc_helpers.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
 
3
- from mm_crypto_utils import Nodes, Proxies, VarInt
3
+ from mm_web3 import Nodes, Proxies
4
4
 
5
5
  from mm_eth import retry
6
6
  from mm_eth.cli import calcs
@@ -14,7 +14,7 @@ async def get_nonce_with_logging(
14
14
  res = await retry.eth_get_transaction_count(retries, nodes, proxies, address=address)
15
15
  prefix = log_prefix or address
16
16
  if res.is_err():
17
- logger.error(f"{prefix}: nonce error: {res.unwrap_error()}")
17
+ logger.error(f"{prefix}: nonce error: {res.unwrap_err()}")
18
18
  return None
19
19
  logger.debug(f"{prefix}: nonce={res.unwrap()}")
20
20
  return res.unwrap()
@@ -24,7 +24,7 @@ async def get_base_fee_with_logging(log_prefix: str | None, retries: int, nodes:
24
24
  prefix = get_log_prefix(log_prefix)
25
25
  res = await retry.get_base_fee_per_gas(retries, nodes, proxies)
26
26
  if res.is_err():
27
- logger.error(f"{prefix}base_fee error, {res.unwrap_error()}")
27
+ logger.error(f"{prefix}base_fee error, {res.unwrap_err()}")
28
28
  return None
29
29
 
30
30
  logger.debug(f"{prefix}base_fee={res.unwrap()}")
@@ -38,7 +38,7 @@ async def calc_max_fee_with_logging(
38
38
  base_fee = await get_base_fee_with_logging(log_prefix, retries, nodes, proxies)
39
39
  if base_fee is None:
40
40
  return None
41
- return calcs.calc_eth_expression(max_fee_expression, VarInt("base_fee", base_fee))
41
+ return calcs.calc_eth_expression(max_fee_expression, {"base_fee": base_fee})
42
42
 
43
43
  return calcs.calc_eth_expression(max_fee_expression)
44
44
 
mm_eth/cli/validators.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from collections.abc import Callable
2
2
 
3
3
  import eth_utils
4
- from mm_crypto_utils import AddressToPrivate, ConfigValidators, Transfer
4
+ from mm_web3 import ConfigValidators, PrivateKeyMap, Transfer
5
5
 
6
6
  from mm_eth import account
7
7
 
@@ -18,28 +18,28 @@ def address_from_private(private_key: str) -> str:
18
18
  class Validators(ConfigValidators):
19
19
  @staticmethod
20
20
  def valid_eth_expression(var_name: str | None = None) -> Callable[[str], str]:
21
- return ConfigValidators.valid_calc_int_expression(var_name, SUFFIX_DECIMALS)
21
+ return ConfigValidators.expression_with_vars(var_name, SUFFIX_DECIMALS)
22
22
 
23
23
  @staticmethod
24
24
  def valid_token_expression(var_name: str | None = None) -> Callable[[str], str]:
25
- return ConfigValidators.valid_calc_int_expression(var_name, {"t": 6})
25
+ return ConfigValidators.expression_with_vars(var_name, {"t": 6})
26
26
 
27
27
  @staticmethod
28
28
  def valid_eth_or_token_expression(var_name: str | None = None) -> Callable[[str], str]:
29
- return ConfigValidators.valid_calc_int_expression(var_name, SUFFIX_DECIMALS | {"t": 6})
29
+ return ConfigValidators.expression_with_vars(var_name, SUFFIX_DECIMALS | {"t": 6})
30
30
 
31
31
  @staticmethod
32
32
  def eth_transfers() -> Callable[[str], list[Transfer]]:
33
- return ConfigValidators.transfers(is_address=eth_utils.is_address, to_lower=True)
33
+ return ConfigValidators.transfers(is_address=eth_utils.is_address, lowercase=True)
34
34
 
35
35
  @staticmethod
36
- def eth_private_keys() -> Callable[[str], AddressToPrivate]:
36
+ def eth_private_keys() -> Callable[[str], PrivateKeyMap]:
37
37
  return ConfigValidators.private_keys(address_from_private)
38
38
 
39
39
  @staticmethod
40
40
  def eth_address() -> Callable[[str], str]:
41
- return ConfigValidators.address(eth_utils.is_address, to_lower=True)
41
+ return ConfigValidators.address(eth_utils.is_address, lowercase=True)
42
42
 
43
43
  @staticmethod
44
44
  def eth_addresses(unique: bool) -> Callable[[str], list[str]]:
45
- return ConfigValidators.addresses(unique, to_lower=True, is_address=eth_utils.is_address)
45
+ return ConfigValidators.addresses(unique, lowercase=True, is_address=eth_utils.is_address)
mm_eth/retry.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from typing import Any, Literal
2
2
 
3
3
  from eth_typing import BlockIdentifier
4
- from mm_crypto_utils import Nodes, Proxies, retry_with_node_and_proxy
5
- from mm_std import Result
4
+ from mm_result import Result
5
+ from mm_web3 import Nodes, Proxies, retry_with_node_and_proxy
6
6
  from web3.types import TxReceipt
7
7
 
8
8
  from mm_eth import rpc
mm_eth/rpc.py CHANGED
@@ -9,7 +9,8 @@ import eth_utils
9
9
  import pydash
10
10
  import websockets
11
11
  from eth_typing import BlockIdentifier
12
- from mm_std import Result, http_request
12
+ from mm_http import http_request
13
+ from mm_result import Result
13
14
  from web3.types import TxReceipt
14
15
 
15
16
  TIMEOUT = 7.0
@@ -32,17 +33,17 @@ async def rpc_call(
32
33
  async def _http_call(node: str, data: dict[str, object], timeout: float, proxy: str | None) -> Result[Any]:
33
34
  res = await http_request(node, method="POST", proxy=proxy, timeout=timeout, json=data)
34
35
  if res.is_err():
35
- return res.to_err()
36
+ return res.to_result_err()
36
37
  try:
37
38
  parsed_body = res.parse_json_body()
38
39
  err = parsed_body.get("error", {}).get("message", "")
39
40
  if err:
40
- return res.to_err(f"service_error: {err}")
41
+ return res.to_result_err(f"service_error: {err}")
41
42
  if "result" in parsed_body:
42
- return res.to_ok(parsed_body["result"])
43
- return res.to_ok("unknown_response")
43
+ return res.to_result_ok(parsed_body["result"])
44
+ return res.to_result_err("unknown_response")
44
45
  except Exception as e:
45
- return res.to_err(e)
46
+ return res.to_result_err(e)
46
47
 
47
48
 
48
49
  async def _ws_call(node: str, data: dict[str, object], timeout: float) -> Result[Any]:
@@ -225,7 +226,7 @@ async def ens_name(node: str, address: str, timeout: float = TIMEOUT, proxy: str
225
226
  extra["name_response"] = name_res.to_dict()
226
227
 
227
228
  if name_res.is_err():
228
- return Result.err(name_res.unwrap_error(), extra)
229
+ return Result.err(name_res.unwrap_err(), extra)
229
230
 
230
231
  if name_res.unwrap() == "0x":
231
232
  return Result.ok(None, extra)
@@ -248,7 +249,7 @@ async def ens_name(node: str, address: str, timeout: float = TIMEOUT, proxy: str
248
249
  async def get_base_fee_per_gas(node: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
249
250
  res = await eth_get_block_by_number(node, "latest", False, timeout=timeout, proxy=proxy)
250
251
  if res.is_err():
251
- return Result.err(res.unwrap_error(), res.extra)
252
+ return Result.err(res.unwrap_err(), res.extra)
252
253
  if "baseFeePerGas" in res.unwrap():
253
254
  return res.with_value(int(res.unwrap()["baseFeePerGas"], 16))
254
255
  return Result.err("no_base_fee_per_gas", res.extra)
@@ -257,7 +258,7 @@ async def get_base_fee_per_gas(node: str, timeout: float = TIMEOUT, proxy: str |
257
258
  async def get_tx_status(node: str, tx_hash: str, timeout: float = TIMEOUT, proxy: str | None = None) -> Result[int]:
258
259
  res = await eth_get_transaction_receipt(node, tx_hash, timeout=timeout, proxy=proxy)
259
260
  if res.is_err():
260
- return Result.err(res.unwrap_error(), res.extra)
261
+ return Result.err(res.unwrap_err(), res.extra)
261
262
  status = res.unwrap().get("status")
262
263
  if status is None:
263
264
  return Result.err("no_status", res.extra)
mm_eth/solc.py CHANGED
@@ -4,7 +4,8 @@ import shutil
4
4
  from dataclasses import dataclass
5
5
  from pathlib import Path
6
6
 
7
- from mm_std import Result, run_command
7
+ import mm_std
8
+ from mm_result import Result
8
9
 
9
10
 
10
11
  @dataclass
@@ -31,7 +32,7 @@ def solc(contract_name: str, contract_path: Path, tmp_dir: Path) -> Result[SolcR
31
32
  work_dir_created = True
32
33
 
33
34
  cmd = f"solc -o '{work_dir}' --abi --bin --optimize '{contract_path}'"
34
- result = run_command(cmd)
35
+ result = mm_std.shell(cmd)
35
36
  if result.code != 0:
36
37
  return Result.err(f"solc error: {result.stderr}")
37
38
 
mm_eth/tx.py CHANGED
@@ -6,7 +6,6 @@ import eth_utils
6
6
  import rlp
7
7
  from eth_account import Account
8
8
  from eth_typing import HexStr
9
- from eth_utils import keccak, to_hex
10
9
  from pydantic import BaseModel
11
10
  from rlp.sedes import Binary, big_endian_int, binary
12
11
  from web3 import Web3
@@ -103,7 +102,7 @@ def sign_legacy_tx(
103
102
  tx["data"] = data
104
103
 
105
104
  signed = Account.sign_transaction(tx, private_key)
106
- return SignedTx(tx_hash=to_hex(signed.hash), raw_tx=to_hex(signed.raw_transaction))
105
+ return SignedTx(tx_hash=eth_utils.to_hex(signed.hash), raw_tx=eth_utils.to_hex(signed.raw_transaction))
107
106
 
108
107
 
109
108
  def sign_tx(
@@ -139,7 +138,7 @@ def sign_tx(
139
138
 
140
139
  def decode_raw_tx(raw_tx: str) -> DecodedRawTx:
141
140
  tx: Any = rlp.decode(eth_utils.to_bytes(hexstr=HexStr(raw_tx)), RPLTransaction)
142
- tx_hash = Web3.to_hex(keccak(eth_utils.to_bytes(hexstr=HexStr(raw_tx))))
141
+ tx_hash = Web3.to_hex(eth_utils.keccak(eth_utils.to_bytes(hexstr=HexStr(raw_tx))))
143
142
  from_ = Account.recover_transaction(raw_tx)
144
143
  to = Web3.to_checksum_address(tx.to) if tx.to else None
145
144
  data = Web3.to_hex(tx.data)
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: mm-eth
3
+ Version: 0.7.1
4
+ Requires-Python: >=3.13
5
+ Requires-Dist: mm-web3~=0.5.1
6
+ Requires-Dist: typer~=0.16.0
7
+ Requires-Dist: web3~=7.12.0
@@ -0,0 +1,33 @@
1
+ mm_eth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ mm_eth/abi.py,sha256=SBst1GqcaMSawAyAragAx8tYkCThw0aiMmsWa884wyA,4808
3
+ mm_eth/account.py,sha256=WUaQq8AtHIL39hJN4-UkhDu7UM20j1xlMplCTKQzr1o,3041
4
+ mm_eth/anvil.py,sha256=f7zd5zZqcOTd1Sy4GdHAGTOjsQZzLI3E0ttT017KYLQ,1792
5
+ mm_eth/converters.py,sha256=smL3Bsky1pYEre2kPhsb4arXoQC_u80P5ilU9NRvr44,2043
6
+ mm_eth/deploy.py,sha256=SB3ruY808_5UnG8kHR34uVP66P3zOWZu0ImKD7UUv2s,691
7
+ mm_eth/erc20.py,sha256=Pxs_w95flqEUF4pJMoaHTfvud8x5Fb2UwU7iwMjdGCw,1143
8
+ mm_eth/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ mm_eth/retry.py,sha256=saRYV07Ca1laqT1nQP3_UmKvYXNWMURh8gyN2r1WrIs,5123
10
+ mm_eth/rpc.py,sha256=3I6mfM_-93qFK25SLwv8zUG2vR5EaEUd_YfcBVX8YF0,9917
11
+ mm_eth/solc.py,sha256=s_iaaslncVzzQsgUgcc04x8ZeCjCf9T9JdW1_yzUDxI,1353
12
+ mm_eth/tx.py,sha256=MSJf6zGRYlwCbJIXXLL2NONVpvF1_clWd_5CV5j4pTE,4095
13
+ mm_eth/utils.py,sha256=TLyapZZ1lp_kW3vdkHmgR8gUtIQ1aEy_0GScxB6v5IQ,659
14
+ mm_eth/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ mm_eth/cli/calcs.py,sha256=FwnYwRKDnBdXTYrjdWqilPd36W969ovziO8qkQoHOsw,477
16
+ mm_eth/cli/cli.py,sha256=aElAbK1FiABwMPTU-vysyFIyg6OkBtiir6Zj4LuNmx8,5600
17
+ mm_eth/cli/cli_utils.py,sha256=Py04-0wI5_6Hby0zN8WxYFZf0jL13B1gxjjYTIDrqOE,1536
18
+ mm_eth/cli/rpc_helpers.py,sha256=nmr4CXVMGblGJxllaLd95gTV_VhauRhWUKmQZk5-Vmg,1643
19
+ mm_eth/cli/validators.py,sha256=0T5tQdqEd4fNaamVK8TifUBTCzqLkNdzTA4rZD98_H8,1649
20
+ mm_eth/cli/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ mm_eth/cli/cmd/balance_cmd.py,sha256=Qtn_4h_MDYh38HizkOx6auAv0_41GyCH3bWDmLXHfjQ,1963
22
+ mm_eth/cli/cmd/balances_cmd.py,sha256=I8GgANCx5vuu8Yb5qOrlcKdUc-Yzw1eg9Ucrran5xnQ,4256
23
+ mm_eth/cli/cmd/deploy_cmd.py,sha256=ge-r_GbGc9r2osz-g7zOmqXkbMsFnjeyNPSoq72Ntpg,1479
24
+ mm_eth/cli/cmd/node_cmd.py,sha256=-BBKj9l-ZiPr6NZ12WQvuxQcXvyDBvcbReMMevN3fME,2574
25
+ mm_eth/cli/cmd/solc_cmd.py,sha256=Puu8mw8wvbltEug7SCMZdNT5Y9tD_-awdKGYKeYHUsw,648
26
+ mm_eth/cli/cmd/transfer_cmd.py,sha256=6ciWTPdnu6dnv0H0c641kgar2nG4aJOxl0G0lszxD4M,16978
27
+ mm_eth/cli/cmd/wallet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ mm_eth/cli/cmd/wallet/mnemonic_cmd.py,sha256=RQbNY3C4-5NvcPXorhO7YIYG8dU0u5gN5sEANmFE1iY,996
29
+ mm_eth/cli/cmd/wallet/private_key_cmd.py,sha256=RmSrUxaGd0U7f6Xo_CZuJ8XQG9PbjI3Tc1Iu7wzkP3Q,262
30
+ mm_eth-0.7.1.dist-info/METADATA,sha256=mqbTSkJIF14yJ9FAJokEFQMkJykqHSrqwidE8NyEppU,161
31
+ mm_eth-0.7.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
32
+ mm_eth-0.7.1.dist-info/entry_points.txt,sha256=aGhpsozl8NIrkuUcX5fSgURCcDhr3ShUdeTSIrJq4oc,46
33
+ mm_eth-0.7.1.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: mm-eth
3
- Version: 0.6.2
4
- Requires-Python: >=3.12
5
- Requires-Dist: aiohttp-socks~=0.10.1
6
- Requires-Dist: mm-crypto-utils>=0.3.6
7
- Requires-Dist: mm-std~=0.4.11
8
- Requires-Dist: typer>=0.15.2
9
- Requires-Dist: web3~=7.10.0
10
- Requires-Dist: websocket-client~=1.8.0
@@ -1,33 +0,0 @@
1
- mm_eth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- mm_eth/abi.py,sha256=gsIsaq5V8QGdYY424c_t_uVRfg0of7Gk9h0LcW3tkjU,4806
3
- mm_eth/account.py,sha256=syA89ihOBTA2FiDK3o55oLBVEgPN1rQT_3bUQN05vuk,3044
4
- mm_eth/anvil.py,sha256=NY1TxYnamfm_47HqCbgUCRdQxPgmAcJ-9YkK3NZ0310,1631
5
- mm_eth/converters.py,sha256=smL3Bsky1pYEre2kPhsb4arXoQC_u80P5ilU9NRvr44,2043
6
- mm_eth/deploy.py,sha256=SB3ruY808_5UnG8kHR34uVP66P3zOWZu0ImKD7UUv2s,691
7
- mm_eth/erc20.py,sha256=Pxs_w95flqEUF4pJMoaHTfvud8x5Fb2UwU7iwMjdGCw,1143
8
- mm_eth/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- mm_eth/retry.py,sha256=tYP7B_lah8hxYahweaGAAhWCJl-pQ0pg_2Lk-432luE,5128
10
- mm_eth/rpc.py,sha256=sFt9vK6rHktNTAIxgKx9CSCANx9Z7tTp165yK_G3uRs,9865
11
- mm_eth/solc.py,sha256=2FGCJpbkCwIs8hGc8APpvC62gyAXGLeyQMrn4f9Bnow,1348
12
- mm_eth/tx.py,sha256=zf5fieuKB-CTOiNNjz3siXU7yga7tlh0vkZTDAHvhnQ,4102
13
- mm_eth/utils.py,sha256=TLyapZZ1lp_kW3vdkHmgR8gUtIQ1aEy_0GScxB6v5IQ,659
14
- mm_eth/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- mm_eth/cli/calcs.py,sha256=N063Qw85iBNFbDtIjOSYJKHL0vU6wyfRfYAlI9TKeK0,496
16
- mm_eth/cli/cli.py,sha256=Qac0HizKAyF1wocVvNK2578ntB2JT9kLHr1U5jfQj6E,5580
17
- mm_eth/cli/cli_utils.py,sha256=jYDuSo-osvKPkd_OJFCHZDfGlFy3LKs-MVKHEnkCPdM,1416
18
- mm_eth/cli/rpc_helpers.py,sha256=5fxiwFb16V0udY2MOb3bTpUrkf3Lhn-mMXFrdbmAKo4,1669
19
- mm_eth/cli/validators.py,sha256=74AxIzryQ-D3f5xJYrOnh4AMFugTpsYhUJ_pzhHd2ek,1675
20
- mm_eth/cli/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- mm_eth/cli/cmd/balance_cmd.py,sha256=hujMv4Fvt4ZMZ3iPOZKdI2u2iN-ClYBmfMn_2Y4lRew,1946
22
- mm_eth/cli/cmd/balances_cmd.py,sha256=VGCqcPCKPFF2O3WNlryg0nFnCkkKRAaubvpbtH0XTV0,4230
23
- mm_eth/cli/cmd/deploy_cmd.py,sha256=T0VapUVOA6M5Hska04AObBKk5_91lClX5hARkj1Dm3k,1465
24
- mm_eth/cli/cmd/node_cmd.py,sha256=JJSiKGG5OL9p7FuHhNeerjfes6Eo_StgLgpZEy3EUU0,2559
25
- mm_eth/cli/cmd/solc_cmd.py,sha256=HDLThGAxk8MlJh_OBTsivoXYCKDlGnljRFp-BbYDlU0,714
26
- mm_eth/cli/cmd/transfer_cmd.py,sha256=kKNoU_kNl2blz9ydbvhlEY1Kck0fCMv4RKwU84HyOSw,16933
27
- mm_eth/cli/cmd/wallet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- mm_eth/cli/cmd/wallet/mnemonic_cmd.py,sha256=xE-5Ux9BdYsTZYBy0dMn9jupGhW4ced-AgYscy_wU_4,1007
29
- mm_eth/cli/cmd/wallet/private_key_cmd.py,sha256=wuW_LvmIZiT9T4R8ir0wH1VQ_CEPjmrLgS-vzf0yv70,272
30
- mm_eth-0.6.2.dist-info/METADATA,sha256=etjH_lZpyb7tlF0LawFlfKZ7EhCGuiE9rFj2y6QXzyE,275
31
- mm_eth-0.6.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
32
- mm_eth-0.6.2.dist-info/entry_points.txt,sha256=aGhpsozl8NIrkuUcX5fSgURCcDhr3ShUdeTSIrJq4oc,46
33
- mm_eth-0.6.2.dist-info/RECORD,,
File without changes