mm-sol 0.6.3__tar.gz → 0.7.1__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 (51) hide show
  1. mm_sol-0.7.1/PKG-INFO +11 -0
  2. {mm_sol-0.6.3 → mm_sol-0.7.1}/justfile +4 -2
  3. {mm_sol-0.6.3 → mm_sol-0.7.1}/pyproject.toml +11 -12
  4. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/calcs.py +14 -14
  5. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cli.py +2 -2
  6. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cli_utils.py +6 -7
  7. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cmd/balance_cmd.py +8 -8
  8. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cmd/balances_cmd.py +5 -5
  9. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cmd/example_cmd.py +2 -2
  10. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cmd/node_cmd.py +2 -2
  11. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cmd/transfer_cmd.py +15 -11
  12. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cmd/wallet/keypair_cmd.py +2 -2
  13. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cmd/wallet/mnemonic_cmd.py +2 -2
  14. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/validators.py +8 -6
  15. mm_sol-0.7.1/src/mm_sol/constants.py +1 -0
  16. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/retry.py +2 -2
  17. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/rpc.py +7 -6
  18. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/rpc_sync.py +7 -6
  19. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/spl_token.py +1 -1
  20. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/transfer.py +1 -1
  21. mm_sol-0.7.1/tests/conftest.py +125 -0
  22. {mm_sol-0.6.3 → mm_sol-0.7.1}/uv.lock +145 -350
  23. mm_sol-0.6.3/PKG-INFO +0 -13
  24. mm_sol-0.6.3/src/mm_sol/constants.py +0 -1
  25. mm_sol-0.6.3/tests/conftest.py +0 -91
  26. {mm_sol-0.6.3 → mm_sol-0.7.1}/.env.example +0 -0
  27. {mm_sol-0.6.3 → mm_sol-0.7.1}/.gitignore +0 -0
  28. {mm_sol-0.6.3 → mm_sol-0.7.1}/README.md +0 -0
  29. {mm_sol-0.6.3 → mm_sol-0.7.1}/dict.dic +0 -0
  30. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/__init__.py +0 -0
  31. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/account.py +0 -0
  32. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/__init__.py +0 -0
  33. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cmd/__init__.py +0 -0
  34. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/cmd/wallet/__init__.py +0 -0
  35. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/examples/balances.toml +0 -0
  36. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/cli/examples/transfer.toml +0 -0
  37. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/converters.py +0 -0
  38. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/py.typed +0 -0
  39. {mm_sol-0.6.3 → mm_sol-0.7.1}/src/mm_sol/utils.py +0 -0
  40. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/__init__.py +0 -0
  41. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/cli/__init__.py +0 -0
  42. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/cli/cmd/__init__.py +0 -0
  43. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/cli/cmd/wallet/__init__.py +0 -0
  44. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/cli/cmd/wallet/test_keypair_cmd.py +0 -0
  45. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/cli/cmd/wallet/test_mnemonic_cmd.py +0 -0
  46. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/test_account.py +0 -0
  47. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/test_client.py +0 -0
  48. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/test_converters.py +0 -0
  49. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/test_rpc.py +0 -0
  50. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/test_rpc_sync.py +0 -0
  51. {mm_sol-0.6.3 → mm_sol-0.7.1}/tests/test_spl_token.py +0 -0
mm_sol-0.7.1/PKG-INFO ADDED
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: mm-sol
3
+ Version: 0.7.1
4
+ Requires-Python: >=3.13
5
+ Requires-Dist: base58~=2.1.1
6
+ Requires-Dist: jinja2>=3.1.6
7
+ Requires-Dist: mm-web3~=0.5.1
8
+ Requires-Dist: mnemonic==0.21
9
+ Requires-Dist: socksio>=1.0.0
10
+ Requires-Dist: solana~=0.36.7
11
+ Requires-Dist: typer>=0.16.0
@@ -23,8 +23,10 @@ lint-fix: format
23
23
  uv run ruff check --fix src tests
24
24
 
25
25
  audit:
26
- uv run pip-audit
27
- uv run bandit -r -c "pyproject.toml" src
26
+ uv export --no-dev --all-extras --format requirements-txt --no-emit-project > requirements.txt
27
+ uv run pip-audit -r requirements.txt --disable-pip
28
+ rm requirements.txt
29
+ uv run bandit --silent --recursive --configfile "pyproject.toml" src
28
30
 
