mm-sol 0.3.3__tar.gz → 0.3.5__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.3 → mm_sol-0.3.5}/PKG-INFO +1 -1
  2. mm_sol-0.3.5/README.md +10 -0
  3. {mm_sol-0.3.3 → mm_sol-0.3.5}/pyproject.toml +1 -1
  4. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/balance.py +3 -0
  5. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/balances_cmd.py +22 -12
  6. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/transfer_sol_cmd.py +2 -2
  7. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/transfer_token_cmd.py +2 -2
  8. mm_sol-0.3.5/src/mm_sol/cli/examples/balances.toml +10 -0
  9. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/converters.py +4 -0
  10. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/transfer.py +1 -2
  11. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/test_token.py +9 -11
  12. {mm_sol-0.3.3 → mm_sol-0.3.5}/uv.lock +1 -1
  13. mm_sol-0.3.3/README.txt +0 -14
  14. mm_sol-0.3.3/src/mm_sol/cli/examples/balances.toml +0 -9
  15. {mm_sol-0.3.3 → mm_sol-0.3.5}/.env.example +0 -0
  16. {mm_sol-0.3.3 → mm_sol-0.3.5}/.gitignore +0 -0
  17. {mm_sol-0.3.3 → mm_sol-0.3.5}/dict.dic +0 -0
  18. {mm_sol-0.3.3 → mm_sol-0.3.5}/justfile +0 -0
  19. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/__init__.py +0 -0
  20. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/account.py +0 -0
  21. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/block.py +0 -0
  22. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/__init__.py +0 -0
  23. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/calcs.py +0 -0
  24. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cli.py +0 -0
  25. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cli_utils.py +0 -0
  26. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/__init__.py +0 -0
  27. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/balance_cmd.py +0 -0
  28. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/example_cmd.py +0 -0
  29. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/node_cmd.py +0 -0
  30. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/wallet/__init__.py +0 -0
  31. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/wallet/keypair_cmd.py +0 -0
  32. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/cmd/wallet/mnemonic_cmd.py +0 -0
  33. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/examples/transfer-sol.toml +0 -0
  34. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/examples/transfer-token.toml +0 -0
  35. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/cli/validators.py +0 -0
  36. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/constants.py +0 -0
  37. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/py.typed +0 -0
  38. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/rpc.py +0 -0
  39. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/solana_cli.py +0 -0
  40. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/token.py +0 -0
  41. {mm_sol-0.3.3 → mm_sol-0.3.5}/src/mm_sol/utils.py +0 -0
  42. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/__init__.py +0 -0
  43. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/cli/__init__.py +0 -0
  44. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/cli/cmd/__init__.py +0 -0
  45. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/cli/cmd/wallet/__init__.py +0 -0
  46. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/cli/cmd/wallet/test_keypair_cmd.py +0 -0
  47. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/cli/cmd/wallet/test_mnemonic_cmd.py +0 -0
  48. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/conftest.py +0 -0
  49. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/test_account.py +0 -0
  50. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/test_balance.py +0 -0
  51. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/test_client.py +0 -0
  52. {mm_sol-0.3.3 → mm_sol-0.3.5}/tests/test_converters.py +0 -0
  53. {mm_sol-0.3.3 → mm_sol-0.3.5}/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.3
3
+ Version: 0.3.5
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.5/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.3"
3
+ version = "0.3.5"
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,20 +4,21 @@ from pathlib import Path
4
4
  from typing import Annotated, Any
5
5
 
6
6
  from mm_crypto_utils import ConfigValidators
7
- from mm_std import BaseConfig, print_json
8
- from pydantic import BeforeValidator
7
+ from mm_std import BaseConfig, Err, fatal, print_json
8
+ from pydantic import BeforeValidator, Field
9
9
 
10
10
  import mm_sol.converters
11
11
  from mm_sol import balance
12
12
  from mm_sol.balance import get_token_balance_with_retries
13
13
  from mm_sol.cli.validators import Validators
14
+ from mm_sol.token import get_decimals_with_retries
14
15
 
15
16
 
16
17
  class Config(BaseConfig):
17
18
  accounts: Annotated[list[str], BeforeValidator(Validators.sol_addresses(unique=True))]
18
19
  tokens: Annotated[list[str], BeforeValidator(Validators.sol_addresses(unique=True))]
19
20
  nodes: Annotated[list[str], BeforeValidator(ConfigValidators.nodes())]
20
- proxies: Annotated[list[str], BeforeValidator(Validators.proxies())]
21
+ proxies: Annotated[list[str], Field(default_factory=list), BeforeValidator(Validators.proxies())]
21
22
 
22
23
  @property
23
24
  def random_node(self) -> str:
@@ -34,22 +35,31 @@ def run(config_path: Path, print_config: bool) -> None:
34
35
 
35
36
  if config.tokens:
36
37
  for token in config.tokens:
37
- result[token] = _get_token_balances(token, config.accounts, config)
38
+ token_decimals_res = get_decimals_with_retries(config.nodes, token, retries=3, proxies=config.proxies)
39
+ if isinstance(token_decimals_res, Err):
40
+ fatal(f"Failed to get decimals for token {token}: {token_decimals_res.unwrap_err()}")
41
+ token_decimals = token_decimals_res.unwrap()
42
+ result[token] = _get_token_balances(token, token_decimals, config.accounts, config)
43
+ result[token + "_decimals"] = token_decimals
38
44
  result[token + "_sum"] = sum([v for v in result[token].values() if v is not None])
