mm-sol 0.6.0__py3-none-any.whl → 0.6.2__py3-none-any.whl
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/cli/calcs.py +7 -7
- mm_sol/cli/cmd/balance_cmd.py +5 -6
- mm_sol/cli/cmd/balances_cmd.py +8 -7
- mm_sol/cli/cmd/node_cmd.py +1 -1
- mm_sol/cli/cmd/transfer_cmd.py +15 -15
- mm_sol/retry.py +108 -0
- mm_sol/rpc.py +11 -26
- mm_sol/rpc_sync.py +9 -26
- mm_sol/spl_token.py +8 -45
- mm_sol/transfer.py +9 -70
- {mm_sol-0.6.0.dist-info → mm_sol-0.6.2.dist-info}/METADATA +2 -3
- {mm_sol-0.6.0.dist-info → mm_sol-0.6.2.dist-info}/RECORD +14 -13
- {mm_sol-0.6.0.dist-info → mm_sol-0.6.2.dist-info}/WHEEL +0 -0
- {mm_sol-0.6.0.dist-info → mm_sol-0.6.2.dist-info}/entry_points.txt +0 -0
mm_sol/cli/calcs.py
CHANGED
|
@@ -2,7 +2,7 @@ import mm_crypto_utils
|
|
|
2
2
|
from mm_crypto_utils import Nodes, Proxies, VarInt
|
|
3
3
|
from mm_std import Result
|
|
4
4
|
|
|
5
|
-
from mm_sol import
|
|
5
|
+
from mm_sol import retry
|
|
6
6
|
from mm_sol.constants import SUFFIX_DECIMALS
|
|
7
7
|
|
|
8
8
|
|
|
@@ -20,15 +20,15 @@ async def calc_sol_value_for_address(
|
|
|
20
20
|
value_expression = value_expression.lower()
|
|
21
21
|
var = None
|
|
22
22
|
if "balance" in value_expression:
|
|
23
|
-
res = await
|
|
24
|
-
if res.
|
|
23
|
+
res = await retry.get_sol_balance(5, nodes, proxies, address=address)
|
|
24
|
+
if res.is_err():
|
|
25
25
|
return res
|
|
26
26
|
var = VarInt("balance", res.unwrap())
|
|
27
27
|
|
|
28
28
|
value = calc_sol_expression(value_expression, var)
|
|
29
29
|
if "balance" in value_expression:
|
|
30
30
|
value = value - fee
|
|
31
|
-
return Result.
|
|
31
|
+
return Result.ok(value)
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
async def calc_token_value_for_address(
|
|
@@ -37,9 +37,9 @@ async def calc_token_value_for_address(
|
|
|
37
37
|
var = None
|
|
38
38
|
value_expression = value_expression.lower()
|
|
39
39
|
if "balance" in value_expression:
|
|
40
|
-
res = await
|
|
41
|
-
if res.
|
|
40
|
+
res = await retry.get_token_balance(5, nodes, proxies, owner=owner, token=token)
|
|
41
|
+
if res.is_err():
|
|
42
42
|
return res
|
|
43
43
|
var = VarInt("balance", res.unwrap())
|
|
44
44
|
value = calc_token_expression(value_expression, token_decimals, var)
|
|
45
|
-
return Result.
|
|
45
|
+
return Result.ok(value)
|
mm_sol/cli/cmd/balance_cmd.py
CHANGED
|
@@ -4,7 +4,8 @@ import mm_crypto_utils
|
|
|
4
4
|
from mm_std import print_json
|
|
5
5
|
from pydantic import BaseModel, Field
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
import mm_sol.retry
|
|
8
|
+
from mm_sol import retry
|
|
8
9
|
from mm_sol.cli import cli_utils
|
|
9
10
|
|
|
10
11
|
|
|
@@ -45,7 +46,7 @@ async def run(
|
|
|
45
46
|
proxies = await mm_crypto_utils.fetch_proxies_or_fatal(proxies_url) if proxies_url else None
|
|
46
47
|
|
|
47
48
|
# sol balance
|
|
48
|
-
sol_balance_res = await
|
|
49
|
+
sol_balance_res = await retry.get_sol_balance(3, rpc_url, proxies, address=wallet_address)
|
|
49
50
|
if sol_balance_res.is_ok():
|
|
50
51
|
result.sol_balance = sol_balance_res.unwrap()
|
|
51
52
|
else:
|
|
@@ -53,16 +54,14 @@ async def run(
|
|
|
53
54
|
|
|
54
55
|
# token balance
|
|
55
56
|
if token_address:
|
|
56
|
-
token_balance_res = await
|
|
57
|
-
3, rpc_url, proxies, owner=wallet_address, token=token_address
|
|
58
|
-
)
|
|
57
|
+
token_balance_res = await mm_sol.retry.get_token_balance(3, rpc_url, proxies, owner=wallet_address, token=token_address)
|
|
59
58
|
|
|
60
59
|
if token_balance_res.is_ok():
|
|
61
60
|
result.token_balance = token_balance_res.unwrap()
|
|
62
61
|
else:
|
|
63
62
|
result.errors.append("token_balance: " + token_balance_res.unwrap_error())
|
|
64
63
|
|
|
65
|
-
decimals_res = await
|
|
64
|
+
decimals_res = await mm_sol.retry.get_token_decimals(3, rpc_url, proxies, token=token_address)
|
|
66
65
|
if decimals_res.is_ok():
|
|
67
66
|
result.token_decimals = decimals_res.unwrap()
|
|
68
67
|
else:
|
mm_sol/cli/cmd/balances_cmd.py
CHANGED
|
@@ -7,7 +7,8 @@ from mm_crypto_utils import ConfigValidators
|
|
|
7
7
|
from mm_std import BaseConfig, fatal, print_json
|
|
8
8
|
from pydantic import BeforeValidator, Field
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
import mm_sol.retry
|
|
11
|
+
from mm_sol import converters, retry
|
|
11
12
|
from mm_sol.cli.validators import Validators
|
|
12
13
|
|
|
13
14
|
|
|
@@ -32,8 +33,8 @@ async def run(config_path: Path, print_config: bool) -> None:
|
|
|
32
33
|
|
|
33
34
|
if config.tokens:
|
|
34
35
|
for token_address in config.tokens:
|
|
35
|
-
res = await
|
|
36
|
-
if res.
|
|
36
|
+
res = await mm_sol.retry.get_token_decimals(3, config.nodes, config.proxies, token=token_address)
|
|
37
|
+
if res.is_err():
|
|
37
38
|
fatal(f"Failed to get decimals for token {token_address}: {res.unwrap_error()}")
|
|
38
39
|
|
|
39
40
|
token_decimals = res.unwrap()
|
|
@@ -50,9 +51,9 @@ async def _get_token_balances(
|
|
|
50
51
|
result: dict[str, Decimal | None] = {}
|
|
51
52
|
for account in accounts:
|
|
52
53
|
result[account] = (
|
|
53
|
-
(await
|
|
54
|
+
(await mm_sol.retry.get_token_balance(3, config.nodes, config.proxies, owner=account, token=token_address))
|
|
54
55
|
.map(lambda v: converters.to_token(v, token_decimals))
|
|
55
|
-
.
|
|
56
|
+
.value
|
|
56
57
|
)
|
|
57
58
|
|
|
58
59
|
return result
|
|
@@ -62,9 +63,9 @@ async def _get_sol_balances(accounts: list[str], config: Config) -> dict[str, De
|
|
|
62
63
|
result = {}
|
|
63
64
|
for account in accounts:
|
|
64
65
|
result[account] = (
|
|
65
|
-
(await
|
|
66
|
+
(await retry.get_sol_balance(3, config.nodes, config.proxies, address=account))
|
|
66
67
|
.map(lambda v: converters.lamports_to_sol(v))
|
|
67
|
-
.
|
|
68
|
+
.value
|
|
68
69
|
)
|
|
69
70
|
|
|
70
71
|
return result
|
mm_sol/cli/cmd/node_cmd.py
CHANGED
|
@@ -7,5 +7,5 @@ from mm_sol.cli import cli_utils
|
|
|
7
7
|
async def run(urls: list[str], proxy: str | None) -> None:
|
|
8
8
|
result = {}
|
|
9
9
|
for url in [cli_utils.public_rpc_url(u) for u in urls]:
|
|
10
|
-
result[url] = (await rpc.get_block_height(url, proxy=proxy, timeout=10)).
|
|
10
|
+
result[url] = (await rpc.get_block_height(url, proxy=proxy, timeout=10)).value_or_error()
|
|
11
11
|
print_json(data=result)
|
mm_sol/cli/cmd/transfer_cmd.py
CHANGED
|
@@ -13,12 +13,12 @@ from rich.live import Live
|
|
|
13
13
|
from rich.table import Table
|
|
14
14
|
from solders.signature import Signature
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
import mm_sol.retry
|
|
17
|
+
from mm_sol import retry
|
|
17
18
|
from mm_sol.cli import calcs, cli_utils
|
|
18
19
|
from mm_sol.cli.cli_utils import BaseConfigParams
|
|
19
20
|
from mm_sol.cli.validators import Validators
|
|
20
21
|
from mm_sol.converters import lamports_to_sol, to_token
|
|
21
|
-
from mm_sol.transfer import transfer_sol_with_retries, transfer_token_with_retries
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class Config(BaseConfig):
|
|
@@ -63,8 +63,8 @@ class Config(BaseConfig):
|
|
|
63
63
|
Validators.valid_sol_expression()(self.value_min_limit)
|
|
64
64
|
|
|
65
65
|
if self.token:
|
|
66
|
-
res = await
|
|
67
|
-
if res.
|
|
66
|
+
res = await mm_sol.retry.get_token_decimals(3, self.nodes, self.proxies, token=self.token)
|
|
67
|
+
if res.is_err():
|
|
68
68
|
fatal(f"can't get decimals for token={self.token}, error={res.unwrap_error()}")
|
|
69
69
|
self.token_decimals = res.unwrap()
|
|
70
70
|
|
|
@@ -130,11 +130,11 @@ async def _calc_value(transfer: Transfer, config: Config, transfer_sol_fee: int)
|
|
|
130
130
|
proxies=config.proxies,
|
|
131
131
|
fee=transfer_sol_fee,
|
|
132
132
|
)
|
|
133
|
-
logger.debug(f"{transfer.log_prefix}: value={value_res.
|
|
134
|
-
if value_res.
|
|
133
|
+
logger.debug(f"{transfer.log_prefix}: value={value_res.value_or_error()}")
|
|
134
|
+
if value_res.is_err():
|
|
135
135
|
logger.info(f"{transfer.log_prefix}: calc value error, {value_res.unwrap_error()}")
|
|
136
136
|
|
|
137
|
-
return value_res.
|
|
137
|
+
return value_res.value
|
|
138
138
|
|
|
139
139
|
|
|
140
140
|
def _check_value_min_limit(transfer: Transfer, value: int, config: Config) -> bool:
|
|
@@ -158,7 +158,7 @@ def _value_with_suffix(value: int, config: Config) -> str:
|
|
|
158
158
|
async def _send_tx(transfer: Transfer, value: int, config: Config) -> Signature | None:
|
|
159
159
|
logger.debug(f"{transfer.log_prefix}: value={_value_with_suffix(value, config)}")
|
|
160
160
|
if config.token:
|
|
161
|
-
res = await
|
|
161
|
+
res = await retry.transfer_token(
|
|
162
162
|
3,
|
|
163
163
|
config.nodes,
|
|
164
164
|
config.proxies,
|
|
@@ -170,7 +170,7 @@ async def _send_tx(transfer: Transfer, value: int, config: Config) -> Signature
|
|
|
170
170
|
decimals=config.token_decimals,
|
|
171
171
|
)
|
|
172
172
|
else:
|
|
173
|
-
res = await
|
|
173
|
+
res = await retry.transfer_sol(
|
|
174
174
|
3,
|
|
175
175
|
config.nodes,
|
|
176
176
|
config.proxies,
|
|
@@ -180,10 +180,10 @@ async def _send_tx(transfer: Transfer, value: int, config: Config) -> Signature
|
|
|
180
180
|
lamports=value,
|
|
181
181
|
)
|
|
182
182
|
|
|
183
|
-
if res.
|
|
183
|
+
if res.is_err():
|
|
184
184
|
logger.info(f"{transfer.log_prefix}: tx error {res.unwrap_error()}")
|
|
185
185
|
return None
|
|
186
|
-
return res.
|
|
186
|
+
return res.value
|
|
187
187
|
|
|
188
188
|
|
|
189
189
|
async def _transfer(transfer: Transfer, config: Config, cmd_params: TransferCmdParams) -> None:
|
|
@@ -255,12 +255,12 @@ async def _print_balances(config: Config) -> None:
|
|
|
255
255
|
|
|
256
256
|
|
|
257
257
|
async def _get_sol_balance_str(address: str, config: Config) -> str:
|
|
258
|
-
res = await
|
|
259
|
-
return res.map(lambda ok: str(lamports_to_sol(ok, config.round_ndigits))).
|
|
258
|
+
res = await retry.get_sol_balance(5, config.nodes, config.proxies, address=address)
|
|
259
|
+
return res.map(lambda ok: str(lamports_to_sol(ok, config.round_ndigits))).value_or_error()
|
|
260
260
|
|
|
261
261
|
|
|
262
262
|
async def _get_token_balance_str(address: str, config: Config) -> str:
|
|
263
263
|
if not config.token:
|
|
264
264
|
raise ValueError("token is not set")
|
|
265
|
-
res = await
|
|
266
|
-
return res.map(lambda ok: str(to_token(ok, config.token_decimals, ndigits=config.round_ndigits))).
|
|
265
|
+
res = await mm_sol.retry.get_token_balance(5, config.nodes, config.proxies, owner=address, token=config.token)
|
|
266
|
+
return res.map(lambda ok: str(to_token(ok, config.token_decimals, ndigits=config.round_ndigits))).value_or_error()
|
mm_sol/retry.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from mm_crypto_utils import Nodes, Proxies, retry_with_node_and_proxy
|
|
2
|
+
from mm_std import Result
|
|
3
|
+
from solders.solders import Pubkey, Signature
|
|
4
|
+
|
|
5
|
+
from mm_sol import rpc, spl_token, transfer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
async def get_sol_balance(retries: int, nodes: Nodes, proxies: Proxies, *, address: str, timeout: float = 5) -> Result[int]:
|
|
9
|
+
return await retry_with_node_and_proxy(
|
|
10
|
+
retries,
|
|
11
|
+
nodes,
|
|
12
|
+
proxies,
|
|
13
|
+
lambda node, proxy: rpc.get_balance(node=node, address=address, timeout=timeout, proxy=proxy),
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def get_token_balance(
|
|
18
|
+
retries: int,
|
|
19
|
+
nodes: Nodes,
|
|
20
|
+
proxies: Proxies,
|
|
21
|
+
*,
|
|
22
|
+
owner: str,
|
|
23
|
+
token: str,
|
|
24
|
+
token_account: str | None = None,
|
|
25
|
+
timeout: float = 5,
|
|
26
|
+
) -> Result[int]:
|
|
27
|
+
return await retry_with_node_and_proxy(
|
|
28
|
+
retries,
|
|
29
|
+
nodes,
|
|
30
|
+
proxies,
|
|
31
|
+
lambda node, proxy: spl_token.get_balance(
|
|
32
|
+
node,
|
|
33
|
+
owner=owner,
|
|
34
|
+
token=token,
|
|
35
|
+
token_account=token_account,
|
|
36
|
+
timeout=timeout,
|
|
37
|
+
proxy=proxy,
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async def transfer_token(
|
|
43
|
+
retries: int,
|
|
44
|
+
nodes: Nodes,
|
|
45
|
+
proxies: Proxies,
|
|
46
|
+
*,
|
|
47
|
+
token_mint_address: str | Pubkey,
|
|
48
|
+
from_address: str | Pubkey,
|
|
49
|
+
private_key: str,
|
|
50
|
+
to_address: str | Pubkey,
|
|
51
|
+
amount: int, # smallest unit
|
|
52
|
+
decimals: int,
|
|
53
|
+
timeout: float = 10,
|
|
54
|
+
create_token_account_if_not_exists: bool = True,
|
|
55
|
+
) -> Result[Signature]:
|
|
56
|
+
return await retry_with_node_and_proxy(
|
|
57
|
+
retries,
|
|
58
|
+
nodes,
|
|
59
|
+
proxies,
|
|
60
|
+
lambda node, proxy: transfer.transfer_token(
|
|
61
|
+
node=node,
|
|
62
|
+
token_mint_address=token_mint_address,
|
|
63
|
+
from_address=from_address,
|
|
64
|
+
private_key=private_key,
|
|
65
|
+
to_address=to_address,
|
|
66
|
+
amount=amount,
|
|
67
|
+
decimals=decimals,
|
|
68
|
+
proxy=proxy,
|
|
69
|
+
timeout=timeout,
|
|
70
|
+
create_token_account_if_not_exists=create_token_account_if_not_exists,
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
async def transfer_sol(
|
|
76
|
+
retries: int,
|
|
77
|
+
nodes: Nodes,
|
|
78
|
+
proxies: Proxies,
|
|
79
|
+
*,
|
|
80
|
+
from_address: str,
|
|
81
|
+
private_key: str,
|
|
82
|
+
to_address: str,
|
|
83
|
+
lamports: int,
|
|
84
|
+
timeout: float = 10,
|
|
85
|
+
) -> Result[Signature]:
|
|
86
|
+
return await retry_with_node_and_proxy(
|
|
87
|
+
retries,
|
|
88
|
+
nodes,
|
|
89
|
+
proxies,
|
|
90
|
+
lambda node, proxy: transfer.transfer_sol(
|
|
91
|
+
node=node,
|
|
92
|
+
proxy=proxy,
|
|
93
|
+
from_address=from_address,
|
|
94
|
+
to_address=to_address,
|
|
95
|
+
lamports=lamports,
|
|
96
|
+
private_key=private_key,
|
|
97
|
+
timeout=timeout,
|
|
98
|
+
),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
async def get_token_decimals(retries: int, nodes: Nodes, proxies: Proxies, *, token: str, timeout: float = 5) -> Result[int]:
|
|
103
|
+
return await retry_with_node_and_proxy(
|
|
104
|
+
retries,
|
|
105
|
+
nodes,
|
|
106
|
+
proxies,
|
|
107
|
+
lambda node, proxy: spl_token.get_decimals(node, token=token, proxy=proxy, timeout=timeout),
|
|
108
|
+
)
|
mm_sol/rpc.py
CHANGED
|
@@ -3,7 +3,6 @@ from collections.abc import Sequence
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
import websockets
|
|
6
|
-
from mm_crypto_utils import Nodes, Proxies, retry_with_node_and_proxy
|
|
7
6
|
from mm_std import Result, http_request
|
|
8
7
|
|
|
9
8
|
|
|
@@ -23,18 +22,18 @@ async def rpc_call(
|
|
|
23
22
|
|
|
24
23
|
async def _http_call(node: str, data: dict[str, object], timeout: float, proxy: str | None) -> Result[Any]:
|
|
25
24
|
res = await http_request(node, method="POST", proxy=proxy, timeout=timeout, json=data)
|
|
26
|
-
if res.
|
|
27
|
-
return res.
|
|
25
|
+
if res.is_err():
|
|
26
|
+
return res.to_err()
|
|
28
27
|
try:
|
|
29
28
|
parsed_body = res.parse_json_body()
|
|
30
29
|
err = parsed_body.get("error", {}).get("message", "")
|
|
31
30
|
if err:
|
|
32
|
-
return res.
|
|
31
|
+
return res.to_err(f"service_error: {err}")
|
|
33
32
|
if "result" in parsed_body:
|
|
34
|
-
return res.
|
|
35
|
-
return res.
|
|
33
|
+
return res.to_ok(parsed_body["result"])
|
|
34
|
+
return res.to_err("unknown_response")
|
|
36
35
|
except Exception as e:
|
|
37
|
-
return res.
|
|
36
|
+
return res.to_err(e)
|
|
38
37
|
|
|
39
38
|
|
|
40
39
|
async def _ws_call(node: str, data: dict[str, object], timeout: float) -> Result[Any]:
|
|
@@ -46,14 +45,14 @@ async def _ws_call(node: str, data: dict[str, object], timeout: float) -> Result
|
|
|
46
45
|
|
|
47
46
|
err = response.get("error", {}).get("message", "")
|
|
48
47
|
if err:
|
|
49
|
-
return Result.
|
|
48
|
+
return Result.err(f"service_error: {err}", {"response": response})
|
|
50
49
|
if "result" in response:
|
|
51
|
-
return Result.
|
|
52
|
-
return Result.
|
|
50
|
+
return Result.ok(response["result"], {"response": response})
|
|
51
|
+
return Result.err("unknown_response", {"response": response})
|
|
53
52
|
except TimeoutError:
|
|
54
|
-
return Result.
|
|
53
|
+
return Result.err("timeout", {"response": response})
|
|
55
54
|
except Exception as e:
|
|
56
|
-
return Result.
|
|
55
|
+
return Result.err(e, {"response": response})
|
|
57
56
|
|
|
58
57
|
|
|
59
58
|
async def get_block_height(node: str, timeout: float = 10, proxy: str | None = None) -> Result[int]:
|
|
@@ -65,17 +64,3 @@ async def get_balance(node: str, address: str, timeout: float = 5, proxy: str |
|
|
|
65
64
|
return (await rpc_call(node=node, method="getBalance", params=[address], timeout=timeout, proxy=proxy)).map(
|
|
66
65
|
lambda r: r["value"]
|
|
67
66
|
)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
async def get_balance_with_retries(
|
|
71
|
-
retries: int, nodes: Nodes, proxies: Proxies, *, address: str, timeout: float = 5
|
|
72
|
-
) -> Result[int]:
|
|
73
|
-
"""
|
|
74
|
-
Retry get_balance with different nodes and proxies
|
|
75
|
-
"""
|
|
76
|
-
return await retry_with_node_and_proxy(
|
|
77
|
-
retries,
|
|
78
|
-
nodes,
|
|
79
|
-
proxies,
|
|
80
|
-
lambda node, proxy: get_balance(node=node, address=address, timeout=timeout, proxy=proxy),
|
|
81
|
-
)
|
mm_sol/rpc_sync.py
CHANGED
|
@@ -91,19 +91,19 @@ def rpc_call(
|
|
|
91
91
|
def _http_call(node: str, data: dict[str, object], timeout: float, proxy: str | None) -> Result[Any]:
|
|
92
92
|
res = http_request_sync(node, method="POST", proxy=proxy, timeout=timeout, json=data)
|
|
93
93
|
try:
|
|
94
|
-
if res.
|
|
95
|
-
return res.
|
|
94
|
+
if res.is_err():
|
|
95
|
+
return res.to_err()
|
|
96
96
|
|
|
97
97
|
json_body = res.parse_json_body()
|
|
98
98
|
err = pydash.get(json_body, "error.message")
|
|
99
99
|
if err:
|
|
100
|
-
return res.
|
|
100
|
+
return res.to_err(f"service_error: {err}")
|
|
101
101
|
if "result" in json_body:
|
|
102
|
-
return res.
|
|
102
|
+
return res.to_ok(json_body["result"])
|
|
103
103
|
|
|
104
|
-
return res.
|
|
104
|
+
return res.to_err("unknown_response")
|
|
105
105
|
except Exception as e:
|
|
106
|
-
return res.
|
|
106
|
+
return res.to_err(e)
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
def get_balance(node: str, address: str, timeout: float = 10, proxy: str | None = None) -> Result[int]:
|
|
@@ -137,7 +137,7 @@ def get_cluster_nodes(node: str, timeout: float = 30, proxy: str | None = None)
|
|
|
137
137
|
|
|
138
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
|
-
if res.
|
|
140
|
+
if res.is_err():
|
|
141
141
|
return res
|
|
142
142
|
try:
|
|
143
143
|
data = res.unwrap()
|
|
@@ -174,9 +174,9 @@ def get_vote_accounts(node: str, timeout: float = 30, proxy: str | None = None)
|
|
|
174
174
|
last_vote=a["lastVote"],
|
|
175
175
|
),
|
|
176
176
|
)
|
|
177
|
-
return
|
|
177
|
+
return res.with_value(result)
|
|
178
178
|
except Exception as e:
|
|
179
|
-
return
|
|
179
|
+
return res.with_error(e)
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
def get_leader_scheduler(
|
|
@@ -194,23 +194,6 @@ def get_leader_scheduler(
|
|
|
194
194
|
)
|
|
195
195
|
|
|
196
196
|
|
|
197
|
-
def get_block_production(node: str, timeout: float = 60, proxy: str | None = None) -> Result[BlockProduction]:
|
|
198
|
-
res = rpc_call(node=node, method="getBlockProduction", timeout=timeout, proxy=proxy, params=[])
|
|
199
|
-
if res.is_error():
|
|
200
|
-
return res
|
|
201
|
-
try:
|
|
202
|
-
res_ok = res.unwrap()
|
|
203
|
-
slot = res_ok["context"]["slot"]
|
|
204
|
-
first_slot = res_ok["value"]["range"]["firstSlot"]
|
|
205
|
-
last_slot = res_ok["value"]["range"]["lastSlot"]
|
|
206
|
-
leaders = []
|
|
207
|
-
for address, (leader, produced) in res.ok["value"]["byIdentity"].items(): # type: ignore[index]
|
|
208
|
-
leaders.append(BlockProduction.Leader(address=address, produced=produced, skipped=leader - produced))
|
|
209
|
-
return Result.success(BlockProduction(slot=slot, first_slot=first_slot, last_slot=last_slot, leaders=leaders), res.extra)
|
|
210
|
-
except Exception as e:
|
|
211
|
-
return Result.failure(e, res.extra)
|
|
212
|
-
|
|
213
|
-
|
|
214
197
|
def get_stake_activation(node: str, address: str, timeout: float = 60, proxy: str | None = None) -> Result[StakeActivation]:
|
|
215
198
|
return rpc_call(node=node, method="getStakeActivation", timeout=timeout, proxy=proxy, params=[address]).map(
|
|
216
199
|
lambda ok: StakeActivation(**ok),
|
mm_sol/spl_token.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from mm_crypto_utils import Nodes, Proxies, retry_with_node_and_proxy
|
|
2
1
|
from mm_std import Result
|
|
3
2
|
from solana.exceptions import SolanaRpcException
|
|
4
3
|
from solana.rpc.core import RPCException
|
|
@@ -26,41 +25,16 @@ async def get_balance(
|
|
|
26
25
|
|
|
27
26
|
# Sometimes it not raise an error, but it returns this :(
|
|
28
27
|
if isinstance(res, InvalidParamsMessage) and "could not find account" in res.message:
|
|
29
|
-
return Result.
|
|
30
|
-
return Result.
|
|
28
|
+
return Result.ok(0, {"response": response})
|
|
29
|
+
return Result.ok(int(res.value.amount), {"response": response})
|
|
31
30
|
except RPCException as e:
|
|
32
31
|
if "could not find account" in str(e):
|
|
33
|
-
return Result.
|
|
34
|
-
return Result.
|
|
32
|
+
return Result.ok(0, {"response": response, "rpc_exception": str(e)})
|
|
33
|
+
return Result.err(e, {"response": response})
|
|
35
34
|
except SolanaRpcException as e:
|
|
36
|
-
return Result.
|
|
35
|
+
return Result.err((e.error_msg, e), {"response": response})
|
|
37
36
|
except Exception as e:
|
|
38
|
-
return Result.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
async def get_balance_with_retries(
|
|
42
|
-
retries: int,
|
|
43
|
-
nodes: Nodes,
|
|
44
|
-
proxies: Proxies,
|
|
45
|
-
*,
|
|
46
|
-
owner: str,
|
|
47
|
-
token: str,
|
|
48
|
-
token_account: str | None = None,
|
|
49
|
-
timeout: float = 5,
|
|
50
|
-
) -> Result[int]:
|
|
51
|
-
return await retry_with_node_and_proxy(
|
|
52
|
-
retries,
|
|
53
|
-
nodes,
|
|
54
|
-
proxies,
|
|
55
|
-
lambda node, proxy: get_balance(
|
|
56
|
-
node,
|
|
57
|
-
owner=owner,
|
|
58
|
-
token=token,
|
|
59
|
-
token_account=token_account,
|
|
60
|
-
timeout=timeout,
|
|
61
|
-
proxy=proxy,
|
|
62
|
-
),
|
|
63
|
-
)
|
|
37
|
+
return Result.err(e, {"response": response})
|
|
64
38
|
|
|
65
39
|
|
|
66
40
|
async def get_decimals(node: str, token: str, timeout: float = 5, proxy: str | None = None) -> Result[int]:
|
|
@@ -69,17 +43,6 @@ async def get_decimals(node: str, token: str, timeout: float = 5, proxy: str | N
|
|
|
69
43
|
client = get_async_client(node, proxy=proxy, timeout=timeout)
|
|
70
44
|
res = await client.get_token_supply(Pubkey.from_string(token))
|
|
71
45
|
response = res.to_json()
|
|
72
|
-
return Result.
|
|
46
|
+
return Result.ok(res.value.decimals, {"response": response})
|
|
73
47
|
except Exception as e:
|
|
74
|
-
return Result.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
async def get_decimals_with_retries(
|
|
78
|
-
retries: int, nodes: Nodes, proxies: Proxies, *, token: str, timeout: float = 5
|
|
79
|
-
) -> Result[int]:
|
|
80
|
-
return await retry_with_node_and_proxy(
|
|
81
|
-
retries,
|
|
82
|
-
nodes,
|
|
83
|
-
proxies,
|
|
84
|
-
lambda node, proxy: get_decimals(node, token=token, proxy=proxy, timeout=timeout),
|
|
85
|
-
)
|
|
48
|
+
return Result.err(e, {"response": response})
|
mm_sol/transfer.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import pydash
|
|
2
|
-
from mm_crypto_utils import Nodes, Proxies, retry_with_node_and_proxy
|
|
3
2
|
from mm_std import Result
|
|
4
3
|
from pydantic import BaseModel
|
|
5
4
|
from solders.message import Message
|
|
@@ -15,39 +14,6 @@ from mm_sol import rpc_sync, utils
|
|
|
15
14
|
from mm_sol.account import check_private_key, get_keypair
|
|
16
15
|
|
|
17
16
|
|
|
18
|
-
async def transfer_token_with_retries(
|
|
19
|
-
retries: int,
|
|
20
|
-
nodes: Nodes,
|
|
21
|
-
proxies: Proxies,
|
|
22
|
-
*,
|
|
23
|
-
token_mint_address: str | Pubkey,
|
|
24
|
-
from_address: str | Pubkey,
|
|
25
|
-
private_key: str,
|
|
26
|
-
to_address: str | Pubkey,
|
|
27
|
-
amount: int, # smallest unit
|
|
28
|
-
decimals: int,
|
|
29
|
-
timeout: float = 10,
|
|
30
|
-
create_token_account_if_not_exists: bool = True,
|
|
31
|
-
) -> Result[Signature]:
|
|
32
|
-
return await retry_with_node_and_proxy(
|
|
33
|
-
retries,
|
|
34
|
-
nodes,
|
|
35
|
-
proxies,
|
|
36
|
-
lambda node, proxy: transfer_token(
|
|
37
|
-
node=node,
|
|
38
|
-
token_mint_address=token_mint_address,
|
|
39
|
-
from_address=from_address,
|
|
40
|
-
private_key=private_key,
|
|
41
|
-
to_address=to_address,
|
|
42
|
-
amount=amount,
|
|
43
|
-
decimals=decimals,
|
|
44
|
-
proxy=proxy,
|
|
45
|
-
timeout=timeout,
|
|
46
|
-
create_token_account_if_not_exists=create_token_account_if_not_exists,
|
|
47
|
-
),
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
|
|
51
17
|
async def transfer_token(
|
|
52
18
|
*,
|
|
53
19
|
node: str,
|
|
@@ -64,7 +30,7 @@ async def transfer_token(
|
|
|
64
30
|
# TODO: try/except this function!!!
|
|
65
31
|
acc = get_keypair(private_key)
|
|
66
32
|
if not check_private_key(from_address, private_key):
|
|
67
|
-
return Result.
|
|
33
|
+
return Result.err("invalid_private_key")
|
|
68
34
|
|
|
69
35
|
from_address = utils.pubkey(from_address)
|
|
70
36
|
token_mint_address = utils.pubkey(token_mint_address)
|
|
@@ -83,7 +49,7 @@ async def transfer_token(
|
|
|
83
49
|
create_account_res = token_client.create_associated_token_account(to_address, skip_confirmation=False)
|
|
84
50
|
logs.append(create_account_res)
|
|
85
51
|
else:
|
|
86
|
-
return Result.
|
|
52
|
+
return Result.err("no_token_account")
|
|
87
53
|
|
|
88
54
|
res = await token_client.transfer_checked(
|
|
89
55
|
source=from_token_account,
|
|
@@ -95,34 +61,7 @@ async def transfer_token(
|
|
|
95
61
|
)
|
|
96
62
|
logs.append(res)
|
|
97
63
|
|
|
98
|
-
return Result.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
async def transfer_sol_with_retries(
|
|
102
|
-
retries: int,
|
|
103
|
-
nodes: Nodes,
|
|
104
|
-
proxies: Proxies,
|
|
105
|
-
*,
|
|
106
|
-
from_address: str,
|
|
107
|
-
private_key: str,
|
|
108
|
-
to_address: str,
|
|
109
|
-
lamports: int,
|
|
110
|
-
timeout: float = 10,
|
|
111
|
-
) -> Result[Signature]:
|
|
112
|
-
return await retry_with_node_and_proxy(
|
|
113
|
-
retries,
|
|
114
|
-
nodes,
|
|
115
|
-
proxies,
|
|
116
|
-
lambda node, proxy: transfer_sol(
|
|
117
|
-
node=node,
|
|
118
|
-
proxy=proxy,
|
|
119
|
-
from_address=from_address,
|
|
120
|
-
to_address=to_address,
|
|
121
|
-
lamports=lamports,
|
|
122
|
-
private_key=private_key,
|
|
123
|
-
timeout=timeout,
|
|
124
|
-
),
|
|
125
|
-
)
|
|
64
|
+
return Result.ok(res.value, {"logs": logs})
|
|
126
65
|
|
|
127
66
|
|
|
128
67
|
async def transfer_sol(
|
|
@@ -137,7 +76,7 @@ async def transfer_sol(
|
|
|
137
76
|
) -> Result[Signature]:
|
|
138
77
|
acc = get_keypair(private_key)
|
|
139
78
|
if not check_private_key(from_address, private_key):
|
|
140
|
-
return Result.
|
|
79
|
+
return Result.err("invalid_private_key")
|
|
141
80
|
|
|
142
81
|
client = utils.get_async_client(node, proxy=proxy, timeout=timeout)
|
|
143
82
|
data = None
|
|
@@ -148,9 +87,9 @@ async def transfer_sol(
|
|
|
148
87
|
tx = Transaction([acc], msg, blockhash.value.blockhash)
|
|
149
88
|
res = await client.send_transaction(tx)
|
|
150
89
|
data = res.to_json()
|
|
151
|
-
return Result.
|
|
90
|
+
return Result.ok(res.value, {"response": data})
|
|
152
91
|
except Exception as e:
|
|
153
|
-
return Result.
|
|
92
|
+
return Result.err(e, {"response": data})
|
|
154
93
|
|
|
155
94
|
|
|
156
95
|
class SolTransferInfo(BaseModel):
|
|
@@ -161,7 +100,7 @@ class SolTransferInfo(BaseModel):
|
|
|
161
100
|
|
|
162
101
|
def find_sol_transfers(node: str, tx_signature: str) -> Result[list[SolTransferInfo]]:
|
|
163
102
|
res = rpc_sync.get_transaction(node, tx_signature, encoding="jsonParsed")
|
|
164
|
-
if res.
|
|
103
|
+
if res.is_err():
|
|
165
104
|
return res # type: ignore[return-value]
|
|
166
105
|
result = []
|
|
167
106
|
try:
|
|
@@ -174,6 +113,6 @@ def find_sol_transfers(node: str, tx_signature: str) -> Result[list[SolTransferI
|
|
|
174
113
|
lamports = pydash.get(ix, "parsed.info.lamports")
|
|
175
114
|
if source and destination and lamports:
|
|
176
115
|
result.append(SolTransferInfo(source=source, destination=destination, lamports=lamports))
|
|
177
|
-
return
|
|
116
|
+
return res.with_value(result)
|
|
178
117
|
except Exception as e:
|
|
179
|
-
return Result.
|
|
118
|
+
return Result.err(e, res.extra)
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mm-sol
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
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.3.
|
|
8
|
-
Requires-Dist: mm-std>=0.4.5
|
|
7
|
+
Requires-Dist: mm-crypto-utils>=0.3.6
|
|
9
8
|
Requires-Dist: mnemonic==0.21
|
|
10
9
|
Requires-Dist: socksio>=1.0.0
|
|
11
10
|
Requires-Dist: solana~=0.36.6
|
|
@@ -3,28 +3,29 @@ mm_sol/account.py,sha256=cVcxRQBuV_Gfm2WgQIwaYuAQijeIJqDDxLC22PN0XSs,3493
|
|
|
3
3
|
mm_sol/constants.py,sha256=WSpfz5_cq_8XbIrNFJGu9okwbfPTL00zsyR_k9-7O0o,29
|
|
4
4
|
mm_sol/converters.py,sha256=rBxe3SIADZS8hG7TYl4FgjmvKH-ykaTmNbnWWQDiFZ4,1430
|
|
5
5
|
mm_sol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
mm_sol/
|
|
7
|
-
mm_sol/
|
|
8
|
-
mm_sol/
|
|
9
|
-
mm_sol/
|
|
6
|
+
mm_sol/retry.py,sha256=9ejCcrHbJFOcZVKdbUASynLs1YHw04VAIKGH8mIJ1OI,2894
|
|
7
|
+
mm_sol/rpc.py,sha256=LhXrHIz58iOG2ES8m67ycBCwkJx-4pLBuDt6Hsq6OkM,2422
|
|
8
|
+
mm_sol/rpc_sync.py,sha256=A06MTSFQ7UUJgm0fy4a6ipqqY2vdDwS07UGXkdrvqWw,7148
|
|
9
|
+
mm_sol/spl_token.py,sha256=hEWVch17EdudlL_tj3ZInCa9wB0iK8hwwpFucmVi7Pc,1941
|
|
10
|
+
mm_sol/transfer.py,sha256=6CXzeMe91JtG8SuTadYQ-DLHJFyskytmBItGVEPh7Os,4410
|
|
10
11
|
mm_sol/utils.py,sha256=oD06NsMSMhN6lqsM6mSgLTtiKwA1uAsen9WR82ofRTE,923
|
|
11
12
|
mm_sol/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
mm_sol/cli/calcs.py,sha256=
|
|
13
|
+
mm_sol/cli/calcs.py,sha256=TR-mWQWYyHPdJYi_rvqiebqBqRsXsaqaSdvwCoSax1g,1675
|
|
13
14
|
mm_sol/cli/cli.py,sha256=0MflYgO-UzFdoaXFCIBuQ1Y6AOQUquX__AhSWOO64KY,4735
|
|
14
15
|
mm_sol/cli/cli_utils.py,sha256=nFdY8tJFZxyssEBEFCc3VTNJt447e6vMnugx4GBPL4o,1840
|
|
15
16
|
mm_sol/cli/validators.py,sha256=M_Rr7JoG3TUYTDAGkjQLDH6l9i9FOrSpss30KdY3UlM,1379
|
|
16
17
|
mm_sol/cli/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
mm_sol/cli/cmd/balance_cmd.py,sha256=
|
|
18
|
-
mm_sol/cli/cmd/balances_cmd.py,sha256=
|
|
18
|
+
mm_sol/cli/cmd/balance_cmd.py,sha256=ot6X7gTSoMcjapVkAgDlA9Wyo4YeFBIRA7GLjTXsu98,2469
|
|
19
|
+
mm_sol/cli/cmd/balances_cmd.py,sha256=XNNyU02mvSxPdfD3usxucNiiHagxSnLe1rKihj-ya78,2676
|
|
19
20
|
mm_sol/cli/cmd/example_cmd.py,sha256=ZLTy1-cmapiCyYvjFInVE-pQCGKZzDgYKUhsOwtbSIY,234
|
|
20
|
-
mm_sol/cli/cmd/node_cmd.py,sha256=
|
|
21
|
-
mm_sol/cli/cmd/transfer_cmd.py,sha256=
|
|
21
|
+
mm_sol/cli/cmd/node_cmd.py,sha256=o3YktYk4o_ya8srN_It2BITam4HlpFMK11ajYNTGuKA,350
|
|
22
|
+
mm_sol/cli/cmd/transfer_cmd.py,sha256=8QMfqUcvS63V_AeEvlvsuIEZIv_ydrMtuCOr4LvcyoY,10922
|
|
22
23
|
mm_sol/cli/cmd/wallet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
24
|
mm_sol/cli/cmd/wallet/keypair_cmd.py,sha256=cRHVVTs9zNYmUozZ8ZlJoutn9V6r8I1AEHBrszR7dTE,538
|
|
24
25
|
mm_sol/cli/cmd/wallet/mnemonic_cmd.py,sha256=IiON_fJT5AFfIr_E1LR6_iDYZ3c_jWCFc-wSYqk61V8,648
|
|
25
26
|
mm_sol/cli/examples/balances.toml,sha256=333g2EkyYBDW7OWFGMIWVZGkdFQMMo0Ag-bg-BvS4Zg,349
|
|
26
27
|
mm_sol/cli/examples/transfer.toml,sha256=kOCdmuwmhlOam4LVtlcYTKF0PoZYHWMlv9gWxNSXMOk,1624
|
|
27
|
-
mm_sol-0.6.
|
|
28
|
-
mm_sol-0.6.
|
|
29
|
-
mm_sol-0.6.
|
|
30
|
-
mm_sol-0.6.
|
|
28
|
+
mm_sol-0.6.2.dist-info/METADATA,sha256=H01qTQ1WZUC-TNzgDPimXXZpIFZVVcf6CvuvmeE6UsQ,320
|
|
29
|
+
mm_sol-0.6.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
30
|
+
mm_sol-0.6.2.dist-info/entry_points.txt,sha256=MrYnosumy9nsITSAw5TiR3WXDwsdoF0YvUIlZ38TLLs,46
|
|
31
|
+
mm_sol-0.6.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|