mm-sol 0.5.4__tar.gz → 0.5.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.5.4 → mm_sol-0.5.6}/PKG-INFO +3 -2
  2. {mm_sol-0.5.4 → mm_sol-0.5.6}/README.md +3 -0
  3. {mm_sol-0.5.4 → mm_sol-0.5.6}/pyproject.toml +5 -3
  4. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/account.py +5 -5
  5. mm_sol-0.5.6/src/mm_sol/async_rpc.py +42 -0
  6. mm_sol-0.5.6/src/mm_sol/balance.py +158 -0
  7. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/rpc.py +13 -13
  8. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/utils.py +11 -0
  9. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/conftest.py +6 -1
  10. mm_sol-0.5.6/tests/test_async_rpc.py +10 -0
  11. mm_sol-0.5.6/tests/test_balance.py +25 -0
  12. {mm_sol-0.5.4 → mm_sol-0.5.6}/uv.lock +65 -41
  13. mm_sol-0.5.4/src/mm_sol/balance.py +0 -86
  14. mm_sol-0.5.4/tests/test_balance.py +0 -6
  15. {mm_sol-0.5.4 → mm_sol-0.5.6}/.env.example +0 -0
  16. {mm_sol-0.5.4 → mm_sol-0.5.6}/.gitignore +0 -0
  17. {mm_sol-0.5.4 → mm_sol-0.5.6}/dict.dic +0 -0
  18. {mm_sol-0.5.4 → mm_sol-0.5.6}/justfile +0 -0
  19. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/__init__.py +0 -0
  20. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/block.py +0 -0
  21. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/__init__.py +0 -0
  22. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/calcs.py +0 -0
  23. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cli.py +0 -0
  24. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cli_utils.py +0 -0
  25. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/__init__.py +0 -0
  26. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/balance_cmd.py +0 -0
  27. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/balances_cmd.py +0 -0
  28. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/example_cmd.py +0 -0
  29. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/node_cmd.py +0 -0
  30. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/transfer_cmd.py +0 -0
  31. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/wallet/__init__.py +0 -0
  32. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/wallet/keypair_cmd.py +0 -0
  33. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/wallet/mnemonic_cmd.py +0 -0
  34. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/examples/balances.toml +0 -0
  35. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/examples/transfer.toml +0 -0
  36. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/validators.py +0 -0
  37. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/constants.py +0 -0
  38. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/converters.py +0 -0
  39. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/py.typed +0 -0
  40. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/solana_cli.py +0 -0
  41. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/token.py +0 -0
  42. {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/transfer.py +0 -0
  43. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/__init__.py +0 -0
  44. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/__init__.py +0 -0
  45. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/cmd/__init__.py +0 -0
  46. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/cmd/wallet/__init__.py +0 -0
  47. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/cmd/wallet/test_keypair_cmd.py +0 -0
  48. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/cmd/wallet/test_mnemonic_cmd.py +0 -0
  49. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/test_account.py +0 -0
  50. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/test_client.py +0 -0
  51. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/test_converters.py +0 -0
  52. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/test_rpc.py +0 -0
  53. {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/test_token.py +0 -0
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mm-sol
3
- Version: 0.5.4
3
+ Version: 0.5.6
4
4
  Requires-Python: >=3.12
5
5
  Requires-Dist: base58~=2.1.1
6
6
  Requires-Dist: jinja2>=3.1.6
7
- Requires-Dist: mm-crypto-utils>=0.2.8
7
+ Requires-Dist: mm-crypto-utils>=0.2.9
8
8
  Requires-Dist: mnemonic==0.21
9
9
  Requires-Dist: socksio>=1.0.0
10
10
  Requires-Dist: solana~=0.36.6
11
+ Requires-Dist: solders~=0.26.0
11
12
  Requires-Dist: typer>=0.15.2
@@ -8,3 +8,6 @@ sudo curl -LsSf https://astral.sh/uv/install.sh | sh
8
8
  source $HOME/.local/bin/env
9
9
  uv tool install mm-sol
10
10
  ```
11
+
12
+ ### Notes
13
+ - AsyncClient from solana-py with socks5 proxy ignores timeout. Use http proxy.
@@ -1,11 +1,12 @@
1
1
  [project]
2
2
  name = "mm-sol"
3
- version = "0.5.4"
3
+ version = "0.5.6"
4
4
  description = ""
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
7
- "mm-crypto-utils>=0.2.8",
7
+ "mm-crypto-utils>=0.2.9",
8
8
  "solana~=0.36.6",
9
+ "solders~=0.26.0",
9
10
  "base58~=2.1.1",
10
11
  "mnemonic==0.21",
11
12
  "typer>=0.15.2",
@@ -23,7 +24,7 @@ build-backend = "hatchling.build"
23
24
  dev-dependencies = [
24
25
  "pytest~=8.3.5",
25
26
  "pytest-xdist~=3.6.1",
26
- "ruff~=0.10.0",
27
+ "ruff~=0.11.2",
27
28
  "pip-audit~=2.8.0",
28
29
  "bandit~=1.8.3",
29
30
  "mypy~=1.15.0",
@@ -64,6 +65,7 @@ ignore = [
64
65
  "TD003", # flake8-todos: missing-todo-link
65
66
  "RET503", # flake8-return: implicit-return
66
67
  "COM812", # it's used in ruff formatter
68
+ "ASYNC109", # flake8-async: async-function-with-timeout
67
69
  ]
68
70
  [tool.ruff.lint.pep8-naming]
69
71
  classmethod-decorators = ["field_validator"]
@@ -51,7 +51,7 @@ def derive_accounts(mnemonic: str, passphrase: str, derivation_path: str, limit:
51
51
  index=i,
52
52
  path=path,
53
53
  address=str(keypair.pubkey()),
54
- private_key=base58.b58encode(bytes(keypair.to_bytes_array())).decode("utf-8"),
54
+ private_key=base58.b58encode(bytes(keypair.to_bytes())).decode("utf-8"),
55
55
  )
56
56
  )
57
57
 
@@ -61,8 +61,8 @@ def derive_accounts(mnemonic: str, passphrase: str, derivation_path: str, limit:
61
61
  def generate_account() -> NewAccount:
62
62
  keypair = Keypair()
63
63
  public_key = str(keypair.pubkey())
64
- private_key_base58 = base58.b58encode(bytes(keypair.to_bytes_array())).decode("utf-8")
65
- private_key_arr = list(keypair.to_bytes_array())
64
+ private_key_base58 = base58.b58encode(bytes(keypair.to_bytes())).decode("utf-8")
65
+ private_key_arr = list(keypair.to_bytes())
66
66
  return NewAccount(public_key=public_key, private_key_base58=private_key_base58, private_key_arr=private_key_arr)
67
67
 
68
68
 
@@ -93,12 +93,12 @@ def get_public_key(private_key: str) -> str:
93
93
 
94
94
  def get_private_key_base58(private_key: str) -> str:
95
95
  keypair = get_keypair(private_key)
96
- return base58.b58encode(bytes(keypair.to_bytes_array())).decode("utf-8")
96
+ return base58.b58encode(bytes(keypair.to_bytes())).decode("utf-8")
97
97
 
98
98
 
99
99
  def get_private_key_arr(private_key: str) -> list[int]:
100
100
  keypair = get_keypair(private_key)
101
- return list(x for x in keypair.to_bytes_array()) # noqa: C400
101
+ return list(x for x in keypair.to_bytes()) # noqa: C400
102
102
 
103
103
 
104
104
  def get_private_key_arr_str(private_key: str) -> str:
@@ -0,0 +1,42 @@
1
+ from typing import Any
2
+
3
+ from mm_std import Result, ahr
4
+
5
+
6
+ async def rpc_call(
7
+ *,
8
+ node: str,
9
+ method: str,
10
+ params: list[Any],
11
+ id_: int = 1,
12
+ timeout: float = 10,
13
+ proxy: str | None = None,
14
+ ) -> Result[Any]:
15
+ data = {"jsonrpc": "2.0", "method": method, "params": params, "id": id_}
16
+ if node.startswith("http"):
17
+ return await _http_call(node, data, timeout, proxy)
18
+ raise NotImplementedError("ws is not implemented")
19
+
20
+
21
+ async def _http_call(node: str, data: dict[str, object], timeout: float, proxy: str | None) -> Result[Any]:
22
+ res = await ahr(node, method="POST", proxy=proxy, timeout=timeout, params=data, json_params=True)
23
+ try:
24
+ if res.is_error():
25
+ return res.to_err_result()
26
+
27
+ err = res.json.get("error", {}).get("message", "")
28
+ if err:
29
+ return res.to_err_result(f"service_error: {err}")
30
+ if "result" in res.json:
31
+ return res.to_ok_result(res.json["result"])
32
+
33
+ return res.to_err_result("unknown_response")
34
+ except Exception as e:
35
+ return res.to_err_result(f"exception: {e!s}")
36
+
37
+
38
+ async def get_balance(node: str, address: str, timeout: float = 10, proxy: str | None = None) -> Result[int]:
39
+ """Returns balance in lamports"""
40
+ return (await rpc_call(node=node, method="getBalance", params=[address], timeout=timeout, proxy=proxy)).and_then(
41
+ lambda r: r["value"]
42
+ )
@@ -0,0 +1,158 @@
1
+ import httpx
2
+ from mm_crypto_utils import Nodes, Proxies, random_node, random_proxy
3
+ from mm_std import Err, Ok, Result
4
+ from solana.exceptions import SolanaRpcException
5
+ from solana.rpc.core import RPCException
6
+ from solders.pubkey import Pubkey
7
+ from solders.rpc.errors import InvalidParamsMessage
8
+ from spl.token.instructions import get_associated_token_address
9
+
10
+ from mm_sol import async_rpc, rpc
11
+ from mm_sol.utils import get_async_client, get_client
12
+
13
+
14
+ def get_sol_balance(node: str, address: str, timeout: float = 10, proxy: str | None = None) -> Result[int]:
15
+ return rpc.get_balance(node, address, timeout, proxy)
16
+
17
+
18
+ async def get_sol_balance_async(node: str, address: str, timeout: float = 10, proxy: str | None = None) -> Result[int]:
19
+ return await async_rpc.get_balance(node, address, timeout, proxy)
20
+
21
+
22
+ def get_sol_balance_with_retries(
23
+ nodes: Nodes, address: str, retries: int, timeout: float = 10, proxies: Proxies = None
24
+ ) -> Result[int]:
25
+ res: Result[int] = Err("not started yet")
26
+ for _ in range(retries):
27
+ res = get_sol_balance(random_node(nodes), address, timeout=timeout, proxy=random_proxy(proxies))
28
+ if res.is_ok():
29
+ return res
30
+ return res
31
+
32
+
33
+ async def get_sol_balance_with_retries_async(
34
+ nodes: Nodes, address: str, retries: int, timeout: float = 10, proxies: Proxies = None
35
+ ) -> Result[int]:
36
+ res: Result[int] = Err("not started yet")
37
+ for _ in range(retries):
38
+ res = await get_sol_balance_async(random_node(nodes), address, timeout=timeout, proxy=random_proxy(proxies))
39
+ if res.is_ok():
40
+ return res
41
+ return res
42
+
43
+
44
+ def get_token_balance(
45
+ node: str,
46
+ owner_address: str,
47
+ token_mint_address: str,
48
+ token_account: str | None = None,
49
+ timeout: float = 10,
50
+ proxy: str | None = None,
51
+ ) -> Result[int]:
52
+ try:
53
+ client = get_client(node, proxy=proxy, timeout=timeout)
54
+ if not token_account:
55
+ token_account = str(
56
+ get_associated_token_address(Pubkey.from_string(owner_address), Pubkey.from_string(token_mint_address))
57
+ )
58
+
59
+ res = client.get_token_account_balance(Pubkey.from_string(token_account))
60
+
61
+ # Sometimes it not raise an error, but it returns this :(
62
+ if isinstance(res, InvalidParamsMessage) and "could not find account" in res.message:
63
+ return Ok(0)
64
+ return Ok(int(res.value.amount), data=res.to_json())
65
+ except RPCException as e:
66
+ if "could not find account" in str(e):
67
+ return Ok(0)
68
+ return Err(e)
69
+ except httpx.HTTPStatusError as e:
70
+ return Err(f"http error: {e}")
71
+ except SolanaRpcException as e:
72
+ return Err(e.error_msg)
73
+ except Exception as e:
74
+ return Err(e)
75
+
76
+
77
+ async def get_token_balance_async(
78
+ node: str,
79
+ owner_address: str,
80
+ token_mint_address: str,
81
+ token_account: str | None = None,
82
+ timeout: float = 10,
83
+ proxy: str | None = None,
84
+ ) -> Result[int]:
85
+ try:
86
+ client = get_async_client(node, proxy=proxy, timeout=timeout)
87
+ if not token_account:
88
+ token_account = str(
89
+ get_associated_token_address(Pubkey.from_string(owner_address), Pubkey.from_string(token_mint_address))
90
+ )
91
+
92
+ res = await client.get_token_account_balance(Pubkey.from_string(token_account))
93
+
94
+ # Sometimes it not raise an error, but it returns this :(
95
+ if isinstance(res, InvalidParamsMessage) and "could not find account" in res.message:
96
+ return Ok(0)
97
+ return Ok(int(res.value.amount), data=res.to_json())
98
+ except RPCException as e:
99
+ if "could not find account" in str(e):
100
+ return Ok(0)
101
+ return Err(e)
102
+
103
+ except httpx.HTTPStatusError as e:
104
+ return Err(f"http error: {e}")
105
+ except SolanaRpcException as e:
106
+ return Err(e.error_msg)
107
+ except Exception as e:
108
+ return Err(e)
109
+
110
+
111
+ def get_token_balance_with_retries(
112
+ nodes: Nodes,
113
+ owner_address: str,
114
+ token_mint_address: str,
115
+ retries: int,
116
+ token_account: str | None = None,
117
+ timeout: float = 10,
118
+ proxies: Proxies = None,
119
+ ) -> Result[int]:
120
+ res: Result[int] = Err("not started yet")
121
+ for _ in range(retries):
122
+ res = get_token_balance(
123
+ random_node(nodes),
124
+ owner_address,
125
+ token_mint_address,
126
+ token_account,
127
+ timeout=timeout,
128
+ proxy=random_proxy(proxies),
129
+ )
130
+ if res.is_ok():
131
+ return res
132
+
133
+ return res
134
+
135
+
136
+ async def get_token_balance_with_retries_async(
137
+ nodes: Nodes,
138
+ owner_address: str,
139
+ token_mint_address: str,
140
+ retries: int,
141
+ token_account: str | None = None,
142
+ timeout: float = 10,
143
+ proxies: Proxies = None,
144
+ ) -> Result[int]:
145
+ res: Result[int] = Err("not started yet")
146
+ for _ in range(retries):
147
+ res = await get_token_balance_async(
148
+ random_node(nodes),
149
+ owner_address,
150
+ token_mint_address,
151
+ token_account,
152
+ timeout=timeout,
153
+ proxy=random_proxy(proxies),
154
+ )
155
+ if res.is_ok():
156
+ return res
157
+
158
+ return res
@@ -78,7 +78,7 @@ def rpc_call(
78
78
  method: str,
79
79
  params: list[Any],
80
80
  id_: int = 1,
81
- timeout: int = 10,
81
+ timeout: float = 10,
82
82
  proxy: str | None = None,
83
83
  ) -> Result[Any]:
84
84
  data = {"jsonrpc": "2.0", "method": method, "params": params, "id": id_}
@@ -87,7 +87,7 @@ def rpc_call(
87
87
  raise NotImplementedError("ws is not implemented")
88
88
 
89
89
 
90
- def _http_call(node: str, data: dict[str, object], timeout: int, proxy: str | None) -> Result[Any]:
90
+ def _http_call(node: str, data: dict[str, object], timeout: float, proxy: str | None) -> Result[Any]:
91
91
  res = hr(node, method="POST", proxy=proxy, timeout=timeout, params=data, json_params=True)
92
92
  try:
93
93
  if res.is_error():
@@ -104,38 +104,38 @@ def _http_call(node: str, data: dict[str, object], timeout: int, proxy: str | No
104
104
  return res.to_err_result(f"exception: {e!s}")
105
105
 
106
106
 
107
- def get_balance(node: str, address: str, timeout: int = 10, proxy: str | None = None) -> Result[int]:
107
+ def get_balance(node: str, address: str, timeout: float = 10, proxy: str | None = None) -> Result[int]:
108
108
  """Returns balance in lamports"""
109
109
  return rpc_call(node=node, method="getBalance", params=[address], timeout=timeout, proxy=proxy).and_then(lambda r: r["value"])
110
110
 
111
111
 
112
- def get_block_height(node: str, timeout: int = 10, proxy: str | None = None) -> Result[int]:
112
+ def get_block_height(node: str, timeout: float = 10, proxy: str | None = None) -> Result[int]:
113
113
  """Returns balance in lamports"""
114
114
  return rpc_call(node=node, method="getBlockHeight", params=[], timeout=timeout, proxy=proxy)
115
115
 
116
116
 
117
- def get_slot(node: str, timeout: int = 10, proxy: str | None = None) -> Result[int]:
117
+ def get_slot(node: str, timeout: float = 10, proxy: str | None = None) -> Result[int]:
118
118
  return rpc_call(node=node, method="getSlot", params=[], timeout=timeout, proxy=proxy)
119
119
 
120
120
 
121
- def get_epoch_info(node: str, epoch: int | None = None, timeout: int = 10, proxy: str | None = None) -> Result[EpochInfo]:
121
+ def get_epoch_info(node: str, epoch: int | None = None, timeout: float = 10, proxy: str | None = None) -> Result[EpochInfo]:
122
122
  params = [epoch] if epoch else []
123
123
  return rpc_call(node=node, method="getEpochInfo", params=params, timeout=timeout, proxy=proxy).and_then(
124
124
  lambda r: EpochInfo(**r),
125
125
  )
126
126
 
127
127
 
128
- def get_health(node: str, timeout: int = 10, proxy: str | None = None) -> Result[bool]:
128
+ def get_health(node: str, timeout: float = 10, proxy: str | None = None) -> Result[bool]:
129
129
  return rpc_call(node=node, method="getHealth", params=[], timeout=timeout, proxy=proxy).and_then(lambda r: r == "ok")
130
130
 
131
131
 
132
- def get_cluster_nodes(node: str, timeout: int = 30, proxy: str | None = None) -> Result[list[ClusterNode]]:
132
+ def get_cluster_nodes(node: str, timeout: float = 30, proxy: str | None = None) -> Result[list[ClusterNode]]:
133
133
  return rpc_call(node=node, method="getClusterNodes", timeout=timeout, proxy=proxy, params=[]).and_then(
134
134
  lambda r: [ClusterNode(**n) for n in r],
135
135
  )
136
136
 
137
137
 
138
- def get_vote_accounts(node: str, timeout: int = 30, proxy: str | None = None) -> Result[list[VoteAccount]]:
138
+ def get_vote_accounts(node: str, timeout: float = 30, proxy: str | None = None) -> Result[list[VoteAccount]]:
139
139
  res = rpc_call(node=node, method="getVoteAccounts", timeout=timeout, proxy=proxy, params=[])
140
140
  if res.is_err():
141
141
  return res
@@ -182,7 +182,7 @@ def get_vote_accounts(node: str, timeout: int = 30, proxy: str | None = None) ->
182
182
  def get_leader_scheduler(
183
183
  node: str,
184
184
  slot: int | None = None,
185
- timeout: int = 10,
185
+ timeout: float = 10,
186
186
  proxy: str | None = None,
187
187
  ) -> Result[dict[str, list[int]]]:
188
188
  return rpc_call(
@@ -194,7 +194,7 @@ def get_leader_scheduler(
194
194
  )
195
195
 
196
196
 
197
- def get_block_production(node: str, timeout: int = 60, proxy: str | None = None) -> Result[BlockProduction]:
197
+ def get_block_production(node: str, timeout: float = 60, proxy: str | None = None) -> Result[BlockProduction]:
198
198
  res = rpc_call(node=node, method="getBlockProduction", timeout=timeout, proxy=proxy, params=[])
199
199
  if res.is_err():
200
200
  return res
@@ -211,7 +211,7 @@ def get_block_production(node: str, timeout: int = 60, proxy: str | None = None)
211
211
  return Err(e, data=res.data)
212
212
 
213
213
 
214
- def get_stake_activation(node: str, address: str, timeout: int = 60, proxy: str | None = None) -> Result[StakeActivation]:
214
+ def get_stake_activation(node: str, address: str, timeout: float = 60, proxy: str | None = None) -> Result[StakeActivation]:
215
215
  return rpc_call(node=node, method="getStakeActivation", timeout=timeout, proxy=proxy, params=[address]).and_then(
216
216
  lambda ok: StakeActivation(**ok),
217
217
  )
@@ -222,7 +222,7 @@ def get_transaction(
222
222
  signature: str,
223
223
  max_supported_transaction_version: int | None = None,
224
224
  encoding: str = "json",
225
- timeout: int = 60,
225
+ timeout: float = 60,
226
226
  proxy: str | None = None,
227
227
  ) -> Result[dict[str, object] | None]:
228
228
  if max_supported_transaction_version is not None:
@@ -1,4 +1,5 @@
1
1
  from solana.rpc.api import Client
2
+ from solana.rpc.async_api import AsyncClient
2
3
  from solana.rpc.commitment import Commitment
3
4
  from solders.pubkey import Pubkey
4
5
 
@@ -13,6 +14,16 @@ def get_client(
13
14
  return Client(endpoint, commitment=commitment, extra_headers=extra_headers, timeout=timeout, proxy=proxy)
14
15
 
15
16
 
17
+ def get_async_client(
18
+ endpoint: str,
19
+ commitment: Commitment | None = None,
20
+ extra_headers: dict[str, str] | None = None,
21
+ proxy: str | None = None,
22
+ timeout: float = 10,
23
+ ) -> AsyncClient:
24
+ return AsyncClient(endpoint, commitment=commitment, extra_headers=extra_headers, timeout=timeout, proxy=proxy)
25
+
26
+
16
27
  def pubkey(value: str | Pubkey) -> Pubkey:
17
28
  if isinstance(value, Pubkey):
18
29
  return value
@@ -5,7 +5,12 @@ import pytest
5
5
  from dotenv import load_dotenv
6
6
  from typer.testing import CliRunner
7
7
 
8
- load_dotenv(".env")
8
+ load_dotenv()
9
+
10
+
11
+ @pytest.fixture
12
+ def anyio_backend():
13
+ return "asyncio"
9
14
 
10
15
 
11
16
  @pytest.fixture
@@ -0,0 +1,10 @@
1
+ import pytest
2
+
3
+ from mm_sol import async_rpc
4
+
5
+ pytestmark = pytest.mark.anyio
6
+
7
+
8
+ async def test_get_balance(mainnet_node, binance_wallet, random_proxy):
9
+ res = await async_rpc.get_balance(mainnet_node, binance_wallet, proxy=random_proxy)
10
+ assert res.unwrap() > 10_000_000
@@ -0,0 +1,25 @@
1
+ import pytest
2
+
3
+ from mm_sol.balance import get_sol_balance, get_sol_balance_async, get_token_balance, get_token_balance_async
4
+
5
+ pytestmark = pytest.mark.anyio
6
+
7
+
8
+ def test_get_sol_balance(mainnet_node, usdt_owner_address, random_proxy):
9
+ res = get_sol_balance(mainnet_node, usdt_owner_address, proxy=random_proxy)
10
+ assert res.unwrap() > 10
11
+
12
+
13
+ async def test_get_sol_balance_async(mainnet_node, usdt_owner_address, random_proxy):
14
+ res = await get_sol_balance_async(mainnet_node, usdt_owner_address, proxy=random_proxy)
15
+ assert res.unwrap() > 10
16
+
17
+
18
+ def test_get_token_balance(mainnet_node, binance_wallet, usdt_token_address, random_proxy):
19
+ res = get_token_balance(mainnet_node, binance_wallet, usdt_token_address, proxy=random_proxy)
20
+ assert res.unwrap() > 10
21
+
22
+
23
+ async def test_get_token_balance_async(mainnet_node, binance_wallet, usdt_token_address, random_proxy):
24
+ res = await get_token_balance_async(mainnet_node, binance_wallet, usdt_token_address, proxy=random_proxy)
25
+ assert res.unwrap() > 10
@@ -13,15 +13,16 @@ wheels = [
13
13
 
14
14
  [[package]]
15
15
  name = "anyio"
16
- version = "4.6.2.post1"
16
+ version = "4.9.0"
17
17
  source = { registry = "https://pypi.org/simple" }
18
18
  dependencies = [
19
19
  { name = "idna" },
20
20
  { name = "sniffio" },
21
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
21
22
  ]
22
- sdist = { url = "https://files.pythonhosted.org/packages/9f/09/45b9b7a6d4e45c6bcb5bf61d19e3ab87df68e0601fa8c5293de3542546cc/anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c", size = 173422 }
23
+ sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 }
23
24
  wheels = [
24
- { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377 },
25
+ { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 },
25
26
  ]
26
27
 
27
28
  [[package]]
@@ -309,6 +310,11 @@ wheels = [
309
310
  { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
310
311
  ]
311
312
 
313
+ [package.optional-dependencies]
314
+ socks = [
315
+ { name = "socksio" },
316
+ ]
317
+
312
318
  [[package]]
313
319
  name = "idna"
314
320
  version = "3.10"
@@ -434,19 +440,19 @@ wheels = [
434
440
 
435
441
  [[package]]
436
442
  name = "mm-crypto-utils"
437
- version = "0.2.8"
443
+ version = "0.2.9"
438
444
  source = { registry = "https://pypi.org/simple" }
439
445
  dependencies = [
440
446
  { name = "loguru" },
441
447
  { name = "mm-std" },
442
448
  ]
443
449
  wheels = [
444
- { url = "https://files.pythonhosted.org/packages/c2/1c/0c6ad62e4671970eb643524742573cf3ee0dd8b2e1f58c6a7458efe8ae5e/mm_crypto_utils-0.2.8-py3-none-any.whl", hash = "sha256:04be8b12968e66973c916390737e31d55682cb5ce746cef73261e7d0381b2f7b", size = 8273 },
450
+ { url = "https://files.pythonhosted.org/packages/bb/4c/c790acb5b025d35a391dd4fef1ebb3b6432a874154844a618d27b10a0a0f/mm_crypto_utils-0.2.9-py3-none-any.whl", hash = "sha256:0b569067e4c4f8e568599134febe23aacbb903bb80799e4258180ec4a51bec9b", size = 8273 },
445
451
  ]
446
452
 
447
453
  [[package]]
448
454
  name = "mm-sol"
449
- version = "0.5.4"
455
+ version = "0.5.6"
450
456
  source = { editable = "." }
451
457
  dependencies = [
452
458
  { name = "base58" },
@@ -455,6 +461,7 @@ dependencies = [
455
461
  { name = "mnemonic" },
456
462
  { name = "socksio" },
457
463
  { name = "solana" },
464
+ { name = "solders" },
458
465
  { name = "typer" },
459
466
  ]
460
467
 
@@ -472,10 +479,11 @@ dev = [
472
479
  requires-dist = [
473
480
  { name = "base58", specifier = "~=2.1.1" },
474
481
  { name = "jinja2", specifier = ">=3.1.6" },
475
- { name = "mm-crypto-utils", specifier = ">=0.2.8" },
482
+ { name = "mm-crypto-utils", specifier = ">=0.2.9" },
476
483
  { name = "mnemonic", specifier = "==0.21" },
477
484
  { name = "socksio", specifier = ">=1.0.0" },
478
485
  { name = "solana", specifier = "~=0.36.6" },
486
+ { name = "solders", specifier = "~=0.26.0" },
479
487
  { name = "typer", specifier = ">=0.15.2" },
480
488
  ]
481
489
 
@@ -486,16 +494,19 @@ dev = [
486
494
  { name = "pip-audit", specifier = "~=2.8.0" },
487
495
  { name = "pytest", specifier = "~=8.3.5" },
488
496
  { name = "pytest-xdist", specifier = "~=3.6.1" },
489
- { name = "ruff", specifier = "~=0.10.0" },
497
+ { name = "ruff", specifier = "~=0.11.2" },
490
498
  ]
491
499
 
492
500
  [[package]]
493
501
  name = "mm-std"
494
- version = "0.3.9"
502
+ version = "0.3.14"
495
503
  source = { registry = "https://pypi.org/simple" }
496
504
  dependencies = [
505
+ { name = "anyio" },
497
506
  { name = "cryptography" },
507
+ { name = "httpx", extra = ["socks"] },
498
508
  { name = "pydantic" },
509
+ { name = "pydantic-settings" },
499
510
  { name = "pydash" },
500
511
  { name = "python-dotenv" },
501
512
  { name = "requests", extra = ["socks"] },
@@ -503,7 +514,7 @@ dependencies = [
503
514
  { name = "tomlkit" },
504
515
  ]
505
516
  wheels = [
506
- { url = "https://files.pythonhosted.org/packages/c7/74/072e33a54a0cdbd7699a17f32950f8fd57e766ce74b768ccb4128cc5da24/mm_std-0.3.9-py3-none-any.whl", hash = "sha256:3ad5ee2fe720e939169a23aa7c9a5f3c7cee25232a184bb3415720ef740ddd04", size = 15581 },
517
+ { url = "https://files.pythonhosted.org/packages/47/61/cd5cbfc1868493ffda46c7d86d94459110ec4dd28c1883936f18f36bfab0/mm_std-0.3.14-py3-none-any.whl", hash = "sha256:db0a3a66590f456ad8c3f268d545803be63339c4b72f3197bc377d101a93ad02", size = 20219 },
507
518
  ]
508
519
 
509
520
  [[package]]
@@ -752,6 +763,19 @@ wheels = [
752
763
  { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
753
764
  ]
754
765
 
766
+ [[package]]
767
+ name = "pydantic-settings"
768
+ version = "2.8.1"
769
+ source = { registry = "https://pypi.org/simple" }
770
+ dependencies = [
771
+ { name = "pydantic" },
772
+ { name = "python-dotenv" },
773
+ ]
774
+ sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 }
775
+ wheels = [
776
+ { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 },
777
+ ]
778
+
755
779
  [[package]]
756
780
  name = "pydash"
757
781
  version = "8.0.5"
@@ -889,27 +913,27 @@ wheels = [
889
913
 
890
914
  [[package]]
891
915
  name = "ruff"
892
- version = "0.10.0"
893
- source = { registry = "https://pypi.org/simple" }
894
- sdist = { url = "https://files.pythonhosted.org/packages/4c/ec/9c59d2956566517c98ac8267554f4eaceafb2a19710a429368518b7fab43/ruff-0.10.0.tar.gz", hash = "sha256:fa1554e18deaf8aa097dbcfeafaf38b17a2a1e98fdc18f50e62e8a836abee392", size = 3789921 }
895
- wheels = [
896
- { url = "https://files.pythonhosted.org/packages/bf/3f/742afe91b43def2a75990b293c676355576c0ff9cdbcf4249f78fa592544/ruff-0.10.0-py3-none-linux_armv6l.whl", hash = "sha256:46a2aa0eaae5048e5f804f0be9489d8a661633e23277b7293089e70d5c1a35c4", size = 10078369 },
897
- { url = "https://files.pythonhosted.org/packages/8d/a0/8696fb4862e82f7b40bbbc2917137594b22826cc62d77278a91391507514/ruff-0.10.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:775a6bc61af9dd0a2e1763406522a137e62aabb743d8b43ed95f019cdd1526c7", size = 10876912 },
898
- { url = "https://files.pythonhosted.org/packages/40/aa/0d48b7b7d7a1f168bb8fd893ed559d633c7d68c4a8ef9b996f0c2bd07aca/ruff-0.10.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8b03e6fcd39d20f0004f9956f0ed5eadc404d3a299f9d9286323884e3b663730", size = 10229962 },
899
- { url = "https://files.pythonhosted.org/packages/21/de/861ced2f75b045d8cfc038d68961d8ac117344df1f43a11abdd05bf7991b/ruff-0.10.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:621101d1af80248827f2409a78c8177c8319986a57b4663613b9c72f8617bfcd", size = 10404627 },
900
- { url = "https://files.pythonhosted.org/packages/21/69/666e0b840191c3ce433962c0d05fc0f6800afe259ea5d230cc731655d8e2/ruff-0.10.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2dfe85cb6bfbd4259801e7d4982f2a72bdbd5749dc73a09d68a6dbf77f2209a", size = 9939383 },
901
- { url = "https://files.pythonhosted.org/packages/76/bf/34a2adc58092c99cdfa9f1303acd82d840d56412022e477e2ab20c261d2d/ruff-0.10.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43ac3879a20c22fdc57e559f0bb27f0c71828656841d0b42d3505b1e5b3a83c8", size = 11492269 },
902
- { url = "https://files.pythonhosted.org/packages/31/3d/f7ccfcf69f15948623b190feea9d411d5029ae39725fcc078f8d43bd07a6/ruff-0.10.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ef5e3aac421bbc62f8a7aab21edd49a359ed42205f7a5091a74386bca1efa293", size = 12186939 },
903
- { url = "https://files.pythonhosted.org/packages/6e/3e/c557c0abfdea85c7d238a3cb238c73e7b6d17c30a584234c4fd8fe2cafb6/ruff-0.10.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f4f62d7fac8b748fce67ad308116b4d4cc1a9f964b4804fc5408fbd06e13ba9", size = 11655896 },
904
- { url = "https://files.pythonhosted.org/packages/3b/8e/3bfa110f37e5192eb3943f14943d05fbb9a76fea380aa87655e6f6276a54/ruff-0.10.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:02f9f6205c5b0d626f98da01a0e75b724a64c21c554bba24b12522c9e9ba6a04", size = 13885502 },
905
- { url = "https://files.pythonhosted.org/packages/51/4a/22cdab59b5563dd7f4c504d0f1e6bb25fc800a5a057395bc24f8ff3a85b2/ruff-0.10.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46a97f3d55f68464c48d1e929a8582c7e5bb80ac73336bbc7b0da894d8e6cd9e", size = 11344767 },
906
- { url = "https://files.pythonhosted.org/packages/3d/0f/8f85de2ac565f82f47c6d8fb7ae04383e6300560f2d1b91c1268ff91e507/ruff-0.10.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0b811197d0dc96c13d610f8cfdc56030b405bcff5c2f10eab187b329da0ca4a", size = 10300331 },
907
- { url = "https://files.pythonhosted.org/packages/90/4a/b337df327832cb30bd8607e8d1fdf1b6b5ca228307d5008dd49028fb66ae/ruff-0.10.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a13a3fda0870c1c964b47ff5d73805ae80d2a9de93ee2d185d453b8fddf85a84", size = 9926551 },
908
- { url = "https://files.pythonhosted.org/packages/d7/e9/141233730b85675ac806c4b62f70516bd9c0aae8a55823f3a6589ed411be/ruff-0.10.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6ceb8d9f062e90ddcbad929f6136edf764bbf6411420a07e8357602ea28cd99f", size = 10925061 },
909
- { url = "https://files.pythonhosted.org/packages/24/09/02987935b55c2d353a226ac1b4f9718830e2e195834929f46c07eeede746/ruff-0.10.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c41d07d573617ed2f287ea892af2446fd8a8d877481e8e1ba6928e020665d240", size = 11394949 },
910
- { url = "https://files.pythonhosted.org/packages/d6/ec/054f9879fb6f4122d43ffe5c9f88c8c323a9cd14220d5c813aea5805e02c/ruff-0.10.0-py3-none-win32.whl", hash = "sha256:76e2de0cbdd587e373cd3b4050d2c45babdd7014c1888a6f121c29525c748a15", size = 10272077 },
911
- { url = "https://files.pythonhosted.org/packages/6e/49/915d8682f24645b904fe6a1aac36101464fc814923fdf293c1388dc5533c/ruff-0.10.0-py3-none-win_amd64.whl", hash = "sha256:f943acdecdcc6786a8d1dad455dd9f94e6d57ccc115be4993f9b52ef8316027a", size = 11393300 },
912
- { url = "https://files.pythonhosted.org/packages/82/ed/5c59941634c9026ceeccc7c119f23f4356f09aafd28c15c1bc734ac66b01/ruff-0.10.0-py3-none-win_arm64.whl", hash = "sha256:935a943bdbd9ff0685acd80d484ea91088e27617537b5f7ef8907187d19d28d0", size = 10510133 },
916
+ version = "0.11.2"
917
+ source = { registry = "https://pypi.org/simple" }
918
+ sdist = { url = "https://files.pythonhosted.org/packages/90/61/fb87430f040e4e577e784e325351186976516faef17d6fcd921fe28edfd7/ruff-0.11.2.tar.gz", hash = "sha256:ec47591497d5a1050175bdf4e1a4e6272cddff7da88a2ad595e1e326041d8d94", size = 3857511 }
919
+ wheels = [
920
+ { url = "https://files.pythonhosted.org/packages/62/99/102578506f0f5fa29fd7e0df0a273864f79af044757aef73d1cae0afe6ad/ruff-0.11.2-py3-none-linux_armv6l.whl", hash = "sha256:c69e20ea49e973f3afec2c06376eb56045709f0212615c1adb0eda35e8a4e477", size = 10113146 },
921
+ { url = "https://files.pythonhosted.org/packages/74/ad/5cd4ba58ab602a579997a8494b96f10f316e874d7c435bcc1a92e6da1b12/ruff-0.11.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2c5424cc1c4eb1d8ecabe6d4f1b70470b4f24a0c0171356290b1953ad8f0e272", size = 10867092 },
922
+ { url = "https://files.pythonhosted.org/packages/fc/3e/d3f13619e1d152c7b600a38c1a035e833e794c6625c9a6cea6f63dbf3af4/ruff-0.11.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ecf20854cc73f42171eedb66f006a43d0a21bfb98a2523a809931cda569552d9", size = 10224082 },
923
+ { url = "https://files.pythonhosted.org/packages/90/06/f77b3d790d24a93f38e3806216f263974909888fd1e826717c3ec956bbcd/ruff-0.11.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c543bf65d5d27240321604cee0633a70c6c25c9a2f2492efa9f6d4b8e4199bb", size = 10394818 },
924
+ { url = "https://files.pythonhosted.org/packages/99/7f/78aa431d3ddebfc2418cd95b786642557ba8b3cb578c075239da9ce97ff9/ruff-0.11.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:20967168cc21195db5830b9224be0e964cc9c8ecf3b5a9e3ce19876e8d3a96e3", size = 9952251 },
925
+ { url = "https://files.pythonhosted.org/packages/30/3e/f11186d1ddfaca438c3bbff73c6a2fdb5b60e6450cc466129c694b0ab7a2/ruff-0.11.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:955a9ce63483999d9f0b8f0b4a3ad669e53484232853054cc8b9d51ab4c5de74", size = 11563566 },
926
+ { url = "https://files.pythonhosted.org/packages/22/6c/6ca91befbc0a6539ee133d9a9ce60b1a354db12c3c5d11cfdbf77140f851/ruff-0.11.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:86b3a27c38b8fce73bcd262b0de32e9a6801b76d52cdb3ae4c914515f0cef608", size = 12208721 },
927
+ { url = "https://files.pythonhosted.org/packages/19/b0/24516a3b850d55b17c03fc399b681c6a549d06ce665915721dc5d6458a5c/ruff-0.11.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3b66a03b248c9fcd9d64d445bafdf1589326bee6fc5c8e92d7562e58883e30f", size = 11662274 },
928
+ { url = "https://files.pythonhosted.org/packages/d7/65/76be06d28ecb7c6070280cef2bcb20c98fbf99ff60b1c57d2fb9b8771348/ruff-0.11.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0397c2672db015be5aa3d4dac54c69aa012429097ff219392c018e21f5085147", size = 13792284 },
929
+ { url = "https://files.pythonhosted.org/packages/ce/d2/4ceed7147e05852876f3b5f3fdc23f878ce2b7e0b90dd6e698bda3d20787/ruff-0.11.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:869bcf3f9abf6457fbe39b5a37333aa4eecc52a3b99c98827ccc371a8e5b6f1b", size = 11327861 },
930
+ { url = "https://files.pythonhosted.org/packages/c4/78/4935ecba13706fd60ebe0e3dc50371f2bdc3d9bc80e68adc32ff93914534/ruff-0.11.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2a2b50ca35457ba785cd8c93ebbe529467594087b527a08d487cf0ee7b3087e9", size = 10276560 },
931
+ { url = "https://files.pythonhosted.org/packages/81/7f/1b2435c3f5245d410bb5dc80f13ec796454c21fbda12b77d7588d5cf4e29/ruff-0.11.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7c69c74bf53ddcfbc22e6eb2f31211df7f65054bfc1f72288fc71e5f82db3eab", size = 9945091 },
932
+ { url = "https://files.pythonhosted.org/packages/39/c4/692284c07e6bf2b31d82bb8c32f8840f9d0627d92983edaac991a2b66c0a/ruff-0.11.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6e8fb75e14560f7cf53b15bbc55baf5ecbe373dd5f3aab96ff7aa7777edd7630", size = 10977133 },
933
+ { url = "https://files.pythonhosted.org/packages/94/cf/8ab81cb7dd7a3b0a3960c2769825038f3adcd75faf46dd6376086df8b128/ruff-0.11.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:842a472d7b4d6f5924e9297aa38149e5dcb1e628773b70e6387ae2c97a63c58f", size = 11378514 },
934
+ { url = "https://files.pythonhosted.org/packages/d9/3a/a647fa4f316482dacf2fd68e8a386327a33d6eabd8eb2f9a0c3d291ec549/ruff-0.11.2-py3-none-win32.whl", hash = "sha256:aca01ccd0eb5eb7156b324cfaa088586f06a86d9e5314b0eb330cb48415097cc", size = 10319835 },
935
+ { url = "https://files.pythonhosted.org/packages/86/54/3c12d3af58012a5e2cd7ebdbe9983f4834af3f8cbea0e8a8c74fa1e23b2b/ruff-0.11.2-py3-none-win_amd64.whl", hash = "sha256:3170150172a8f994136c0c66f494edf199a0bbea7a409f649e4bc8f4d7084080", size = 11373713 },
936
+ { url = "https://files.pythonhosted.org/packages/d6/d4/dd813703af8a1e2ac33bf3feb27e8a5ad514c9f219df80c64d69807e7f71/ruff-0.11.2-py3-none-win_arm64.whl", hash = "sha256:52933095158ff328f4c77af3d74f0379e34fd52f175144cefc1b192e7ccd32b4", size = 10441990 },
913
937
  ]
914
938
 
915
939
  [[package]]
@@ -957,22 +981,22 @@ wheels = [
957
981
 
958
982
  [[package]]
959
983
  name = "solders"
960
- version = "0.23.0"
984
+ version = "0.26.0"
961
985
  source = { registry = "https://pypi.org/simple" }
962
986
  dependencies = [
963
987
  { name = "jsonalias" },
964
988
  { name = "typing-extensions" },
965
989
  ]
966
- sdist = { url = "https://files.pythonhosted.org/packages/fc/ab/9942f251097ea262bea3a2d0b6c45040442c10c5787b71240c2820eaccda/solders-0.23.0.tar.gz", hash = "sha256:f25e8908cca40a766a99dee1aa983fb7816949201b40ec927d0802debd3ec0e4", size = 182285 }
990
+ sdist = { url = "https://files.pythonhosted.org/packages/87/96/23ad2e43e2676b78834064fe051e3db3ce1899336ecd4797f92fcd06113a/solders-0.26.0.tar.gz", hash = "sha256:057533892d6fa432c1ce1e2f5e3428802964666c10b57d3d1bcaab86295f046c", size = 181123 }
967
991
  wheels = [
968
- { url = "https://files.pythonhosted.org/packages/06/d6/c1eb92ea5549cfee8f62a3488de29923008c10e87898bba39d6201f845ed/solders-0.23.0-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:87c2865f4f634abf56cc3fe9cfea985ddd0a09097d01b7716ed83ff6ef2ad1c6", size = 27236077 },
969
- { url = "https://files.pythonhosted.org/packages/4b/6c/f63ef9925a053a013d45c1ec4a67ae485ea221d911d124454bef0368a85d/solders-0.23.0-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:183fdc05ed0d6004d24bef17c01794a17be4a90f71c31429207bf33ec2e7738d", size = 6926426 },
970
- { url = "https://files.pythonhosted.org/packages/32/5d/867a0769783046051fa745df21a3b0ef00ea8b6d9671657f490d20e9dda1/solders-0.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1ba84ec23fc7af821dc7e5b48a6d247ae36b86457f930cbc64afa79587e4caf", size = 15470544 },
971
- { url = "https://files.pythonhosted.org/packages/c6/69/a33543e8ffa399e34aae46272d41a810d9ea5e1285f66e3547da7c70e359/solders-0.23.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:37d097f4a3e55e8870b9a81e7675834aa9b516e956c8b0c18adcac742573378c", size = 7133781 },
972
- { url = "https://files.pythonhosted.org/packages/e1/21/44ec75b4c6ffa56f5a1bb09046156f41b5e05a397bbaf7289027de1bc551/solders-0.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6319929399fdd04d10c5cebbd76993e58d92ade05febec5a488eed7810aae270", size = 7457170 },
973
- { url = "https://files.pythonhosted.org/packages/ea/e6/ffee411c48ca3ffeedf1bf9a633eac5d26240092a7e551a206a51495a992/solders-0.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:67514b6dc2ecec9e15198e70eac329eac7b191f0f063c6ab80748b9ff921c3d5", size = 7259632 },
974
- { url = "https://files.pythonhosted.org/packages/f9/14/902c802f44e09989feee5f750f5d5621041a681c222153cddb21c2dbe3f8/solders-0.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9cb2360a47f3f4772903dfb3314ec57b637f7fc8ac30690fec28d48e16edacf4", size = 7205242 },
975
- { url = "https://files.pythonhosted.org/packages/0d/6b/fd205612876b489f9c70bad04d8d7a98d462fb338a142e02991822f4f359/solders-0.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:2976f6589a5a0a45600aee83b34312ad0d96ea03f8d358493143fc01223997c3", size = 5530329 },
992
+ { url = "https://files.pythonhosted.org/packages/a5/ce/58bbb4d2c696e770cdd37e5f6dc2891ef7610c0c085bf400f9c42dcff1ad/solders-0.26.0-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9c1a0ef5daa1a05934af5fb6e7e32eab7c42cede406c80067fee006f461ffc4a", size = 24344472 },
993
+ { url = "https://files.pythonhosted.org/packages/5a/35/221cec0e5900c2202833e7e9110c3405a2d96ed25e110b247f88b8782e29/solders-0.26.0-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1b964efbd7c0b38aef3bf4293ea5938517ae649b9a23e7cd147d889931775aab", size = 6674734 },
994
+ { url = "https://files.pythonhosted.org/packages/41/33/d17b7dbc92672351d59fc65cdb93b8924fc682deba09f6d96f25440187ae/solders-0.26.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e6a769c5298b887b7588edb171d93709a89302aef75913fe893d11c653739d", size = 13472961 },
995
+ { url = "https://files.pythonhosted.org/packages/bb/e7/533367d815ab000587ccc37d89e154132f63347f02dcaaac5df72bd851de/solders-0.26.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b3cc55b971ec6ed1b4466fa7e7e09eee9baba492b8cd9e3204e3e1a0c5a0c4aa", size = 6886198 },
996
+ { url = "https://files.pythonhosted.org/packages/52/e0/ab41ab3df5fdf3b0e55613be93a43c2fe58b15a6ea8ceca26d3fba02e3c6/solders-0.26.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3e3973074c17265921c70246a17bcf80972c5b96a3e1ed7f5049101f11865092", size = 7319170 },
997
+ { url = "https://files.pythonhosted.org/packages/7d/34/5174ce592607e0ac020aff203217f2f113a55eec49af3db12945fea42d89/solders-0.26.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:59b52419452602f697e659199a25acacda8365971c376ef3c0687aecdd929e07", size = 7134977 },
998
+ { url = "https://files.pythonhosted.org/packages/ba/5e/822faabda0d473c29bdf59fe8869a411fd436af8ca6f5d6e89f7513f682f/solders-0.26.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5946ec3f2a340afa9ce5c2b8ab628ae1dea2ad2235551b1297cafdd7e3e5c51a", size = 6984222 },
999
+ { url = "https://files.pythonhosted.org/packages/23/e8/dc992f677762ea2de44b7768120d95887ef39fab10d6f29fb53e6a9882c1/solders-0.26.0-cp37-abi3-win_amd64.whl", hash = "sha256:5466616610170aab08c627ae01724e425bcf90085bc574da682e9f3bd954900b", size = 5480492 },
976
1000
  ]
977
1001
 
978
1002
  [[package]]
@@ -1,86 +0,0 @@
1
- import httpx
2
- from mm_crypto_utils import Nodes, Proxies, random_node, random_proxy
3
- from mm_std import Err, Ok, Result
4
- from solana.exceptions import SolanaRpcException
5
- from solana.rpc.core import RPCException
6
- from solders.pubkey import Pubkey
7
- from solders.rpc.errors import InvalidParamsMessage
8
- from spl.token.instructions import get_associated_token_address
9
-
10
- from mm_sol import rpc
11
- from mm_sol.utils import get_client
12
-
13
-
14
- def get_sol_balance(node: str, address: str, timeout: int = 10, proxy: str | None = None) -> Result[int]:
15
- return rpc.get_balance(node, address, timeout, proxy)
16
-
17
-
18
- def get_sol_balance_with_retries(
19
- nodes: Nodes, address: str, retries: int, timeout: int = 10, proxies: Proxies = None
20
- ) -> Result[int]:
21
- res: Result[int] = Err("not started yet")
22
- for _ in range(retries):
23
- res = get_sol_balance(random_node(nodes), address, timeout=timeout, proxy=random_proxy(proxies))
24
- if res.is_ok():
25
- return res
26
- return res
27
-
28
-
29
- def get_token_balance(
30
- node: str,
31
- owner_address: str,
32
- token_mint_address: str,
33
- token_account: str | None = None,
34
- timeout: float = 10,
35
- proxy: str | None = None,
36
- ) -> Result[int]:
37
- try:
38
- client = get_client(node, proxy=proxy, timeout=timeout)
39
- if not token_account:
40
- token_account = str(
41
- get_associated_token_address(Pubkey.from_string(owner_address), Pubkey.from_string(token_mint_address))
42
- )
43
-
44
- res = client.get_token_account_balance(Pubkey.from_string(token_account))
45
-
46
- # Sometimes it not raise an error, but it returns this :(
47
- if isinstance(res, InvalidParamsMessage) and "could not find account" in res.message:
48
- return Ok(0)
49
- return Ok(int(res.value.amount), data=res.to_json())
50
- except RPCException as e:
51
- if len(e.args) > 1:
52
- s = e.args[0]
53
- if isinstance(s, InvalidParamsMessage) and "could not find account" in s.message:
54
- return Ok(0)
55
- return Err(e)
56
- except httpx.HTTPStatusError as e:
57
- return Err(f"http error: {e}")
58
- except SolanaRpcException as e:
59
- return Err(e.error_msg)
60
- except Exception as e:
61
- return Err(e)
62
-
63
-
64
- def get_token_balance_with_retries(
65
- nodes: Nodes,
66
- owner_address: str,
67
- token_mint_address: str,
68
- retries: int,
69
- token_account: str | None = None,
70
- timeout: float = 10,
71
- proxies: Proxies = None,
72
- ) -> Result[int]:
73
- res: Result[int] = Err("not started yet")
74
- for _ in range(retries):
75
- res = get_token_balance(
76
- random_node(nodes),
77
- owner_address,
78
- token_mint_address,
79
- token_account,
80
- timeout=timeout,
81
- proxy=random_proxy(proxies),
82
- )
83
- if res.is_ok():
84
- return res
85
-
86
- return res
@@ -1,6 +0,0 @@
1
- from mm_sol.balance import get_sol_balance
2
-
3
-
4
- def test_get_balance(mainnet_node, usdt_owner_address, random_proxy):
5
- res = get_sol_balance(mainnet_node, usdt_owner_address, proxy=random_proxy)
6
- assert res.unwrap() > 10
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