mm-balance 0.1.20__py3-none-any.whl → 0.2.1__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_balance/cli.py +15 -12
- mm_balance/config/example.toml +83 -0
- mm_balance/config.py +33 -43
- mm_balance/output/formats/json_format.py +3 -3
- mm_balance/output/formats/table_format.py +22 -20
- mm_balance/price.py +1 -1
- mm_balance/result.py +1 -1
- mm_balance/rpc/solana.py +6 -5
- mm_balance/token_decimals.py +1 -1
- mm_balance/workers.py +3 -3
- mm_balance-0.2.1.dist-info/METADATA +9 -0
- mm_balance-0.2.1.dist-info/RECORD +24 -0
- mm_balance/config/example.yml +0 -74
- mm_balance-0.1.20.dist-info/METADATA +0 -10
- mm_balance-0.1.20.dist-info/RECORD +0 -24
- {mm_balance-0.1.20.dist-info → mm_balance-0.2.1.dist-info}/WHEEL +0 -0
- {mm_balance-0.1.20.dist-info → mm_balance-0.2.1.dist-info}/entry_points.txt +0 -0
mm_balance/cli.py
CHANGED
|
@@ -19,7 +19,7 @@ app = typer.Typer(no_args_is_help=True, pretty_exceptions_enable=False, add_comp
|
|
|
19
19
|
|
|
20
20
|
def example_callback(value: bool) -> None:
|
|
21
21
|
if value:
|
|
22
|
-
data = pkgutil.get_data(__name__, "config/example.
|
|
22
|
+
data = pkgutil.get_data(__name__, "config/example.toml")
|
|
23
23
|
typer.echo(data)
|
|
24
24
|
raise typer.Exit
|
|
25
25
|
|
|
@@ -37,6 +37,7 @@ def cli(
|
|
|
37
37
|
print_format: Annotated[PrintFormat | None, typer.Option("--format", "-f", help="Print format.")] = None,
|
|
38
38
|
skip_empty: Annotated[bool | None, typer.Option("--skip-empty", "-s", help="Skip empty balances.")] = None,
|
|
39
39
|
debug: Annotated[bool | None, typer.Option("--debug", "-d", help="Print debug info.")] = None,
|
|
40
|
+
print_config: Annotated[bool | None, typer.Option("--config", "-c", help="Print config and exit.")] = None,
|
|
40
41
|
price: Annotated[bool | None, typer.Option("--price/--no-price", help="Print prices.")] = None,
|
|
41
42
|
_example: Annotated[bool | None, typer.Option("--example", callback=example_callback, help="Print a config example.")] = None,
|
|
42
43
|
_networks: Annotated[
|
|
@@ -46,36 +47,38 @@ def cli(
|
|
|
46
47
|
zip_password = "" # nosec
|
|
47
48
|
if config_path.name.endswith(".zip"):
|
|
48
49
|
zip_password = getpass.getpass("zip password")
|
|
49
|
-
config = Config.
|
|
50
|
+
config = Config.read_toml_config_or_exit(config_path, zip_password=zip_password)
|
|
51
|
+
if print_config:
|
|
52
|
+
config.print_and_exit()
|
|
50
53
|
|
|
51
54
|
if print_format is not None:
|
|
52
|
-
config.print_format = print_format
|
|
55
|
+
config.settings.print_format = print_format
|
|
53
56
|
if debug is not None:
|
|
54
|
-
config.print_debug = debug
|
|
57
|
+
config.settings.print_debug = debug
|
|
55
58
|
if skip_empty is not None:
|
|
56
|
-
config.skip_empty = skip_empty
|
|
59
|
+
config.settings.skip_empty = skip_empty
|
|
57
60
|
if price is not None:
|
|
58
|
-
config.price = price
|
|
61
|
+
config.settings.price = price
|
|
59
62
|
|
|
60
|
-
if config.print_debug and config.print_format is PrintFormat.TABLE:
|
|
63
|
+
if config.settings.print_debug and config.settings.print_format is PrintFormat.TABLE:
|
|
61
64
|
table_format.print_nodes(config)
|
|
62
65
|
table_format.print_proxy_count(config)
|
|
63
66
|
|
|
64
67
|
token_decimals = get_token_decimals(config)
|
|
65
|
-
if config.print_debug and config.print_format is PrintFormat.TABLE:
|
|
68
|
+
if config.settings.print_debug and config.settings.print_format is PrintFormat.TABLE:
|
|
66
69
|
table_format.print_token_decimals(token_decimals)
|
|
67
70
|
|
|
68
|
-
prices = get_prices(config) if config.price else Prices()
|
|
69
|
-
if config.print_format is PrintFormat.TABLE:
|
|
71
|
+
prices = get_prices(config) if config.settings.price else Prices()
|
|
72
|
+
if config.settings.print_format is PrintFormat.TABLE:
|
|
70
73
|
table_format.print_prices(config, prices)
|
|
71
74
|
|
|
72
75
|
workers = Workers(config, token_decimals)
|
|
73
76
|
workers.process()
|
|
74
77
|
|
|
75
78
|
result = create_balances_result(config, prices, workers)
|
|
76
|
-
if config.print_format is PrintFormat.TABLE:
|
|
79
|
+
if config.settings.print_format is PrintFormat.TABLE:
|
|
77
80
|
table_format.print_result(config, result, workers)
|
|
78
|
-
elif config.print_format is PrintFormat.JSON:
|
|
81
|
+
elif config.settings.print_format is PrintFormat.JSON:
|
|
79
82
|
json_format.print_result(config, token_decimals, prices, workers, result)
|
|
80
83
|
else:
|
|
81
84
|
fatal("Unsupported print format")
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
[[coins]]
|
|
2
|
+
ticker = "SOL"
|
|
3
|
+
network = "solana"
|
|
4
|
+
addresses = "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S" # binance
|
|
5
|
+
|
|
6
|
+
[[coins]]
|
|
7
|
+
ticker = "USDT"
|
|
8
|
+
network = "solana"
|
|
9
|
+
addresses = "2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S" # binance
|
|
10
|
+
|
|
11
|
+
[[coins]]
|
|
12
|
+
ticker = "BTC"
|
|
13
|
+
network = "bitcoin"
|
|
14
|
+
comment = "coldwallets"
|
|
15
|
+
addresses = """
|
|
16
|
+
34xp4vRoCGJym3xR7yCVPFHoCNxv4Twseo # binance
|
|
17
|
+
bc1qgdjqv0av3q56jvd82tkdjpy7gdp9ut8tlqmgrpmv24sq90ecnvqqjwvw97 # bitfinex
|
|
18
|
+
bc1ql49ydapnjafl5t2cp9zqpjwe6pdgmxy98859v2 # robinhood
|
|
19
|
+
"""
|
|
20
|
+
share = 0.1
|
|
21
|
+
|
|
22
|
+
[[coins]]
|
|
23
|
+
ticker = "ETH"
|
|
24
|
+
network = "ethereum"
|
|
25
|
+
comment = "okx"
|
|
26
|
+
addresses = "okx_eth"
|
|
27
|
+
|
|
28
|
+
[[coins]]
|
|
29
|
+
ticker = "USDT"
|
|
30
|
+
network = "ethereum"
|
|
31
|
+
comment = "okx"
|
|
32
|
+
addresses = "okx_eth"
|
|
33
|
+
|
|
34
|
+
[[coins]]
|
|
35
|
+
ticker = "ETH"
|
|
36
|
+
network = "ethereum"
|
|
37
|
+
comment = "binance"
|
|
38
|
+
addresses = "binance_eth"
|
|
39
|
+
|
|
40
|
+
[[coins]]
|
|
41
|
+
ticker = "USDT"
|
|
42
|
+
network = "ethereum"
|
|
43
|
+
comment = "binance"
|
|
44
|
+
addresses = "binance_eth"
|
|
45
|
+
|
|
46
|
+
[[coins]]
|
|
47
|
+
ticker = "USDC"
|
|
48
|
+
network = "aptos"
|
|
49
|
+
comment = "swap.thala.apt"
|
|
50
|
+
token_address = "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC"
|
|
51
|
+
token_decimals = 6
|
|
52
|
+
addresses = "0x48271d39d0b05bd6efca2278f22277d6fcc375504f9839fd73f74ace240861af"
|
|
53
|
+
|
|
54
|
+
[[addresses]]
|
|
55
|
+
name = "okx_eth"
|
|
56
|
+
addresses = """
|
|
57
|
+
0xf59869753f41db720127ceb8dbb8afaf89030de4
|
|
58
|
+
0x65a0947ba5175359bb457d3b34491edf4cbf7997
|
|
59
|
+
0xe9172daf64b05b26eb18f07ac8d6d723acb48f99
|
|
60
|
+
0x4d19c0a5357bc48be0017095d3c871d9afc3f21d
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
[[addresses]]
|
|
64
|
+
name = "binance_eth"
|
|
65
|
+
addresses = """
|
|
66
|
+
0xf977814e90da44bfa03b6295a0616a897441acec
|
|
67
|
+
0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
[settings] # all records are optional
|
|
72
|
+
round_ndigits = 4
|
|
73
|
+
price = true
|
|
74
|
+
skip_empty = false
|
|
75
|
+
print_debug = false
|
|
76
|
+
print_format = "table" # table, json
|
|
77
|
+
format_number_separator = ","
|
|
78
|
+
proxies = """
|
|
79
|
+
# env_url: MM_BALANCE_PROXIES_URL
|
|
80
|
+
# socks5://usr:pass@site.com:1234
|
|
81
|
+
# http://site.com:1234
|
|
82
|
+
"""
|
|
83
|
+
|
mm_balance/config.py
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
from decimal import Decimal
|
|
5
4
|
from pathlib import Path
|
|
6
|
-
from typing import Annotated, Self
|
|
5
|
+
from typing import Annotated, Self
|
|
7
6
|
|
|
8
|
-
import mm_crypto_utils
|
|
9
7
|
import pydash
|
|
10
8
|
from mm_crypto_utils import ConfigValidators
|
|
11
9
|
from mm_std import BaseConfig, PrintFormat, fatal
|
|
@@ -14,6 +12,10 @@ from pydantic import BeforeValidator, Field, StringConstraints, model_validator
|
|
|
14
12
|
from mm_balance.constants import DEFAULT_NODES, TOKEN_ADDRESS, Network
|
|
15
13
|
|
|
16
14
|
|
|
15
|
+
class Validators(ConfigValidators):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
17
19
|
class Group(BaseConfig):
|
|
18
20
|
comment: str = ""
|
|
19
21
|
ticker: Annotated[str, StringConstraints(to_upper=True)]
|
|
@@ -21,7 +23,7 @@ class Group(BaseConfig):
|
|
|
21
23
|
token_address: str | None = None # If None, it's a native token, for example ETH
|
|
22
24
|
token_decimals: int | None = None
|
|
23
25
|
coingecko_id: str | None = None
|
|
24
|
-
addresses: Annotated[list[str], BeforeValidator(
|
|
26
|
+
addresses: Annotated[list[str], BeforeValidator(Validators.addresses(unique=True))]
|
|
25
27
|
share: Decimal = Decimal(1)
|
|
26
28
|
|
|
27
29
|
@property
|
|
@@ -41,14 +43,24 @@ class Group(BaseConfig):
|
|
|
41
43
|
return self
|
|
42
44
|
|
|
43
45
|
def process_addresses(self, address_groups: list[AddressGroup]) -> None:
|
|
44
|
-
|
|
45
|
-
for
|
|
46
|
-
if
|
|
47
|
-
|
|
46
|
+
result = []
|
|
47
|
+
for line in self.addresses:
|
|
48
|
+
if line.startswith("file:"):
|
|
49
|
+
path = Path(line.removeprefix("file:").strip()).expanduser()
|
|
50
|
+
if path.is_file():
|
|
51
|
+
result += path.read_text().strip().splitlines()
|
|
52
|
+
else:
|
|
53
|
+
fatal(f"File with addresses not found: {path}")
|
|
54
|
+
elif line.startswith("group:"):
|
|
55
|
+
group_name = line.removeprefix("group:").strip()
|
|
56
|
+
address_group = next((ag for ag in address_groups if ag.name == group_name), None)
|
|
57
|
+
if address_group is None:
|
|
58
|
+
raise ValueError(f"Address group not found: {group_name}")
|
|
59
|
+
result += address_group.addresses
|
|
48
60
|
else:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
self.addresses = pydash.uniq(
|
|
61
|
+
result.append(line)
|
|
62
|
+
# TODO: check address is valid. There is network info in the group
|
|
63
|
+
self.addresses = pydash.uniq(result)
|
|
52
64
|
|
|
53
65
|
|
|
54
66
|
class AddressGroup(BaseConfig):
|
|
@@ -56,20 +68,22 @@ class AddressGroup(BaseConfig):
|
|
|
56
68
|
addresses: Annotated[list[str], BeforeValidator(ConfigValidators.addresses(unique=True))]
|
|
57
69
|
|
|
58
70
|
|
|
59
|
-
class
|
|
60
|
-
|
|
61
|
-
addresses: list[AddressGroup] = Field(default_factory=list)
|
|
62
|
-
|
|
63
|
-
proxies_url: str | None = None
|
|
64
|
-
proxies: list[str] = Field(default_factory=list)
|
|
71
|
+
class Settings(BaseConfig):
|
|
72
|
+
proxies: Annotated[list[str], Field(default_factory=list), BeforeValidator(Validators.proxies())]
|
|
65
73
|
round_ndigits: int = 4
|
|
66
|
-
nodes: dict[Network, list[str]] = Field(default_factory=dict)
|
|
67
74
|
print_format: PrintFormat = PrintFormat.TABLE
|
|
68
75
|
price: bool = True
|
|
69
76
|
skip_empty: bool = False # don't print the address with an empty balance
|
|
70
77
|
print_debug: bool = False # print debug info: nodes, token_decimals
|
|
71
|
-
format_number_separator: str = "," #
|
|
78
|
+
format_number_separator: str = "," # as thousands separators
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Config(BaseConfig):
|
|
82
|
+
groups: list[Group] = Field(alias="coins")
|
|
83
|
+
addresses: list[AddressGroup] = Field(default_factory=list)
|
|
84
|
+
nodes: dict[Network, list[str]] = Field(default_factory=dict)
|
|
72
85
|
workers: dict[Network, int] = Field(default_factory=dict)
|
|
86
|
+
settings: Settings = Field(default_factory=Settings) # type: ignore[arg-type]
|
|
73
87
|
|
|
74
88
|
def has_share(self) -> bool:
|
|
75
89
|
return any(g.share != Decimal(1) for g in self.groups)
|
|
@@ -79,12 +93,6 @@ class Config(BaseConfig):
|
|
|
79
93
|
|
|
80
94
|
@model_validator(mode="after")
|
|
81
95
|
def final_validator(self) -> Self:
|
|
82
|
-
# load from proxies_url
|
|
83
|
-
if self.proxies_url is not None:
|
|
84
|
-
self.proxies += mm_crypto_utils.fetch_proxies_or_fatal(self.proxies_url)
|
|
85
|
-
elif os.getenv("MM_BALANCE_PROXIES_URL"):
|
|
86
|
-
self.proxies += mm_crypto_utils.fetch_proxies_or_fatal(cast(str, os.getenv("MM_BALANCE_PROXIES_URL")))
|
|
87
|
-
|
|
88
96
|
# load addresses from address_group
|
|
89
97
|
for group in self.groups:
|
|
90
98
|
group.process_addresses(self.addresses)
|
|
@@ -105,21 +113,3 @@ class Config(BaseConfig):
|
|
|
105
113
|
def detect_token_address(ticker: str, network: Network) -> str | None:
|
|
106
114
|
if network in TOKEN_ADDRESS:
|
|
107
115
|
return TOKEN_ADDRESS[network].get(ticker)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def get_address_group_by_name(address_groups: list[AddressGroup], name: str) -> AddressGroup | None:
|
|
111
|
-
return pydash.find(address_groups, lambda g: g.name == name)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def process_file_addresses(addresses: list[str]) -> list[str]:
|
|
115
|
-
result = []
|
|
116
|
-
for address in addresses:
|
|
117
|
-
if address.startswith("file://"):
|
|
118
|
-
path = Path(address.removeprefix("file://"))
|
|
119
|
-
if path.is_file():
|
|
120
|
-
result.extend(path.read_text().strip().splitlines())
|
|
121
|
-
else:
|
|
122
|
-
fatal(f"File with addresses not found: {path}")
|
|
123
|
-
else:
|
|
124
|
-
result.append(address)
|
|
125
|
-
return result
|
|
@@ -9,11 +9,11 @@ from mm_balance.workers import Workers
|
|
|
9
9
|
|
|
10
10
|
def print_result(config: Config, token_decimals: TokenDecimals, prices: Prices, workers: Workers, result: BalancesResult) -> None:
|
|
11
11
|
data: dict[str, object] = {}
|
|
12
|
-
if config.print_debug:
|
|
12
|
+
if config.settings.print_debug:
|
|
13
13
|
data["nodes"] = config.nodes
|
|
14
14
|
data["token_decimals"] = token_decimals
|
|
15
|
-
data["proxies"] = len(config.proxies)
|
|
16
|
-
if config.price:
|
|
15
|
+
data["proxies"] = len(config.settings.proxies)
|
|
16
|
+
if config.settings.price:
|
|
17
17
|
data["prices"] = prices
|
|
18
18
|
|
|
19
19
|
data["groups"] = result.groups
|
|
@@ -18,7 +18,7 @@ def print_nodes(config: Config) -> None:
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def print_proxy_count(config: Config) -> None:
|
|
21
|
-
print_table("Proxies", ["count"], [[len(config.proxies)]])
|
|
21
|
+
print_table("Proxies", ["count"], [[len(config.settings.proxies)]])
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def print_token_decimals(token_decimals: TokenDecimals) -> None:
|
|
@@ -29,10 +29,12 @@ def print_token_decimals(token_decimals: TokenDecimals) -> None:
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def print_prices(config: Config, prices: Prices) -> None:
|
|
32
|
-
if config.price:
|
|
32
|
+
if config.settings.price:
|
|
33
33
|
rows = []
|
|
34
34
|
for ticker, price in prices.items():
|
|
35
|
-
rows.append(
|
|
35
|
+
rows.append(
|
|
36
|
+
[ticker, format_number(round(price, config.settings.round_ndigits), config.settings.format_number_separator, "$")]
|
|
37
|
+
)
|
|
36
38
|
print_table("Prices", ["coin", "usd"], rows)
|
|
37
39
|
|
|
38
40
|
|
|
@@ -64,19 +66,19 @@ def _print_total(config: Config, total: Total, is_share_total: bool) -> None:
|
|
|
64
66
|
|
|
65
67
|
rows = []
|
|
66
68
|
for ticker, balance in total.coin_balances.items():
|
|
67
|
-
balance_str = format_number(balance, config.format_number_separator)
|
|
69
|
+
balance_str = format_number(balance, config.settings.format_number_separator)
|
|
68
70
|
row = [ticker, balance_str]
|
|
69
|
-
if config.price:
|
|
70
|
-
usd_value_str = format_number(total.coin_usd_values[ticker], config.format_number_separator, "$")
|
|
71
|
+
if config.settings.price:
|
|
72
|
+
usd_value_str = format_number(total.coin_usd_values[ticker], config.settings.format_number_separator, "$")
|
|
71
73
|
portfolio_share = total.portfolio_share[ticker]
|
|
72
74
|
row += [usd_value_str, f"{portfolio_share}%"]
|
|
73
75
|
rows.append(row)
|
|
74
76
|
|
|
75
|
-
if config.price:
|
|
77
|
+
if config.settings.price:
|
|
76
78
|
headers += ["usd", "portfolio_share"]
|
|
77
79
|
if total.stablecoin_sum > 0:
|
|
78
|
-
rows.append(["stablecoin_sum", format_number(total.stablecoin_sum, config.format_number_separator, "$")])
|
|
79
|
-
rows.append(["total_usd_sum", format_number(total.total_usd_sum, config.format_number_separator, "$")])
|
|
80
|
+
rows.append(["stablecoin_sum", format_number(total.stablecoin_sum, config.settings.format_number_separator, "$")])
|
|
81
|
+
rows.append(["total_usd_sum", format_number(total.total_usd_sum, config.settings.format_number_separator, "$")])
|
|
80
82
|
|
|
81
83
|
print_table(table_name, headers, rows)
|
|
82
84
|
|
|
@@ -92,28 +94,28 @@ def _print_group(config: Config, group: GroupResult) -> None:
|
|
|
92
94
|
if isinstance(address.balance, str):
|
|
93
95
|
rows.append([address.address, address.balance])
|
|
94
96
|
else:
|
|
95
|
-
if config.skip_empty and address.balance.balance == Decimal(0):
|
|
97
|
+
if config.settings.skip_empty and address.balance.balance == Decimal(0):
|
|
96
98
|
continue
|
|
97
|
-
balance_str = format_number(address.balance.balance, config.format_number_separator)
|
|
99
|
+
balance_str = format_number(address.balance.balance, config.settings.format_number_separator)
|
|
98
100
|
row = [address.address, balance_str]
|
|
99
|
-
if config.price:
|
|
100
|
-
usd_value_str = format_number(address.balance.usd_value, config.format_number_separator, "$")
|
|
101
|
+
if config.settings.price:
|
|
102
|
+
usd_value_str = format_number(address.balance.usd_value, config.settings.format_number_separator, "$")
|
|
101
103
|
row.append(usd_value_str)
|
|
102
104
|
rows.append(row)
|
|
103
105
|
|
|
104
|
-
sum_row = ["sum", format_number(group.balance_sum, config.format_number_separator)]
|
|
105
|
-
if config.price:
|
|
106
|
-
sum_row.append(format_number(group.usd_sum, config.format_number_separator, "$"))
|
|
106
|
+
sum_row = ["sum", format_number(group.balance_sum, config.settings.format_number_separator)]
|
|
107
|
+
if config.settings.price:
|
|
108
|
+
sum_row.append(format_number(group.usd_sum, config.settings.format_number_separator, "$"))
|
|
107
109
|
rows.append(sum_row)
|
|
108
110
|
|
|
109
111
|
if group.share < Decimal(1):
|
|
110
|
-
sum_share_str = format_number(group.balance_sum_share, config.format_number_separator)
|
|
112
|
+
sum_share_str = format_number(group.balance_sum_share, config.settings.format_number_separator)
|
|
111
113
|
sum_share_row = [f"sum_share, {group.share}", sum_share_str]
|
|
112
|
-
if config.price:
|
|
113
|
-
sum_share_row.append(format_number(group.usd_sum_share, config.format_number_separator, "$"))
|
|
114
|
+
if config.settings.price:
|
|
115
|
+
sum_share_row.append(format_number(group.usd_sum_share, config.settings.format_number_separator, "$"))
|
|
114
116
|
rows.append(sum_share_row)
|
|
115
117
|
|
|
116
118
|
table_headers = ["address", "balance"]
|
|
117
|
-
if config.price:
|
|
119
|
+
if config.settings.price:
|
|
118
120
|
table_headers += ["usd"]
|
|
119
121
|
print_table(group_name, table_headers, rows)
|
mm_balance/price.py
CHANGED
|
@@ -30,7 +30,7 @@ def get_prices(config: Config) -> Prices:
|
|
|
30
30
|
|
|
31
31
|
url = f"https://api.coingecko.com/api/v3/simple/price?ids={','.join(coingecko_map.values())}&vs_currencies=usd"
|
|
32
32
|
for _ in range(RETRIES_COINGECKO_PRICES):
|
|
33
|
-
res = hr(url, proxy=random_str_choice(config.proxies))
|
|
33
|
+
res = hr(url, proxy=random_str_choice(config.settings.proxies))
|
|
34
34
|
if res.code != 200:
|
|
35
35
|
continue
|
|
36
36
|
|
mm_balance/result.py
CHANGED
|
@@ -106,7 +106,7 @@ def _create_group_result(config: Config, group: Group, tasks: list[Task], prices
|
|
|
106
106
|
coin_value = task.balance.ok
|
|
107
107
|
usd_value = Decimal(0)
|
|
108
108
|
if group.ticker in prices:
|
|
109
|
-
usd_value = round(coin_value * prices[group.ticker], config.round_ndigits)
|
|
109
|
+
usd_value = round(coin_value * prices[group.ticker], config.settings.round_ndigits)
|
|
110
110
|
balance = Balance(balance=coin_value, usd_value=usd_value)
|
|
111
111
|
balance_sum += balance.balance
|
|
112
112
|
usd_sum += balance.usd_value
|
mm_balance/rpc/solana.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from mm_solana import token as solana_token
|
|
3
|
+
from mm_sol import balance, token
|
|
5
4
|
from mm_std import Ok, Result
|
|
6
5
|
|
|
7
6
|
from mm_balance.constants import RETRIES_BALANCE, RETRIES_DECIMALS, TIMEOUT_BALANCE, TIMEOUT_DECIMALS
|
|
@@ -11,9 +10,11 @@ def get_balance(
|
|
|
11
10
|
nodes: list[str], wallet: str, token: str | None, decimals: int, proxies: list[str], round_ndigits: int
|
|
12
11
|
) -> Result[Decimal]:
|
|
13
12
|
if token is None:
|
|
14
|
-
res = balance.
|
|
13
|
+
res = balance.get_sol_balance_with_retries(
|
|
14
|
+
nodes, wallet, retries=RETRIES_BALANCE, timeout=TIMEOUT_BALANCE, proxies=proxies
|
|
15
|
+
)
|
|
15
16
|
else:
|
|
16
|
-
res =
|
|
17
|
+
res = balance.get_token_balance_with_retries(
|
|
17
18
|
nodes, wallet, token, retries=RETRIES_BALANCE, timeout=TIMEOUT_BALANCE, proxies=proxies
|
|
18
19
|
)
|
|
19
20
|
|
|
@@ -21,6 +22,6 @@ def get_balance(
|
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
def get_token_decimals(nodes: list[str], token_address: str, proxies: list[str]) -> Result[int]:
|
|
24
|
-
return
|
|
25
|
+
return token.get_decimals_with_retries(
|
|
25
26
|
nodes, token_address, retries=RETRIES_DECIMALS, timeout=TIMEOUT_DECIMALS, proxies=proxies
|
|
26
27
|
)
|
mm_balance/token_decimals.py
CHANGED
|
@@ -14,7 +14,7 @@ class TokenDecimals(dict[Network, dict[str | None, int]]): # {network: {None: 1
|
|
|
14
14
|
|
|
15
15
|
def get_token_decimals(config: Config) -> TokenDecimals:
|
|
16
16
|
result = TokenDecimals(config.networks())
|
|
17
|
-
proxies = config.proxies
|
|
17
|
+
proxies = config.settings.proxies
|
|
18
18
|
|
|
19
19
|
for group in config.groups:
|
|
20
20
|
# token_decimals is already known
|
mm_balance/workers.py
CHANGED
|
@@ -24,7 +24,7 @@ class Workers:
|
|
|
24
24
|
self.config = config
|
|
25
25
|
self.token_decimals = token_decimals
|
|
26
26
|
self.tasks: dict[Network, list[Task]] = {network: [] for network in config.networks()}
|
|
27
|
-
self.progress_bar = utils.create_progress_bar(config.print_format is not PrintFormat.TABLE)
|
|
27
|
+
self.progress_bar = utils.create_progress_bar(config.settings.print_format is not PrintFormat.TABLE)
|
|
28
28
|
self.progress_bar_task: dict[Network, TaskID] = {}
|
|
29
29
|
|
|
30
30
|
for idx, group in enumerate(config.groups):
|
|
@@ -63,8 +63,8 @@ class Workers:
|
|
|
63
63
|
|
|
64
64
|
def _get_balance(self, network: Network, wallet_address: str, token_address: str | None) -> Result[Decimal]:
|
|
65
65
|
nodes = self.config.nodes[network]
|
|
66
|
-
round_ndigits = self.config.round_ndigits
|
|
67
|
-
proxies = self.config.proxies
|
|
66
|
+
round_ndigits = self.config.settings.round_ndigits
|
|
67
|
+
proxies = self.config.settings.proxies
|
|
68
68
|
token_decimals = self.token_decimals[network][token_address]
|
|
69
69
|
|
|
70
70
|
if network.is_evm_network():
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
mm_balance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
mm_balance/cli.py,sha256=glOveB7bhcmuHBZvKZpmO1iMB1S21E6TnIIwuB25jUo,3398
|
|
3
|
+
mm_balance/config.py,sha256=4zz1hOdob4bronkzLxR9XRuj614OlWcXd5hjqCaRimc,4260
|
|
4
|
+
mm_balance/constants.py,sha256=kqG2zuwv0l-PzDHIrMJVQpfQWiXjr2DsqGPcKmqNJLo,2334
|
|
5
|
+
mm_balance/price.py,sha256=DzvcQngS6wgi_4YWoXxGvOuOkwJvUbN0KI8DeIxbB5A,1494
|
|
6
|
+
mm_balance/result.py,sha256=WsVLbf54cCJ76rIM2zHaOCQXMfZZMczdg8rlC2tDGWY,4533
|
|
7
|
+
mm_balance/token_decimals.py,sha256=7XL_x_1ZIhp_cr1TvXwnqLqINUWFGo0j5_5lWuxmwpE,2277
|
|
8
|
+
mm_balance/utils.py,sha256=bngYS2WFIakGZO31_ey4MPsllvDhgOxkAnGiXqom3J4,286
|
|
9
|
+
mm_balance/workers.py,sha256=vEmmUI-ioMOv1C_Jcz0rnzI3vn7BaVWFnyXddteJkoA,3766
|
|
10
|
+
mm_balance/config/example.toml,sha256=vj5-jDB_V3I0-lSuEByI10P_3o0DIhHYH1QzJu8WVrg,1788
|
|
11
|
+
mm_balance/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
mm_balance/output/utils.py,sha256=WUFwshFMKZKdcwRtO21nhxqW78JeLAatDyHPZhdV96A,716
|
|
13
|
+
mm_balance/output/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
mm_balance/output/formats/json_format.py,sha256=japDhWBVaZ9Znwrhi6BLsUEyn2zyNVxPFYhH7SiILb8,893
|
|
15
|
+
mm_balance/output/formats/table_format.py,sha256=o_3MGOb0Ug9sqfaR61JDucSEtmFF22KlJoqpFWRuFu8,4758
|
|
16
|
+
mm_balance/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
mm_balance/rpc/aptos.py,sha256=1JCYCqDim4tk1axXscaAJRXPd4J6vV1ABFbwMbPgrL0,641
|
|
18
|
+
mm_balance/rpc/btc.py,sha256=iGDoO7HXP6reLI0bWm0UhEl7-UFZTo5xCWNzFngbITw,458
|
|
19
|
+
mm_balance/rpc/evm.py,sha256=ewlMmRrcXKlky3DPNbnUBTVwnvyw7N9iCZLsCX2V14w,1007
|
|
20
|
+
mm_balance/rpc/solana.py,sha256=c756415rlhQkHsVsqxHcA7wIaSM64IqyxMohjz8IGhY,1031
|
|
21
|
+
mm_balance-0.2.1.dist-info/METADATA,sha256=r7KoFJHCD1PNDFC5pmrU7SsdA9YU1uf5Dr7ohPezchs,225
|
|
22
|
+
mm_balance-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
23
|
+
mm_balance-0.2.1.dist-info/entry_points.txt,sha256=rSnP0ZW1a3ACNwTWM7T53CmOycKbzhG43m2_wseENng,50
|
|
24
|
+
mm_balance-0.2.1.dist-info/RECORD,,
|
mm_balance/config/example.yml
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
coins:
|
|
2
|
-
- ticker: SOL
|
|
3
|
-
network: solana
|
|
4
|
-
addresses:
|
|
5
|
-
- 2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S # binance
|
|
6
|
-
|
|
7
|
-
- ticker: USDT
|
|
8
|
-
network: solana
|
|
9
|
-
addresses:
|
|
10
|
-
- 2ojv9BAiHUrvsm9gxDe7fJSzbNZSJcxZvf8dqmWGHG8S # binance
|
|
11
|
-
|
|
12
|
-
- ticker: BTC
|
|
13
|
-
network: bitcoin
|
|
14
|
-
comment: coldwallets
|
|
15
|
-
addresses: |
|
|
16
|
-
34xp4vRoCGJym3xR7yCVPFHoCNxv4Twseo # binance
|
|
17
|
-
bc1qgdjqv0av3q56jvd82tkdjpy7gdp9ut8tlqmgrpmv24sq90ecnvqqjwvw97 # bitfinex
|
|
18
|
-
bc1ql49ydapnjafl5t2cp9zqpjwe6pdgmxy98859v2 # robinhood
|
|
19
|
-
share: 0.1 # 10%
|
|
20
|
-
|
|
21
|
-
- ticker: ETH
|
|
22
|
-
network: ethereum
|
|
23
|
-
comment: okx
|
|
24
|
-
addresses: okx_eth
|
|
25
|
-
|
|
26
|
-
- ticker: USDT
|
|
27
|
-
network: ethereum
|
|
28
|
-
comment: okx
|
|
29
|
-
addresses: okx_eth
|
|
30
|
-
|
|
31
|
-
- ticker: ETH
|
|
32
|
-
network: ethereum
|
|
33
|
-
comment: binance
|
|
34
|
-
addresses: binance_eth
|
|
35
|
-
|
|
36
|
-
- ticker: USDT
|
|
37
|
-
network: ethereum
|
|
38
|
-
comment: binance
|
|
39
|
-
addresses: binance_eth
|
|
40
|
-
|
|
41
|
-
- ticker: USDC
|
|
42
|
-
comment: swap.thala.apt
|
|
43
|
-
network: aptos
|
|
44
|
-
token_address: 0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC
|
|
45
|
-
token_decimals: 6
|
|
46
|
-
addresses: "0x48271d39d0b05bd6efca2278f22277d6fcc375504f9839fd73f74ace240861af" # for a single line it's necessary to use quotes
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
addresses:
|
|
50
|
-
- name: okx_eth
|
|
51
|
-
addresses: |
|
|
52
|
-
0xf59869753f41db720127ceb8dbb8afaf89030de4
|
|
53
|
-
0x65a0947ba5175359bb457d3b34491edf4cbf7997
|
|
54
|
-
0xe9172daf64b05b26eb18f07ac8d6d723acb48f99
|
|
55
|
-
0x4d19c0a5357bc48be0017095d3c871d9afc3f21d
|
|
56
|
-
|
|
57
|
-
- name: binance_eth
|
|
58
|
-
addresses: |
|
|
59
|
-
0xf977814e90da44bfa03b6295a0616a897441acec
|
|
60
|
-
0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503
|
|
61
|
-
|
|
62
|
-
##Optional settings:
|
|
63
|
-
#proxies_url: https://site.com/api/proxies # get proxies from this url
|
|
64
|
-
#proxies:
|
|
65
|
-
#- http://123.123.123.121
|
|
66
|
-
#- http://123.123.123.122
|
|
67
|
-
#- http://123.123.123.123
|
|
68
|
-
#- http://123.123.123.124
|
|
69
|
-
#round_ndigits: 4
|
|
70
|
-
#price: yes
|
|
71
|
-
#skip_empty: no
|
|
72
|
-
#print_debug: no
|
|
73
|
-
#print_format: table # table, json
|
|
74
|
-
#format_number_separator: ","
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: mm-balance
|
|
3
|
-
Version: 0.1.20
|
|
4
|
-
Requires-Python: >=3.12
|
|
5
|
-
Requires-Dist: mm-aptos==0.1.5
|
|
6
|
-
Requires-Dist: mm-btc==0.2.1
|
|
7
|
-
Requires-Dist: mm-crypto-utils==0.0.13
|
|
8
|
-
Requires-Dist: mm-eth==0.2.5
|
|
9
|
-
Requires-Dist: mm-solana==0.2.4
|
|
10
|
-
Requires-Dist: typer>=0.15.1
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
mm_balance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
mm_balance/cli.py,sha256=7yQ0DiSDVYeG9lFav-PJnVswncWIfpZsj1G7CZvMDmc,3119
|
|
3
|
-
mm_balance/config.py,sha256=7dB_8MrefufIi32-ubgiUxjacmeKERGNIbc1yXNw4ec,4578
|
|
4
|
-
mm_balance/constants.py,sha256=kqG2zuwv0l-PzDHIrMJVQpfQWiXjr2DsqGPcKmqNJLo,2334
|
|
5
|
-
mm_balance/price.py,sha256=9pK2Iy44zTcHIlrja5wC_jV03Q7bJr-S8joNzEsyUl0,1485
|
|
6
|
-
mm_balance/result.py,sha256=rwCSnuyK-u4mz1QmQucFKLC2-vpkSoXfFxbu0MScp9g,4524
|
|
7
|
-
mm_balance/token_decimals.py,sha256=5VZyx29euGtE2KIStQjh6xCcVg1qQweFAykUZU6vkRU,2268
|
|
8
|
-
mm_balance/utils.py,sha256=bngYS2WFIakGZO31_ey4MPsllvDhgOxkAnGiXqom3J4,286
|
|
9
|
-
mm_balance/workers.py,sha256=U9oQOkYtg83tbzCdhs-RqrqeX9JAY1H_AW65gLytxMU,3739
|
|
10
|
-
mm_balance/config/example.yml,sha256=7n72tHsnUbZ64q856IeKHlmt-7PvtAgZlB8t4AS0NzI,1899
|
|
11
|
-
mm_balance/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
mm_balance/output/utils.py,sha256=WUFwshFMKZKdcwRtO21nhxqW78JeLAatDyHPZhdV96A,716
|
|
13
|
-
mm_balance/output/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
mm_balance/output/formats/json_format.py,sha256=yMkZTA6vOUpLSixF7H4H4lVZKShRs4B9qmUR8L-CRvw,866
|
|
15
|
-
mm_balance/output/formats/table_format.py,sha256=qN1uMF1cyAVOH31Gohoc_a4l9-pWTSZ8WRxBBSAwj54,4539
|
|
16
|
-
mm_balance/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
mm_balance/rpc/aptos.py,sha256=1JCYCqDim4tk1axXscaAJRXPd4J6vV1ABFbwMbPgrL0,641
|
|
18
|
-
mm_balance/rpc/btc.py,sha256=iGDoO7HXP6reLI0bWm0UhEl7-UFZTo5xCWNzFngbITw,458
|
|
19
|
-
mm_balance/rpc/evm.py,sha256=ewlMmRrcXKlky3DPNbnUBTVwnvyw7N9iCZLsCX2V14w,1007
|
|
20
|
-
mm_balance/rpc/solana.py,sha256=Lwl5Otqy48g4LWfz9uN7_rkICKUIYJvf88PbIAnfMdc,1051
|
|
21
|
-
mm_balance-0.1.20.dist-info/METADATA,sha256=rUfrIh7wO8EWYVsDHysHw0yX202FfCpmGHK9qm_R_nA,268
|
|
22
|
-
mm_balance-0.1.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
23
|
-
mm_balance-0.1.20.dist-info/entry_points.txt,sha256=rSnP0ZW1a3ACNwTWM7T53CmOycKbzhG43m2_wseENng,50
|
|
24
|
-
mm_balance-0.1.20.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|