29
31
  publish: build
30
32
  git diff-index --quiet HEAD
@@ -1,18 +1,16 @@
1
1
  [project]
2
2
  name = "mm-sol"
3
- version = "0.6.3"
3
+ version = "0.7.1"
4
4
  description = ""
5
- requires-python = ">=3.12"
5
+ requires-python = ">=3.13"
6
6
  dependencies = [
7
- "mm-crypto-utils>=0.3.7",
8
- "solana~=0.36.6",
9
- "solders~=0.26.0",
7
+ "mm-web3~=0.5.1",
8
+ "solana~=0.36.7",
10
9
  "base58~=2.1.1",
11
10
  "mnemonic==0.21",
12
- "typer>=0.15.3",
11
+ "typer>=0.16.0",
13
12
  "jinja2>=3.1.6",
14
13
  "socksio>=1.0.0",
15
- "h11>=0.16.0", # secure fix, remove when solana updated
16
14
  ]
17
15
  [project.scripts]
18
16
  mm-sol = "mm_sol.cli.cli:app"
@@ -23,13 +21,14 @@ build-backend = "hatchling.build"
23
21
 
24
22
  [tool.uv]
25
23
  dev-dependencies = [
26
- "pytest~=8.3.5",
27
- "pytest-xdist~=3.6.1",
28
- "ruff~=0.11.9",
24
+ "pytest~=8.4.0",
25
+ "pytest-asyncio~=1.0.0",
26
+ "pytest-xdist~=3.7.0",
27
+ "ruff~=0.11.13",
29
28
  "pip-audit~=2.9.0",
30
29
  "bandit~=1.8.3",
31
- "mypy~=1.15.0",
32
- "pytest-asyncio~=0.26.0",
30
+ "mypy~=1.16.0",
31
+ "python-dotenv>=1.1.0",
33
32
  ]
34
33
 
35
34
  [tool.mypy]
@@ -1,31 +1,31 @@
1
- import mm_crypto_utils
2
- from mm_crypto_utils import Nodes, Proxies, VarInt
3
- from mm_std import Result
1
+ from mm_result import Result
2
+ from mm_web3 import Nodes, Proxies
3
+ from mm_web3.calcs import calc_expression_with_vars
4
4
 
5
5
  from mm_sol import retry
6
- from mm_sol.constants import SUFFIX_DECIMALS
6
+ from mm_sol.constants import UNIT_DECIMALS
7
7
 
8
8
 
9
- def calc_sol_expression(expression: str, var: VarInt | None = None) -> int:
10
- return mm_crypto_utils.calc_int_expression(expression, var=var, suffix_decimals=SUFFIX_DECIMALS)
9
+ def calc_sol_expression(expression: str, variables: dict[str, int] | None = None) -> int:
10
+ return calc_expression_with_vars(expression, variables, unit_decimals=UNIT_DECIMALS)
11
11
 
12
12
 
13
- def calc_token_expression(expression: str, token_decimals: int, var: VarInt | None = None) -> int:
14
- return mm_crypto_utils.calc_int_expression(expression, var=var, suffix_decimals={"t": token_decimals})
13
+ def calc_token_expression(expression: str, token_decimals: int, variables: dict[str, int] | None = None) -> int:
14
+ return calc_expression_with_vars(expression, variables, unit_decimals={"t": token_decimals})
15
15
 
16
16
 
17
17
  async def calc_sol_value_for_address(
18
18
  *, nodes: Nodes, value_expression: str, address: str, proxies: Proxies, fee: int
19
19
  ) -> Result[int]:
20
20
  value_expression = value_expression.lower()
21
- var = None
21
+ variables: dict[str, int] | None = None
22
22
  if "balance" in value_expression:
23
23
  res = await retry.get_sol_balance(5, nodes, proxies, address=address)
24
24
  if res.is_err():
25
25
  return res
26
- var = VarInt("balance", res.unwrap())
26
+ variables = {"balance": res.unwrap()}
27
27
 
28
- value = calc_sol_expression(value_expression, var)
28
+ value = calc_sol_expression(value_expression, variables)
29
29
  if "balance" in value_expression:
30
30
  value = value - fee
31
31
  return Result.ok(value)
@@ -34,12 +34,12 @@ async def calc_sol_value_for_address(
34
34
  async def calc_token_value_for_address(
35
35
  *, nodes: Nodes, value_expression: str, owner: str, token: str, token_decimals: int, proxies: Proxies
36
36
  ) -> Result[int]:
37
- var = None
37
+ variables: dict[str, int] | None = None
38
38
  value_expression = value_expression.lower()
39
39
  if "balance" in value_expression:
40
40
  res = await retry.get_token_balance(5, nodes, proxies, owner=owner, token=token)
41
41
  if res.is_err():
42
42
  return res
43
- var = VarInt("balance", res.unwrap())
44
- value = calc_token_expression(value_expression, token_decimals, var)
43
+ variables = {"balance": res.unwrap()}
44
+ value = calc_token_expression(value_expression, token_decimals, variables)
45
45
  return Result.ok(value)
@@ -3,8 +3,8 @@ from enum import Enum
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 print_plain
8
8
 
9
9
  from mm_sol.account import PHANTOM_DERIVATION_PATH
10
10
 
@@ -24,7 +24,7 @@ app.add_typer(wallet_app, name="w", hidden=True)
24
24
 
25
25
  def version_callback(value: bool) -> None:
26
26
  if value:
27
- print_plain(f"mm-sol: {cli_utils.get_version()}")
27
+ mm_print.plain(f"mm-sol: {cli_utils.get_version()}")
28
28
  raise typer.Exit
29
29
 
30
30
 
@@ -2,10 +2,9 @@ import importlib.metadata
2
2
  import time
3
3
  from pathlib import Path
4
4
 
5
- import mm_crypto_utils
5
+ import mm_print
6
6
  from loguru import logger
7
- from mm_crypto_utils import Nodes, Proxies
8
- from mm_std import BaseConfig, print_json
7
+ from mm_web3 import Nodes, Proxies, Web3CliConfig, random_node, random_proxy
9
8
  from pydantic import BaseModel
10
9
  from solders.signature import Signature
11
10
 
@@ -21,12 +20,12 @@ class BaseConfigParams(BaseModel):
21
20
  print_config_and_exit: bool
22
21
 
23
22
 
24
- def print_config(config: BaseConfig, exclude: set[str] | None = None, count: set[str] | None = None) -> None:
23
+ def print_config(config: Web3CliConfig, exclude: set[str] | None = None, count: set[str] | None = None) -> None:
25
24
  data = config.model_dump(exclude=exclude)
26
25
  if count:
27
26
  for k in count:
28
27
  data[k] = len(data[k])
29
- print_json(data)
28
+ mm_print.json(data)
30
29
 
31
30
 
32
31
  def public_rpc_url(url: str | None) -> str:
@@ -48,8 +47,8 @@ def wait_confirmation(nodes: Nodes, proxies: Proxies, signature: Signature, log_
48
47
  count = 0
49
48
  while True:
50
49
  try:
51
- node = mm_crypto_utils.random_node(nodes)
52
- proxy = mm_crypto_utils.random_proxy(proxies)
50
+ node = random_node(nodes)
51
+ proxy = random_proxy(proxies)
53
52
  client = get_client(node, proxy=proxy)
54
53
  res = client.get_transaction(signature)
55
54
  if res.value and res.value.slot: # check for tx error
@@ -1,7 +1,7 @@
1
1
  from decimal import Decimal
2
2
 
3
- import mm_crypto_utils
4
- from mm_std import print_json
3
+ import mm_print
4
+ from mm_web3 import fetch_proxies
5
5
  from pydantic import BaseModel, Field
6
6
 
7
7
  import mm_sol.retry
@@ -43,14 +43,14 @@ async def run(
43
43
 
44
44
  rpc_url = cli_utils.public_rpc_url(rpc_url)
45
45
 
46
- proxies = await mm_crypto_utils.fetch_proxies_or_fatal(proxies_url) if proxies_url else None
46
+ proxies = (await fetch_proxies(proxies_url)).unwrap() if proxies_url else None
47
47
 
48
48
  # sol balance
49
49
  sol_balance_res = await retry.get_sol_balance(3, rpc_url, proxies, address=wallet_address)
50
50
  if sol_balance_res.is_ok():
51
51
  result.sol_balance = sol_balance_res.unwrap()
52
52
  else:
53
- result.errors.append("sol_balance: " + sol_balance_res.unwrap_error())
53
+ result.errors.append("sol_balance: " + sol_balance_res.unwrap_err())
54
54
 
55
55
  # token balance
56
56
  if token_address:
@@ -59,15 +59,15 @@ async def run(
59
59
  if token_balance_res.is_ok():
60
60
  result.token_balance = token_balance_res.unwrap()
61
61
  else:
62
- result.errors.append("token_balance: " + token_balance_res.unwrap_error())
62
+ result.errors.append("token_balance: " + token_balance_res.unwrap_err())
63
63
 
64
64
  decimals_res = await mm_sol.retry.get_token_decimals(3, rpc_url, proxies, token=token_address)
65
65
  if decimals_res.is_ok():
66
66
  result.token_decimals = decimals_res.unwrap()
67
67
  else:
68
- result.errors.append("token_decimals: " + decimals_res.unwrap_error())
68
+ result.errors.append("token_decimals: " + decimals_res.unwrap_err())
69
69
 
70
70
  if lamport:
71
- print_json(result)
71
+ mm_print.json(result)
72
72
  else:
73
- print_json(result.to_human_readable())
73
+ mm_print.json(result.to_human_readable())
@@ -3,8 +3,8 @@ from decimal import Decimal
3
3
  from pathlib import Path
4
4
  from typing import Annotated, Any
5
5
 
6
- from mm_crypto_utils import ConfigValidators
7
- from mm_std import BaseConfig, fatal, print_json
6
+ import mm_print
7
+ from mm_web3 import ConfigValidators, Web3CliConfig
8
8
  from pydantic import BeforeValidator, Field
9
9
 
10
10
  import mm_sol.retry
@@ -12,7 +12,7 @@ from mm_sol import converters, retry
12
12
  from mm_sol.cli.validators import Validators
13
13
 
14
14
 
15
- class Config(BaseConfig):
15
+ class Config(Web3CliConfig):
16
16
  accounts: Annotated[list[str], BeforeValidator(Validators.sol_addresses(unique=True))]
17
17
  tokens: Annotated[list[str], BeforeValidator(Validators.sol_addresses(unique=True))]
18
18
  nodes: Annotated[list[str], BeforeValidator(ConfigValidators.nodes())]
@@ -35,14 +35,14 @@ async def run(config_path: Path, print_config: bool) -> None:
35
35
  for token_address in config.tokens:
36
36
  res = await mm_sol.retry.get_token_decimals(3, config.nodes, config.proxies, token=token_address)
37
37
  if res.is_err():
38
- fatal(f"Failed to get decimals for token {token_address}: {res.unwrap_error()}")
38
+ mm_print.fatal(f"Failed to get decimals for token {token_address}: {res.unwrap_err()}")
39
39
 
40
40
  token_decimals = res.unwrap()
41
41
  result[token_address] = await _get_token_balances(token_address, token_decimals, config.accounts, config)
42
42
  result[token_address + "_decimals"] = token_decimals
43
43
  result[token_address + "_sum"] = sum([v for v in result[token_address].values() if v is not None])
44
44
 
45
- print_json(result)
45
+ mm_print.json(result)
46
46
 
47
47
 
48
48
  async def _get_token_balances(
@@ -1,8 +1,8 @@
1
1
  from pathlib import Path
2
2
 
3
- from mm_std import pretty_print_toml
3
+ import mm_print
4
4
 
5
5
 
6
6
  def run(module: str) -> None:
7
7
  example_file = Path(Path(__file__).parent.absolute(), "../examples", f"{module}.toml")
8
- pretty_print_toml(example_file.read_text())
8
+ mm_print.toml(toml=example_file.read_text())
@@ -1,4 +1,4 @@
1
- from mm_std import print_json
1
+ import mm_print
2
2
 
3
3
  from mm_sol import rpc
4
4
  from mm_sol.cli import cli_utils
@@ -8,4 +8,4 @@ async def run(urls: list[str], proxy: str | None) -> None:
8
8
  result = {}
9
9
  for url in [cli_utils.public_rpc_url(u) for u in urls]:
10
10
  result[url] = (await rpc.get_block_height(url, proxy=proxy, timeout=10)).value_or_error()
11
- print_json(data=result)
11
+ mm_print.json(data=result)
@@ -3,10 +3,14 @@ import sys
3
3
  from pathlib import Path
4
4
  from typing import Annotated
5
5
 
6
- import mm_crypto_utils
6
+ import mm_print
7
7
  from loguru import logger
8
- from mm_crypto_utils import AddressToPrivate, Transfer
9
- from mm_std import BaseConfig, fatal, utc_now
8
+ from mm_std import utc_now
9
+ from mm_web3 import Web3CliConfig
10
+ from mm_web3.account import PrivateKeyMap
11
+ from mm_web3.calcs import calc_decimal_expression
12
+ from mm_web3.log import init_loguru
13
+ from mm_web3.validators import Transfer
10
14
  from pydantic import AfterValidator, BeforeValidator, Field, model_validator
11
15
  from rich.console import Console
12
16
  from rich.live import Live
@@ -21,16 +25,16 @@ from mm_sol.cli.validators import Validators
21
25
  from mm_sol.converters import lamports_to_sol, to_token
22
26
 
23
27
 
24
- class Config(BaseConfig):
28
+ class Config(Web3CliConfig):
25
29
  nodes: Annotated[list[str], BeforeValidator(Validators.nodes())]
26
30
  transfers: Annotated[list[Transfer], BeforeValidator(Validators.sol_transfers())]
27
- private_keys: Annotated[AddressToPrivate, BeforeValidator(Validators.sol_private_keys())]
31
+ private_keys: Annotated[PrivateKeyMap, BeforeValidator(Validators.sol_private_keys())]
28
32
  proxies: Annotated[list[str], Field(default_factory=list), BeforeValidator(Validators.proxies())]
29
33
  token: Annotated[str | None, AfterValidator(Validators.sol_address())] = None
30
34
  token_decimals: int = -1
31
35
  default_value: Annotated[str | None, AfterValidator(Validators.valid_sol_or_token_expression("balance"))] = None
32
36
  value_min_limit: Annotated[str | None, AfterValidator(Validators.valid_sol_or_token_expression())] = None
33
- delay: Annotated[str | None, AfterValidator(Validators.valid_calc_decimal_value())] = None # in seconds
37
+ delay: Annotated[str | None, AfterValidator(Validators.decimal_expression())] = None # in seconds
34
38
  round_ndigits: int = 5
35
39
  log_debug: Annotated[Path | None, BeforeValidator(Validators.log_file())] = None
36
40
  log_info: Annotated[Path | None, BeforeValidator(Validators.log_file())] = None
@@ -65,7 +69,7 @@ class Config(BaseConfig):
65
69
  if self.token:
66
70
  res = await mm_sol.retry.get_token_decimals(3, self.nodes, self.proxies, token=self.token)
67
71
  if res.is_err():
68
- fatal(f"can't get decimals for token={self.token}, error={res.unwrap_error()}")
72
+ mm_print.fatal(f"can't get decimals for token={self.token}, error={res.unwrap_err()}")
69
73
  self.token_decimals = res.unwrap()
70
74
 
71
75
  return self
@@ -99,13 +103,13 @@ async def run(cmd_params: TransferCmdParams) -> None:
99
103
 
100
104
 
101
105
  async def _run_transfers(config: Config, cmd_params: TransferCmdParams) -> None:
102
- mm_crypto_utils.init_logger(cmd_params.debug, config.log_debug, config.log_info)
106
+ init_loguru(cmd_params.debug, config.log_debug, config.log_info)
103
107
  logger.info(f"transfer {cmd_params.config_path}: started at {utc_now()} UTC")
104
108
  logger.debug(f"config={config.model_dump(exclude={'private_keys'}) | {'version': cli_utils.get_version()}}")
105
109
  for i, route in enumerate(config.transfers):
106
110
  await _transfer(route, config, cmd_params)
107
111
  if config.delay is not None and i < len(config.transfers) - 1:
108
- delay_value = mm_crypto_utils.calc_decimal_value(config.delay)
112
+ delay_value = calc_decimal_expression(config.delay)
109
113
  logger.info(f"delay {delay_value} seconds")
110
114
  if not cmd_params.emulate:
111
115
  await asyncio.sleep(float(delay_value))
@@ -132,7 +136,7 @@ async def _calc_value(transfer: Transfer, config: Config, transfer_sol_fee: int)
132
136
  )
133
137
  logger.debug(f"{transfer.log_prefix}: value={value_res.value_or_error()}")
134
138
  if value_res.is_err():
135
- logger.info(f"{transfer.log_prefix}: calc value error, {value_res.unwrap_error()}")
139
+ logger.info(f"{transfer.log_prefix}: calc value error, {value_res.unwrap_err()}")
136
140
 
137
141
  return value_res.value
138
142
 
@@ -181,7 +185,7 @@ async def _send_tx(transfer: Transfer, value: int, config: Config) -> Signature
181
185
  )
182
186
 
183
187
  if res.is_err():
184
- logger.info(f"{transfer.log_prefix}: tx error {res.unwrap_error()}")
188
+ logger.info(f"{transfer.log_prefix}: tx error {res.unwrap_err()}")
185
189
  return None
186
190
  return res.value
187
191
 
@@ -1,6 +1,6 @@
1
1
  from pathlib import Path
2
2
 
3
- from mm_std import print_json
3
+ import mm_print
4
4
 
5
5
  from mm_sol.account import (
6
6
  get_private_key_arr_str,
@@ -16,4 +16,4 @@ def run(private_key: str) -> None:
16
16
  public = get_public_key(private_key)
17
17
  private_base58 = get_private_key_base58(private_key)
18
18
  private_arr = get_private_key_arr_str(private_key)
19
- print_json({"public": public, "private_base58": private_base58, "private_arr": private_arr})
19
+ mm_print.json({"public": public, "private_base58": private_base58, "private_arr": private_arr})
@@ -1,7 +1,7 @@
1
1
  from dataclasses import asdict
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_sol.account import derive_accounts, generate_mnemonic
7
7
 
@@ -16,4 +16,4 @@ def run(mnemonic: str, passphrase: str, words: int, derivation_path: str, limit:
16
16
  accounts = derive_accounts(mnemonic=mnemonic, passphrase=passphrase, derivation_path=derivation_path, limit=limit)
17
17
 
18
18
  result["accounts"] = [asdict(acc) for acc in accounts]
19
- print_json(result)
19
+ mm_print.json(result)
@@ -1,9 +1,11 @@
1
1
  from collections.abc import Callable
2
2
 
3
- from mm_crypto_utils import AddressToPrivate, ConfigValidators, Transfer
3
+ from mm_web3 import ConfigValidators
4
+ from mm_web3.account import PrivateKeyMap
5
+ from mm_web3.validators import Transfer
4
6
 
5
7
  from mm_sol.account import get_public_key, is_address
6
- from mm_sol.constants import SUFFIX_DECIMALS
8
+ from mm_sol.constants import UNIT_DECIMALS
7
9
 
8
10
 
9
11
  class Validators(ConfigValidators):
@@ -20,17 +22,17 @@ class Validators(ConfigValidators):
20
22
  return ConfigValidators.transfers(is_address)
21
23
 
22
24
  @staticmethod
23
- def sol_private_keys() -> Callable[[str], AddressToPrivate]:
25
+ def sol_private_keys() -> Callable[[str], PrivateKeyMap]:
24
26
  return ConfigValidators.private_keys(get_public_key)
25
27
 
26
28
  @staticmethod
27
29
  def valid_sol_expression(var_name: str | None = None) -> Callable[[str], str]:
28
- return ConfigValidators.valid_calc_int_expression(var_name, SUFFIX_DECIMALS)
30
+ return ConfigValidators.expression_with_vars(var_name, UNIT_DECIMALS)
29
31
 
30
32
  @staticmethod
31
33
  def valid_token_expression(var_name: str | None = None) -> Callable[[str], str]:
32
- return ConfigValidators.valid_calc_int_expression(var_name, {"t": 6})
34
+ return ConfigValidators.expression_with_vars(var_name, {"t": 6})
33
35
 
34
36
  @staticmethod
35
37
  def valid_sol_or_token_expression(var_name: str | None = None) -> Callable[[str], str]:
36
- return ConfigValidators.valid_calc_int_expression(var_name, SUFFIX_DECIMALS | {"t": 6})
38
+ return ConfigValidators.expression_with_vars(var_name, UNIT_DECIMALS | {"t": 6})
@@ -0,0 +1 @@
1
+ UNIT_DECIMALS = {"sol": 9}
@@ -1,5 +1,5 @@
1
- from mm_crypto_utils import Nodes, Proxies, retry_with_node_and_proxy
2
- from mm_std import Result
1
+ from mm_result import Result
2
+ from mm_web3 import Nodes, Proxies, retry_with_node_and_proxy
3
3
  from solders.solders import Pubkey, Signature
4
4
 
5
5
  from mm_sol import rpc, spl_token, transfer
@@ -3,7 +3,8 @@ from collections.abc import Sequence
3
3
  from typing import Any
4
4
 
5
5
  import websockets
6
- from mm_std import Result, http_request
6
+ from mm_http import http_request
7
+ from mm_result import Result
7
8
 
8
9
 
9
10
  async def rpc_call(
@@ -23,17 +24,17 @@ async def rpc_call(
23
24
  async def _http_call(node: str, data: dict[str, object], timeout: float, proxy: str | None) -> Result[Any]:
24
25
  res = await http_request(node, method="POST", proxy=proxy, timeout=timeout, json=data)
25
26
  if res.is_err():
26
- return res.to_err()
27
+ return res.to_result_err()
27
28
  try:
28
29
  parsed_body = res.parse_json_body()
29
30
  err = parsed_body.get("error", {}).get("message", "")
30
31
  if err:
31
- return res.to_err(f"service_error: {err}")
32
+ return res.to_result_err(f"service_error: {err}")
32
33
  if "result" in parsed_body:
33
- return res.to_ok(parsed_body["result"])
34
- return res.to_err("unknown_response")
34
+ return res.to_result_ok(parsed_body["result"])
35
+ return res.to_result_err("unknown_response")
35
36
  except Exception as e:
36
- return res.to_err(e)
37
+ return res.to_result_err(e)
37
38
 
38
39
 
39
40
  async def _ws_call(node: str, data: dict[str, object], timeout: float) -> Result[Any]:
@@ -1,7 +1,8 @@
1
1
  from typing import Any
2
2
 
3
3
  import pydash
4
- from mm_std import Result, http_request_sync
4
+ from mm_http import http_request_sync
5
+ from mm_result import Result
5
6
  from pydantic import BaseModel, ConfigDict, Field
6
7
 
7
8
  DEFAULT_MAINNET_RPC = "https://api.mainnet-beta.solana.com"
@@ -92,18 +93,18 @@ def _http_call(node: str, data: dict[str, object], timeout: float, proxy: str |
92
93
  res = http_request_sync(node, method="POST", proxy=proxy, timeout=timeout, json=data)
93
94
  try:
94
95
  if res.is_err():
95
- return res.to_err()
96
+ return res.to_result_err()
96
97
 
97
98
  json_body = res.parse_json_body()
98
99
  err = pydash.get(json_body, "error.message")
99
100
  if err:
100
- return res.to_err(f"service_error: {err}")
101
+ return res.to_result_err(f"service_error: {err}")
101
102
  if "result" in json_body:
102
- return res.to_ok(json_body["result"])
103
+ return res.to_result_ok(json_body["result"])
103
104
 
104
- return res.to_err("unknown_response")
105
+ return res.to_result_err("unknown_response")
105
106
  except Exception as e:
106
- return res.to_err(e)
107
+ return res.to_result_err(e)
107
108
 
108
109
 
109
110
  def get_balance(node: str, address: str, timeout: float = 5, proxy: str | None = None) -> Result[int]:
@@ -1,4 +1,4 @@
1
- from mm_std import Result
1
+ from mm_result import Result
2
2
  from solana.exceptions import SolanaRpcException
3
3
  from solana.rpc.core import RPCException
4
4
  from solders.solders import InvalidParamsMessage, Pubkey, get_associated_token_address
@@ -1,5 +1,5 @@
1
1
  import pydash
2
- from mm_std import Result
2
+ from mm_result import Result
3
3
  from pydantic import BaseModel
4
4
  from solders.message import Message
5
5
  from solders.pubkey import Pubkey
@@ -0,0 +1,125 @@
1
+ import os
2
+
3
+ import mm_web3
4
+ import pytest
5
+ from dotenv import load_dotenv
6
+ from mm_web3 import fetch_proxies_sync
7
+ from typer.testing import CliRunner
8
+
9
+ load_dotenv()
10
+
11
+
12
+ @pytest.fixture
13
+ def anyio_backend():
14
+ return "asyncio"
15
+
16
+
17
+ @pytest.fixture
18
+ def devnet_address_1() -> str:
19
+ value = os.getenv("DEVNET_ADDRESS_1")
20
+ if value is None:
21
+ pytest.skip("DEVNET_ADDRESS_1 environment variable not set")
22
+ return value
23
+
24
+
25
+ @pytest.fixture
26
+ def devnet_address_2() -> str:
27
+ value = os.getenv("DEVNET_ADDRESS_2")
28
+ if value is None:
29
+ pytest.skip("DEVNET_ADDRESS_2 environment variable not set")
30
+ return value
31
+
32
+
33
+ @pytest.fixture
34
+ def devnet_address_3() -> str:
35
+ value = os.getenv("DEVNET_ADDRESS_3")
36
+ if value is None:
37
+ pytest.skip("DEVNET_ADDRESS_3 environment variable not set")
38
+ return value
39
+
40
+
41
+ @pytest.fixture
42
+ def devnet_private_1() -> str:
43
+ value = os.getenv("DEVNET_PRIVATE_1")
44
+ if value is None:
45
+ pytest.skip("DEVNET_PRIVATE_1 environment variable not set")
46
+ return value
47
+
48
+
49
+ @pytest.fixture
50
+ def devnet_private_2() -> str:
51
+ value = os.getenv("DEVNET_PRIVATE_2")
52
+ if value is None:
53
+ pytest.skip("DEVNET_PRIVATE_2 environment variable not set")
54
+ return value
55
+
56
+
57
+ @pytest.fixture
58
+ def devnet_private_3() -> str:
59
+ value = os.getenv("DEVNET_PRIVATE_3")
60
+ if value is None:
61
+ pytest.skip("DEVNET_PRIVATE_3 environment variable not set")
62
+ return value
63
+
64
+
65
+ @pytest.fixture
66
+ def mainnet_node() -> str:
67
+ value = os.getenv("MAINNET_NODE")
68
+ if value is None:
69
+ pytest.skip("MAINNET_NODE environment variable not set")
70
+ return value
71
+
72
+
73
+ @pytest.fixture
74
+ def testnet_node() -> str:
75
+ value = os.getenv("TESTNET_NODE")
76
+ if value is None:
77
+ pytest.skip("TESTNET_NODE environment variable not set")
78
+ return value
79
+
80
+
81
+ @pytest.fixture
82
+ def usdt_token_address() -> str:
83
+ value = os.getenv("USDT_TOKEN_ADDRESS")
84
+ if value is None:
85
+ pytest.skip("USDT_TOKEN_ADDRESS environment variable not set")
86
+ return value
87
+
88
+
89
+ @pytest.fixture
90
+ def usdt_owner_address() -> str:
91
+ value = os.getenv("USDT_OWNER_ADDRESS")
92
+ if value is None:
93
+ pytest.skip("USDT_OWNER_ADDRESS environment variable not set")
94
+ return value
95
+
96
+
97
+ @pytest.fixture
98
+ def binance_wallet():
99
+ return "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S"
100
+
101
+
102
+ @pytest.fixture
103
+ def proxy() -> str:
104
+ value = os.getenv("PROXY")
105
+ if value is None:
106
+ pytest.skip("PROXY environment variable not set")
107
+ return value
108
+
109
+
110
+ @pytest.fixture(scope="session")
111
+ def proxies() -> list[str]:
112
+ proxies_url = os.getenv("PROXIES_URL")
113
+ if proxies_url:
114
+ return fetch_proxies_sync(proxies_url).unwrap("Failed to fetch proxies from URL")
115
+ return []
116
+
117
+
118
+ @pytest.fixture
119
+ def random_proxy(proxies) -> str | None:
120
+ return mm_web3.random_proxy(proxies)
121
+
122
+
123
+ @pytest.fixture
124
+ def cli_runner() -> CliRunner:
125
+ return CliRunner()