mm-sol 0.3.4__tar.gz → 0.3.6__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 (53) hide show
  1. {mm_sol-0.3.4 → mm_sol-0.3.6}/PKG-INFO +1 -1
  2. mm_sol-0.3.6/README.md +10 -0
  3. {mm_sol-0.3.4 → mm_sol-0.3.6}/pyproject.toml +1 -1
  4. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/balance.py +3 -0
  5. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cli_utils.py +0 -25
  6. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/balances_cmd.py +2 -2
  7. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/transfer_sol_cmd.py +23 -3
  8. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/transfer_token_cmd.py +41 -6
  9. mm_sol-0.3.6/src/mm_sol/cli/examples/balances.toml +10 -0
  10. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/examples/transfer-sol.toml +2 -1
  11. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/transfer.py +1 -2
  12. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/test_token.py +9 -11
  13. {mm_sol-0.3.4 → mm_sol-0.3.6}/uv.lock +1 -1
  14. mm_sol-0.3.4/README.txt +0 -14
  15. mm_sol-0.3.4/src/mm_sol/cli/examples/balances.toml +0 -10
  16. {mm_sol-0.3.4 → mm_sol-0.3.6}/.env.example +0 -0
  17. {mm_sol-0.3.4 → mm_sol-0.3.6}/.gitignore +0 -0
  18. {mm_sol-0.3.4 → mm_sol-0.3.6}/dict.dic +0 -0
  19. {mm_sol-0.3.4 → mm_sol-0.3.6}/justfile +0 -0
  20. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/__init__.py +0 -0
  21. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/account.py +0 -0
  22. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/block.py +0 -0
  23. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/__init__.py +0 -0
  24. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/calcs.py +0 -0
  25. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cli.py +0 -0
  26. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/__init__.py +0 -0
  27. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/balance_cmd.py +0 -0
  28. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/example_cmd.py +0 -0
  29. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/node_cmd.py +0 -0
  30. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/wallet/__init__.py +0 -0
  31. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/wallet/keypair_cmd.py +0 -0
  32. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/cmd/wallet/mnemonic_cmd.py +0 -0
  33. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/examples/transfer-token.toml +0 -0
  34. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/cli/validators.py +0 -0
  35. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/constants.py +0 -0
  36. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/converters.py +0 -0
  37. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/py.typed +0 -0
  38. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/rpc.py +0 -0
  39. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/solana_cli.py +0 -0
  40. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/token.py +0 -0
  41. {mm_sol-0.3.4 → mm_sol-0.3.6}/src/mm_sol/utils.py +0 -0
  42. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/__init__.py +0 -0
  43. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/cli/__init__.py +0 -0
  44. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/cli/cmd/__init__.py +0 -0
  45. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/cli/cmd/wallet/__init__.py +0 -0
  46. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/cli/cmd/wallet/test_keypair_cmd.py +0 -0
  47. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/cli/cmd/wallet/test_mnemonic_cmd.py +0 -0
  48. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/conftest.py +0 -0
  49. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/test_account.py +0 -0
  50. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/test_balance.py +0 -0
  51. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/test_client.py +0 -0
  52. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/test_converters.py +0 -0
  53. {mm_sol-0.3.4 → mm_sol-0.3.6}/tests/test_rpc.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mm-sol
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Requires-Python: >=3.12
5
5
  Requires-Dist: base58~=2.1.1
6
6
  Requires-Dist: jinja2>=3.1.5