39
45
 
40
46
  print_json(result)
41
47
 
42
48
 
43
- def _get_token_balances(token: str, accounts: list[str], config: Config) -> dict[str, int | None]:
49
+ def _get_token_balances(token: str, token_decimals: int, accounts: list[str], config: Config) -> dict[str, Decimal | None]:
44
50
  result = {}
45
51
  for account in accounts:
46
- result[account] = get_token_balance_with_retries(
47
- nodes=config.nodes,
48
- owner_address=account,
49
- token_mint_address=token,
50
- retries=3,
51
- proxies=config.proxies,
52
- ).ok_or_none()
52
+ result[account] = (
53
+ get_token_balance_with_retries(
54
+ nodes=config.nodes,
55
+ owner_address=account,
56
+ token_mint_address=token,
57
+ retries=3,
58
+ proxies=config.proxies,
59
+ )
60
+ .map(lambda v: mm_sol.converters.to_token(v, token_decimals))
61
+ .unwrap_or(None)
62
+ )
53
63
  return result
54
64
 
55
65
 
@@ -7,7 +7,7 @@ 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
11
 
12
12
  from mm_sol import transfer
13
13
  from mm_sol.cli import calcs, cli_utils
@@ -21,7 +21,7 @@ class Config(BaseConfig):
21
21
  nodes: Annotated[list[str], BeforeValidator(Validators.nodes())]
22
22
  routes: Annotated[list[TxRoute], BeforeValidator(Validators.sol_routes())]
23
23
  private_keys: Annotated[AddressToPrivate, BeforeValidator(Validators.sol_private_keys())]
24
- proxies: Annotated[list[str], BeforeValidator(Validators.proxies())]
24
+ proxies: Annotated[list[str], Field(default_factory=list), BeforeValidator(Validators.proxies())]
25
25
  value: Annotated[str, AfterValidator(Validators.valid_sol_expression("balance"))]
26
26
  value_min_limit: Annotated[str | None, AfterValidator(Validators.valid_sol_expression())] = None
27
27
  delay: Annotated[str | None, AfterValidator(Validators.valid_calc_decimal_value())] = None # in seconds
@@ -8,7 +8,7 @@ import typer
8
8
  from loguru import logger
9
9
  from mm_crypto_utils import AddressToPrivate, TxRoute
10
10
  from mm_std import BaseConfig, Err, fatal, utc_now
11
- from pydantic import AfterValidator, BeforeValidator, model_validator
11
+ from pydantic import AfterValidator, BeforeValidator, Field, model_validator
12
12
 
13
13
  from mm_sol import transfer
14
14
  from mm_sol.cli import calcs, cli_utils
@@ -22,7 +22,7 @@ class Config(BaseConfig):
22
22
  nodes: Annotated[list[str], BeforeValidator(Validators.nodes())]
23
23
  routes: Annotated[list[TxRoute], BeforeValidator(Validators.sol_routes())]
24
24
  private_keys: Annotated[AddressToPrivate, BeforeValidator(Validators.sol_private_keys())]
25
- proxies: Annotated[list[str], BeforeValidator(Validators.proxies())]
25
+ proxies: Annotated[list[str], Field(default_factory=list), BeforeValidator(Validators.proxies())]
26
26
  token: Annotated[str, AfterValidator(Validators.sol_address())]
27
27
  value: Annotated[str, AfterValidator(Validators.valid_token_expression("balance"))]
28
28
  value_min_limit: Annotated[str | None, AfterValidator(Validators.valid_token_expression())] = None
@@ -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"
@@ -2,10 +2,14 @@ from decimal import Decimal
2
2
 
3
3
 
4
4
  def lamports_to_sol(lamports: int, ndigits: int = 4) -> Decimal:
5
+ if lamports == 0:
6
+ return Decimal(0)
5
7
  return Decimal(str(round(lamports / 10**9, ndigits=ndigits)))
6
8
 
7
9
 
8
10
  def to_token(smallest_unit_value: int, decimals: int, ndigits: int = 4) -> Decimal:
11
+ if smallest_unit_value == 0:
12
+ return Decimal(0)
9
13
  return Decimal(str(round(smallest_unit_value / 10**decimals, ndigits=ndigits)))
10
14
 
11
15
 
@@ -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.3"
496
+ version = "0.3.5"
497
497
  source = { editable = "." }
498
498
  dependencies = [
499
499
  { name = "base58" },
mm_sol-0.3.3/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,9 +0,0 @@
1
- accounts = """
2
- 5BJ9ViMj4gi2BBX3wbCEJ4p4vpVWKpG6ja2aQP2ACBUv
3
- HbSAUDjzpr44fWzf9Ynj3V1jq3wBsgg88N88P4vzeGPX
4
- """
5
- tokens = """
6
- 7XtmNSHJDHTZdx2K1S8D529kHsBbt3Civxt6vUHrGxBR
7
- 65FKbLPtrssmc8aVn9DEBY8VTGB85vsgadrmaW9H4nEB
8
- """
9
- nodes = "https://api.mainnet-beta.solana.com"
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