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.
- {mm_sol-0.5.4 → mm_sol-0.5.6}/PKG-INFO +3 -2
- {mm_sol-0.5.4 → mm_sol-0.5.6}/README.md +3 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/pyproject.toml +5 -3
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/account.py +5 -5
- mm_sol-0.5.6/src/mm_sol/async_rpc.py +42 -0
- mm_sol-0.5.6/src/mm_sol/balance.py +158 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/rpc.py +13 -13
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/utils.py +11 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/conftest.py +6 -1
- mm_sol-0.5.6/tests/test_async_rpc.py +10 -0
- mm_sol-0.5.6/tests/test_balance.py +25 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/uv.lock +65 -41
- mm_sol-0.5.4/src/mm_sol/balance.py +0 -86
- mm_sol-0.5.4/tests/test_balance.py +0 -6
- {mm_sol-0.5.4 → mm_sol-0.5.6}/.env.example +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/.gitignore +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/dict.dic +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/justfile +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/block.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/calcs.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cli.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cli_utils.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/balance_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/balances_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/example_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/node_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/transfer_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/wallet/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/wallet/keypair_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/cmd/wallet/mnemonic_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/examples/balances.toml +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/examples/transfer.toml +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/cli/validators.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/constants.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/converters.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/py.typed +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/solana_cli.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/token.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/src/mm_sol/transfer.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/cmd/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/cmd/wallet/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/cmd/wallet/test_keypair_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/cli/cmd/wallet/test_mnemonic_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/test_account.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/test_client.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/test_converters.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.6}/tests/test_rpc.py +0 -0
- {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.
|
|
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.
|
|
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
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mm-sol"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.6"
|
|
4
4
|
description = ""
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
dependencies = [
|
|
7
|
-
"mm-crypto-utils>=0.2.
|
|
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.
|
|
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.
|
|
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.
|
|
65
|
-
private_key_arr = list(keypair.
|
|
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.
|
|
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.
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
@@ -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.
|
|
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/
|
|
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/
|
|
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.
|
|
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/
|
|
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.
|
|
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.
|
|
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.
|
|
497
|
+
{ name = "ruff", specifier = "~=0.11.2" },
|
|
490
498
|
]
|
|
491
499
|
|
|
492
500
|
[[package]]
|
|
493
501
|
name = "mm-std"
|
|
494
|
-
version = "0.3.
|
|
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/
|
|
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.
|
|
893
|
-
source = { registry = "https://pypi.org/simple" }
|
|
894
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
895
|
-
wheels = [
|
|
896
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
897
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
898
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
899
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
900
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
901
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
902
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
903
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
904
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
905
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
906
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
907
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
908
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
909
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
910
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
911
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
912
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
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.
|
|
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/
|
|
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/
|
|
969
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
970
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
971
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
972
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
973
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
974
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
975
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
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
|
|
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
|
|
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
|