mm_sol-0.3.6/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # mm-sol
2
+ A Python library and cli tool for interacting with Solana blockchain. It's based on https://github.com/michaelhly/solana-py
3
+
4
+ ### Install on Ubuntu
5
+ ```shell
6
+ sudo apt update && sudo apt-get install build-essential libgmp3-dev python3-dev -y
7
+ sudo curl -LsSf https://astral.sh/uv/install.sh | sh
8
+ source $HOME/.local/bin/env
9
+ uv tool install mm-sol
10
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mm-sol"
3
- version = "0.3.4"
3
+ version = "0.3.6"
4
4
  description = ""
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
@@ -92,4 +92,7 @@ def get_token_balance_with_retries(
92
92
  )
93
93
  if res.is_ok():
94
94
  return res
95
+ if isinstance(res, Err) and res.err == "no_token_accounts":
96
+ return res
97
+
95
98
  return res
@@ -4,12 +4,8 @@ import time
4
4
  import mm_crypto_utils
5
5
  from loguru import logger
6
6
  from mm_crypto_utils import Nodes, Proxies
7
- from rich.live import Live
8
- from rich.table import Table
9
7
  from solders.signature import Signature
10
8
 
11
- from mm_sol.balance import get_sol_balance_with_retries
12
- from mm_sol.converters import lamports_to_sol
13
9
  from mm_sol.utils import get_client
14
10
 
15
11
 
@@ -32,27 +28,6 @@ def public_rpc_url(url: str | None) -> str:
32
28
  return url
33
29
 
34
30
 
35
- def print_balances(
36
- rpc_nodes: list[str],
37
- addresses: list[str],
38
- *,
39
- proxies: Proxies = None,
40
- round_ndigits: int = 5,
41
- ) -> None:
42
- table = Table(title="balances")
43
- table.add_column("n")
44
- table.add_column("address")
45
- table.add_column("balance, sol")
46
- with Live(table, refresh_per_second=0.5):
47
- for count, address in enumerate(addresses):
48
- balance = get_sol_balance_with_retries(rpc_nodes, address, proxies=proxies, retries=5).map_or_else(
49
- lambda err: err,
50
- lambda ok: str(lamports_to_sol(ok, round_ndigits)),
51
- )
52
- row: list[str] = [str(count), address, balance]
53
- table.add_row(*row)
54
-
55
-
56
31
  def wait_confirmation(nodes: Nodes, proxies: Proxies, signature: Signature, log_prefix: str) -> bool:
57
32
  count = 0
58
33
  while True:
@@ -5,7 +5,7 @@ from typing import Annotated, Any
5
5
 
6
6
  from mm_crypto_utils import ConfigValidators
7
7
  from mm_std import BaseConfig, Err, fatal, print_json
8
- from pydantic import BeforeValidator
8
+ from pydantic import BeforeValidator, Field
9
9
 
10
10
  import mm_sol.converters
11
11
  from mm_sol import balance
@@ -18,7 +18,7 @@ class Config(BaseConfig):
18
18
  accounts: Annotated[list[str], BeforeValidator(Validators.sol_addresses(unique=True))]
19
19
  tokens: Annotated[list[str], BeforeValidator(Validators.sol_addresses(unique=True))]
20
20
  nodes: Annotated[list[str], BeforeValidator(ConfigValidators.nodes())]
21
- proxies: Annotated[list[str], BeforeValidator(Validators.proxies())]
21
+ proxies: Annotated[list[str], Field(default_factory=list), BeforeValidator(Validators.proxies())]
22
22
 
23
23
  @property
24
24
  def random_node(self) -> str:
@@ -7,9 +7,12 @@ import mm_crypto_utils
7
7
  from loguru import logger
8
8
  from mm_crypto_utils import AddressToPrivate, TxRoute
9
9
  from mm_std import BaseConfig, Err, utc_now
10
- from pydantic import AfterValidator, BeforeValidator, model_validator
10
+ from pydantic import AfterValidator, BeforeValidator, Field, model_validator
11
+ from rich.live import Live
12
+ from rich.table import Table
11
13
 
12
14
  from mm_sol import transfer
15
+ from mm_sol.balance import get_sol_balance_with_retries
13
16
  from mm_sol.cli import calcs, cli_utils
14
17
  from mm_sol.cli.calcs import calc_sol_expression
15
18
  from mm_sol.cli.validators import Validators
@@ -21,7 +24,7 @@ class Config(BaseConfig):
21
24
  nodes: Annotated[list[str], BeforeValidator(Validators.nodes())]
22
25
  routes: Annotated[list[TxRoute], BeforeValidator(Validators.sol_routes())]
23
26
  private_keys: Annotated[AddressToPrivate, BeforeValidator(Validators.sol_private_keys())]
24
- proxies: Annotated[list[str], BeforeValidator(Validators.proxies())]
27
+ proxies: Annotated[list[str], Field(default_factory=list), BeforeValidator(Validators.proxies())]
25
28
  value: Annotated[str, AfterValidator(Validators.valid_sol_expression("balance"))]
26
29
  value_min_limit: Annotated[str | None, AfterValidator(Validators.valid_sol_expression())] = None
27
30
  delay: Annotated[str | None, AfterValidator(Validators.valid_calc_decimal_value())] = None # in seconds
@@ -58,7 +61,7 @@ def run(
58
61
  mm_crypto_utils.init_logger(debug, config.log_debug, config.log_info)
59
62
 
60
63
  if print_balances:
61
- cli_utils.print_balances(config.nodes, config.from_addresses, round_ndigits=config.round_ndigits, proxies=config.proxies)
64
+ _print_balances(config)
62
65
  sys.exit(0)
63
66
 
64
67
  _run_transfers(config, no_confirmation=no_confirmation, emulate=emulate)
@@ -137,3 +140,20 @@ def _transfer(*, from_address: str, to_address: str, config: Config, no_confirma
137
140
  status = "OK"
138
141
  msg = f"{log_prefix}: sig={signature}, value={lamports_to_sol(value, config.round_ndigits)}, status={status}"
139
142
  logger.info(msg)
143
+
144
+
145
+ def _print_balances(config: Config) -> None:
146
+ table = Table("n", "from_address", "sol", "to_address", "sol", title="balances")
147
+ with Live(table, refresh_per_second=0.5):
148
+ for count, route in enumerate(config.routes):
149
+ from_balance = _get_sol_balance_str(route.from_address, config)
150
+ to_balance = _get_sol_balance_str(route.to_address, config)
151
+ row: list[str] = [str(count), route.from_address, from_balance, route.to_address, to_balance]
152
+ table.add_row(*row)
153
+
154
+
155
+ def _get_sol_balance_str(address: str, config: Config) -> str:
156
+ return get_sol_balance_with_retries(config.nodes, address, proxies=config.proxies, retries=5).map_or_else(
157
+ lambda err: err,
158
+ lambda ok: str(lamports_to_sol(ok, config.round_ndigits)),
159
+ )
@@ -4,16 +4,18 @@ from pathlib import Path
4
4
  from typing import Annotated, Self
5
5
 
6
6
  import mm_crypto_utils
7
- import typer
8
7
  from loguru import logger
9
8
  from mm_crypto_utils import AddressToPrivate, TxRoute
10
9
  from mm_std import BaseConfig, Err, fatal, utc_now
11
- from pydantic import AfterValidator, BeforeValidator, model_validator
10
+ from pydantic import AfterValidator, BeforeValidator, Field, model_validator
11
+ from rich.live import Live
12
+ from rich.table import Table
12
13
 
13
14
  from mm_sol import transfer
15
+ from mm_sol.balance import get_sol_balance_with_retries, get_token_balance_with_retries
14
16
  from mm_sol.cli import calcs, cli_utils
15
17
  from mm_sol.cli.validators import Validators
16
- from mm_sol.converters import to_token
18
+ from mm_sol.converters import lamports_to_sol, to_token
17
19
  from mm_sol.token import get_decimals_with_retries
18
20
 
19
21
 
@@ -22,7 +24,7 @@ class Config(BaseConfig):
22
24
  nodes: Annotated[list[str], BeforeValidator(Validators.nodes())]
23
25
  routes: Annotated[list[TxRoute], BeforeValidator(Validators.sol_routes())]
24
26
  private_keys: Annotated[AddressToPrivate, BeforeValidator(Validators.sol_private_keys())]
25
- proxies: Annotated[list[str], BeforeValidator(Validators.proxies())]
27
+ proxies: Annotated[list[str], Field(default_factory=list), BeforeValidator(Validators.proxies())]
26
28
  token: Annotated[str, AfterValidator(Validators.sol_address())]
27
29
  value: Annotated[str, AfterValidator(Validators.valid_token_expression("balance"))]
28
30
  value_min_limit: Annotated[str | None, AfterValidator(Validators.valid_token_expression())] = None
@@ -67,8 +69,7 @@ def run(
67
69
  logger.debug(f"token decimals={token_decimals}")
68
70
 
69
71
  if print_balances:
70
- # cli_utils.print_balances(config.nodes, config.from_addresses, round_ndigits=config.round_ndigits, proxies=config.proxies) # noqa: E501
71
- typer.echo("Not implemented yet")
72
+ _print_balances(config, token_decimals)
72
73
  sys.exit(0)
73
74
 
74
75
  _run_transfers(config, token_decimals, no_confirmation=no_confirmation, emulate=emulate)
@@ -151,3 +152,37 @@ def _transfer(*, route: TxRoute, config: Config, token_decimals: int, no_confirm
151
152
  status = "OK"
152
153
  msg = f"{log_prefix}: sig={signature}, value={value_t}, status={status}"
153
154
  logger.info(msg)
155
+
156
+
157
+ def _print_balances(config: Config, token_decimals: int) -> None:
158
+ table = Table("n", "from_address", "sol", "t", "to_address", "sol", "t", title="balances")
159
+ with Live(table, refresh_per_second=0.5):
160
+ for count, route in enumerate(config.routes):
161
+ from_sol_balance = _get_sol_balance_str(route.from_address, config)
162
+ to_sol_balance = _get_sol_balance_str(route.to_address, config)
163
+ from_t_balance = _get_token_balance_str(route.from_address, config, token_decimals)
164
+ to_t_balance = _get_token_balance_str(route.to_address, config, token_decimals)
165
+ row: list[str] = [
166
+ str(count),
167
+ route.from_address,
168
+ from_sol_balance,
169
+ from_t_balance,
170
+ route.to_address,
171
+ to_sol_balance,
172
+ to_t_balance,
173
+ ]
174
+ table.add_row(*row)
175
+
176
+
177
+ def _get_sol_balance_str(address: str, config: Config) -> str:
178
+ return get_sol_balance_with_retries(config.nodes, address, proxies=config.proxies, retries=5).map_or_else(
179
+ lambda err: err,
180
+ lambda ok: str(lamports_to_sol(ok, config.round_ndigits)),
181
+ )
182
+
183
+
184
+ def _get_token_balance_str(address: str, config: Config, token_decimals: int) -> str:
185
+ return get_token_balance_with_retries(config.nodes, address, config.token, proxies=config.proxies, retries=5).map_or_else(
186
+ lambda err: err,
187
+ lambda ok: str(to_token(ok, token_decimals, ndigits=config.round_ndigits)),
188
+ )
@@ -0,0 +1,10 @@
1
+ accounts = """
2
+ 9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM # binance
3
+ 61aq585V8cR2sZBeawJFt2NPqmN7zDi1sws4KLs5xHXV # Jupiter cold wallet
4
+ """
5
+ tokens = """
6
+ EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v # USDC
7
+ JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN # JUP
8
+ """
9
+ nodes = "https://api.mainnet-beta.solana.com"
10
+ # proxies = "env_url: MM_SOL_PROXIES_URL"
@@ -3,6 +3,7 @@ Bd8CxCTLez2ckVTqEJjuZkWjYFSRbo8fA1qYbd7yFVP9 Eaft9xXzfgbRqsHd65WspoaxTtH7pkznM9Y
3
3
  Fc2TRJVCpFZpRz56mFnQETctib1zwFnwHcS7HoQSgUzZ EVJctTWikt29rUXBf49tyQdK87x837HtvpCwqeSjp1Ur
4
4
  """
5
5
  private_keys = "file: ./path/to/privates.txt"
6
- value = "0.012 sol"
6
+ value = "0.5balance - 0.012sol"
7
+ delay = "random(5,10)"
7
8
  proxies = "url: https://site.com/api/get-proxies"
8
9
  nodes = "https://api.devnet.solana.com"
@@ -42,13 +42,12 @@ def transfer_token(
42
42
 
43
43
  recipient_token_account = get_associated_token_address(to_address, token_mint_address, token_program_id=TOKEN_PROGRAM_ID)
44
44
  from_token_account = get_associated_token_address(from_address, token_mint_address, token_program_id=TOKEN_PROGRAM_ID)
45
-
46
45
  data: list[object] = []
47
46
 
48
47
  account_info_res = client.get_account_info(recipient_token_account)
49
48
  if account_info_res.value is None:
50
49
  if create_token_account_if_not_exists:
51
- create_account_res = token_client.create_account(to_address, skip_confirmation=False)
50
+ create_account_res = token_client.create_associated_token_account(to_address, skip_confirmation=False)
52
51
  data.append(create_account_res)
53
52
  else:
54
53
  return Err("no_token_account")
@@ -10,27 +10,25 @@ def test_get_balance(mainnet_node, usdt_token_address, usdt_owner_address, proxi
10
10
  assert res.unwrap() > 0
11
11
 
12
12
 
13
- def test_get_balance_no_tokens_account_1(mainnet_node, usdt_token_address, random_proxy):
14
- res = mm_sol.balance.get_token_balance(
13
+ def test_get_balance_no_tokens_account_1(mainnet_node, usdt_token_address, proxies):
14
+ res = mm_sol.balance.get_token_balance_with_retries(
15
15
  mainnet_node,
16
16
  generate_account().public_key,
17
17
  usdt_token_address,
18
- proxy=random_proxy,
18
+ proxies=proxies,
19
19
  no_token_accounts_return_zero=False,
20
+ retries=5,
20
21
  )
21
22
  assert res.err == "no_token_accounts"
22
23
 
23
24
 
24
- def test_get_balance_no_tokens_account_2(mainnet_node, usdt_token_address, random_proxy):
25
- res = mm_sol.balance.get_token_balance(
26
- mainnet_node,
27
- generate_account().public_key,
28
- usdt_token_address,
29
- proxy=random_proxy,
25
+ def test_get_balance_no_tokens_account_2(mainnet_node, usdt_token_address, proxies):
26
+ res = mm_sol.balance.get_token_balance_with_retries(
27
+ mainnet_node, generate_account().public_key, usdt_token_address, proxies=proxies, retries=5
30
28
  )
31
29
  assert res.ok == 0
32
30
 
33
31
 
34
- def test_get_decimals(mainnet_node, usdt_token_address, random_proxy):
35
- res = token.get_decimals(mainnet_node, usdt_token_address, proxy=random_proxy)
32
+ def test_get_decimals(mainnet_node, usdt_token_address, proxies):
33
+ res = token.get_decimals_with_retries(mainnet_node, usdt_token_address, proxies=proxies, retries=5)
36
34
  assert res.unwrap() == 6
@@ -493,7 +493,7 @@ wheels = [
493
493
 
494
494
  [[package]]
495
495
  name = "mm-sol"
496
- version = "0.3.4"
496
+ version = "0.3.6"
497
497
  source = { editable = "." }
498
498
  dependencies = [
499
499
  { name = "base58" },
mm_sol-0.3.4/README.txt DELETED
@@ -1,14 +0,0 @@
1
- mm-sol
2
-
3
- Maninnet RPC:
4
- - https://api.mainnet-beta.solana.com
5
-
6
- Testnet RPC:
7
- - https://api.testnet.solana.com
8
-
9
- Devnet RPC:
10
- - https://api.devnet.solana.com
11
-
12
- Links:
13
- - https://solana.com/docs/rpc
14
- - https://github.com/michaelhly/solana-py
@@ -1,10 +0,0 @@
1
- accounts = """
2
- 5BJ9ViMj4gi2BBX3wbCEJ4p4vpVWKpG6ja2aQP2ACBUv
3
- HbSAUDjzpr44fWzf9Ynj3V1jq3wBsgg88N88P4vzeGPX
4
- """
5
- tokens = """
6
- 7XtmNSHJDHTZdx2K1S8D529kHsBbt3Civxt6vUHrGxBR
7
- 65FKbLPtrssmc8aVn9DEBY8VTGB85vsgadrmaW9H4nEB
8
- """
9
- nodes = "https://api.mainnet-beta.solana.com"
10
- # proxies = "env_url: MM_SOL_PROXIES_URL"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes