mm-sol 0.5.4__tar.gz → 0.5.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.
- {mm_sol-0.5.4 → mm_sol-0.5.5}/PKG-INFO +2 -2
- {mm_sol-0.5.4 → mm_sol-0.5.5}/README.md +3 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/pyproject.toml +4 -3
- mm_sol-0.5.5/src/mm_sol/async_rpc.py +42 -0
- mm_sol-0.5.5/src/mm_sol/balance.py +161 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/rpc.py +13 -13
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/utils.py +11 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/conftest.py +6 -1
- mm_sol-0.5.5/tests/test_async_rpc.py +10 -0
- mm_sol-0.5.5/tests/test_balance.py +25 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/uv.lock +53 -31
- 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.5}/.env.example +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/.gitignore +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/dict.dic +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/justfile +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/account.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/block.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/calcs.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cli.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cli_utils.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cmd/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cmd/balance_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cmd/balances_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cmd/example_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cmd/node_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cmd/transfer_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cmd/wallet/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cmd/wallet/keypair_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/cmd/wallet/mnemonic_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/examples/balances.toml +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/examples/transfer.toml +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/cli/validators.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/constants.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/converters.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/py.typed +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/solana_cli.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/token.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/src/mm_sol/transfer.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/cli/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/cli/cmd/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/cli/cmd/wallet/__init__.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/cli/cmd/wallet/test_keypair_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/cli/cmd/wallet/test_mnemonic_cmd.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/test_account.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/test_client.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/test_converters.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/test_rpc.py +0 -0
- {mm_sol-0.5.4 → mm_sol-0.5.5}/tests/test_token.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mm-sol
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.5
|
|
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
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mm-sol"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.5"
|
|
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
9
|
"base58~=2.1.1",
|
|
10
10
|
"mnemonic==0.21",
|
|
@@ -23,7 +23,7 @@ build-backend = "hatchling.build"
|
|
|
23
23
|
dev-dependencies = [
|
|
24
24
|
"pytest~=8.3.5",
|
|
25
25
|
"pytest-xdist~=3.6.1",
|
|
26
|
-
"ruff~=0.
|
|
26
|
+
"ruff~=0.11.2",
|
|
27
27
|
"pip-audit~=2.8.0",
|
|
28
28
|
"bandit~=1.8.3",
|
|
29
29
|
"mypy~=1.15.0",
|
|
@@ -64,6 +64,7 @@ ignore = [
|
|
|
64
64
|
"TD003", # flake8-todos: missing-todo-link
|
|
65
65
|
"RET503", # flake8-return: implicit-return
|
|
66
66
|
"COM812", # it's used in ruff formatter
|
|
67
|
+
"ASYNC109", # flake8-async: async-function-with-timeout
|
|
67
68
|
]
|
|
68
69
|
[tool.ruff.lint.pep8-naming]
|
|
69
70
|
classmethod-decorators = ["field_validator"]
|
|
@@ -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,161 @@
|
|
|
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 len(e.args) > 1:
|
|
67
|
+
s = e.args[0]
|
|
68
|
+
if isinstance(s, InvalidParamsMessage) and "could not find account" in s.message:
|
|
69
|
+
return Ok(0)
|
|
70
|
+
return Err(e)
|
|
71
|
+
except httpx.HTTPStatusError as e:
|
|
72
|
+
return Err(f"http error: {e}")
|
|
73
|
+
except SolanaRpcException as e:
|
|
74
|
+
return Err(e.error_msg)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
return Err(e)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
async def get_token_balance_async(
|
|
80
|
+
node: str,
|
|
81
|
+
owner_address: str,
|
|
82
|
+
token_mint_address: str,
|
|
83
|
+
token_account: str | None = None,
|
|
84
|
+
timeout: float = 10,
|
|
85
|
+
proxy: str | None = None,
|
|
86
|
+
) -> Result[int]:
|
|
87
|
+
try:
|
|
88
|
+
client = get_async_client(node, proxy=proxy, timeout=timeout)
|
|
89
|
+
if not token_account:
|
|
90
|
+
token_account = str(
|
|
91
|
+
get_associated_token_address(Pubkey.from_string(owner_address), Pubkey.from_string(token_mint_address))
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
res = await client.get_token_account_balance(Pubkey.from_string(token_account))
|
|
95
|
+
|
|
96
|
+
# Sometimes it not raise an error, but it returns this :(
|
|
97
|
+
if isinstance(res, InvalidParamsMessage) and "could not find account" in res.message:
|
|
98
|
+
return Ok(0)
|
|
99
|
+
return Ok(int(res.value.amount), data=res.to_json())
|
|
100
|
+
except RPCException as e:
|
|
101
|
+
if len(e.args) > 1:
|
|
102
|
+
s = e.args[0]
|
|
103
|
+
if isinstance(s, InvalidParamsMessage) and "could not find account" in s.message:
|
|
104
|
+
return Ok(0)
|
|
105
|
+
return Err(e)
|
|
106
|
+
except httpx.HTTPStatusError as e:
|
|
107
|
+
return Err(f"http error: {e}")
|
|
108
|
+
except SolanaRpcException as e:
|
|
109
|
+
return Err(e.error_msg)
|
|
110
|
+
except Exception as e:
|
|
111
|
+
return Err(e)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_token_balance_with_retries(
|
|
115
|
+
nodes: Nodes,
|
|
116
|
+
owner_address: str,
|
|
117
|
+
token_mint_address: str,
|
|
118
|
+
retries: int,
|
|
119
|
+
token_account: str | None = None,
|
|
120
|
+
timeout: float = 10,
|
|
121
|
+
proxies: Proxies = None,
|
|
122
|
+
) -> Result[int]:
|
|
123
|
+
res: Result[int] = Err("not started yet")
|
|
124
|
+
for _ in range(retries):
|
|
125
|
+
res = get_token_balance(
|
|
126
|
+
random_node(nodes),
|
|
127
|
+
owner_address,
|
|
128
|
+
token_mint_address,
|
|
129
|
+
token_account,
|
|
130
|
+
timeout=timeout,
|
|
131
|
+
proxy=random_proxy(proxies),
|
|
132
|
+
)
|
|
133
|
+
if res.is_ok():
|
|
134
|
+
return res
|
|
135
|
+
|
|
136
|
+
return res
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
async def get_token_balance_with_retries_async(
|
|
140
|
+
nodes: Nodes,
|
|
141
|
+
owner_address: str,
|
|
142
|
+
token_mint_address: str,
|
|
143
|
+
retries: int,
|
|
144
|
+
token_account: str | None = None,
|
|
145
|
+
timeout: float = 10,
|
|
146
|
+
proxies: Proxies = None,
|
|
147
|
+
) -> Result[int]:
|
|
148
|
+
res: Result[int] = Err("not started yet")
|
|
149
|
+
for _ in range(retries):
|
|
150
|
+
res = await get_token_balance_async(
|
|
151
|
+
random_node(nodes),
|
|
152
|
+
owner_address,
|
|
153
|
+
token_mint_address,
|
|
154
|
+
token_account,
|
|
155
|
+
timeout=timeout,
|
|
156
|
+
proxy=random_proxy(proxies),
|
|
157
|
+
)
|
|
158
|
+
if res.is_ok():
|
|
159
|
+
return res
|
|
160
|
+
|
|
161
|
+
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.5"
|
|
450
456
|
source = { editable = "." }
|
|
451
457
|
dependencies = [
|
|
452
458
|
{ name = "base58" },
|
|
@@ -472,7 +478,7 @@ dev = [
|
|
|
472
478
|
requires-dist = [
|
|
473
479
|
{ name = "base58", specifier = "~=2.1.1" },
|
|
474
480
|
{ name = "jinja2", specifier = ">=3.1.6" },
|
|
475
|
-
{ name = "mm-crypto-utils", specifier = ">=0.2.
|
|
481
|
+
{ name = "mm-crypto-utils", specifier = ">=0.2.9" },
|
|
476
482
|
{ name = "mnemonic", specifier = "==0.21" },
|
|
477
483
|
{ name = "socksio", specifier = ">=1.0.0" },
|
|
478
484
|
{ name = "solana", specifier = "~=0.36.6" },
|
|
@@ -486,16 +492,19 @@ dev = [
|
|
|
486
492
|
{ name = "pip-audit", specifier = "~=2.8.0" },
|
|
487
493
|
{ name = "pytest", specifier = "~=8.3.5" },
|
|
488
494
|
{ name = "pytest-xdist", specifier = "~=3.6.1" },
|
|
489
|
-
{ name = "ruff", specifier = "~=0.
|
|
495
|
+
{ name = "ruff", specifier = "~=0.11.2" },
|
|
490
496
|
]
|
|
491
497
|
|
|
492
498
|
[[package]]
|
|
493
499
|
name = "mm-std"
|
|
494
|
-
version = "0.3.
|
|
500
|
+
version = "0.3.14"
|
|
495
501
|
source = { registry = "https://pypi.org/simple" }
|
|
496
502
|
dependencies = [
|
|
503
|
+
{ name = "anyio" },
|
|
497
504
|
{ name = "cryptography" },
|
|
505
|
+
{ name = "httpx", extra = ["socks"] },
|
|
498
506
|
{ name = "pydantic" },
|
|
507
|
+
{ name = "pydantic-settings" },
|
|
499
508
|
{ name = "pydash" },
|
|
500
509
|
{ name = "python-dotenv" },
|
|
501
510
|
{ name = "requests", extra = ["socks"] },
|
|
@@ -503,7 +512,7 @@ dependencies = [
|
|
|
503
512
|
{ name = "tomlkit" },
|
|
504
513
|
]
|
|
505
514
|
wheels = [
|
|
506
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
515
|
+
{ url = "https://files.pythonhosted.org/packages/47/61/cd5cbfc1868493ffda46c7d86d94459110ec4dd28c1883936f18f36bfab0/mm_std-0.3.14-py3-none-any.whl", hash = "sha256:db0a3a66590f456ad8c3f268d545803be63339c4b72f3197bc377d101a93ad02", size = 20219 },
|
|
507
516
|
]
|
|
508
517
|
|
|
509
518
|
[[package]]
|
|
@@ -752,6 +761,19 @@ wheels = [
|
|
|
752
761
|
{ 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
762
|
]
|
|
754
763
|
|
|
764
|
+
[[package]]
|
|
765
|
+
name = "pydantic-settings"
|
|
766
|
+
version = "2.8.1"
|
|
767
|
+
source = { registry = "https://pypi.org/simple" }
|
|
768
|
+
dependencies = [
|
|
769
|
+
{ name = "pydantic" },
|
|
770
|
+
{ name = "python-dotenv" },
|
|
771
|
+
]
|
|
772
|
+
sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 }
|
|
773
|
+
wheels = [
|
|
774
|
+
{ url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 },
|
|
775
|
+
]
|
|
776
|
+
|
|
755
777
|
[[package]]
|
|
756
778
|
name = "pydash"
|
|
757
779
|
version = "8.0.5"
|
|
@@ -889,27 +911,27 @@ wheels = [
|
|
|
889
911
|
|
|
890
912
|
[[package]]
|
|
891
913
|
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/
|
|
914
|
+
version = "0.11.2"
|
|
915
|
+
source = { registry = "https://pypi.org/simple" }
|
|
916
|
+
sdist = { url = "https://files.pythonhosted.org/packages/90/61/fb87430f040e4e577e784e325351186976516faef17d6fcd921fe28edfd7/ruff-0.11.2.tar.gz", hash = "sha256:ec47591497d5a1050175bdf4e1a4e6272cddff7da88a2ad595e1e326041d8d94", size = 3857511 }
|
|
917
|
+
wheels = [
|
|
918
|
+
{ url = "https://files.pythonhosted.org/packages/62/99/102578506f0f5fa29fd7e0df0a273864f79af044757aef73d1cae0afe6ad/ruff-0.11.2-py3-none-linux_armv6l.whl", hash = "sha256:c69e20ea49e973f3afec2c06376eb56045709f0212615c1adb0eda35e8a4e477", size = 10113146 },
|
|
919
|
+
{ 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 },
|
|
920
|
+
{ 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 },
|
|
921
|
+
{ 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 },
|
|
922
|
+
{ 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 },
|
|
923
|
+
{ 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 },
|
|
924
|
+
{ 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 },
|
|
925
|
+
{ 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 },
|
|
926
|
+
{ 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 },
|
|
927
|
+
{ 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 },
|
|
928
|
+
{ 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 },
|
|
929
|
+
{ 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 },
|
|
930
|
+
{ 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 },
|
|
931
|
+
{ 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 },
|
|
932
|
+
{ url = "https://files.pythonhosted.org/packages/d9/3a/a647fa4f316482dacf2fd68e8a386327a33d6eabd8eb2f9a0c3d291ec549/ruff-0.11.2-py3-none-win32.whl", hash = "sha256:aca01ccd0eb5eb7156b324cfaa088586f06a86d9e5314b0eb330cb48415097cc", size = 10319835 },
|
|
933
|
+
{ url = "https://files.pythonhosted.org/packages/86/54/3c12d3af58012a5e2cd7ebdbe9983f4834af3f8cbea0e8a8c74fa1e23b2b/ruff-0.11.2-py3-none-win_amd64.whl", hash = "sha256:3170150172a8f994136c0c66f494edf199a0bbea7a409f649e4bc8f4d7084080", size = 11373713 },
|
|
934
|
+
{ url = "https://files.pythonhosted.org/packages/d6/d4/dd813703af8a1e2ac33bf3feb27e8a5ad514c9f219df80c64d69807e7f71/ruff-0.11.2-py3-none-win_arm64.whl", hash = "sha256:52933095158ff328f4c77af3d74f0379e34fd52f175144cefc1b192e7ccd32b4", size = 10441990 },
|
|
913
935
|
]
|
|
914
936
|
|
|
915
937
|
[[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
|
|
File without changes
|