mm-balance 0.1.14__tar.gz → 0.1.15__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_balance-0.1.14 → mm_balance-0.1.15}/PKG-INFO +1 -1
- {mm_balance-0.1.14 → mm_balance-0.1.15}/justfile +1 -1
- {mm_balance-0.1.14 → mm_balance-0.1.15}/pyproject.toml +4 -4
- mm_balance-0.1.15/src/mm_balance/cli.py +84 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/config/example.yml +1 -0
- mm_balance-0.1.15/src/mm_balance/output/formats/json_format.py +27 -0
- mm_balance-0.1.15/src/mm_balance/output/formats/table_format.py +115 -0
- mm_balance-0.1.15/src/mm_balance/output/utils.py +20 -0
- mm_balance-0.1.15/src/mm_balance/result.py +131 -0
- mm_balance-0.1.14/src/mm_balance/balances.py → mm_balance-0.1.15/src/mm_balance/workers.py +28 -30
- mm_balance-0.1.15/tests/conftest.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/uv.lock +73 -73
- mm_balance-0.1.14/src/mm_balance/cli.py +0 -64
- mm_balance-0.1.14/src/mm_balance/output.py +0 -109
- mm_balance-0.1.14/src/mm_balance/total.py +0 -122
- {mm_balance-0.1.14 → mm_balance-0.1.15}/.gitignore +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/README.md +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/__init__.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/config.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/constants.py +0 -0
- {mm_balance-0.1.14/src/mm_balance/rpc → mm_balance-0.1.15/src/mm_balance/output}/__init__.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/price.py +0 -0
- {mm_balance-0.1.14/tests → mm_balance-0.1.15/src/mm_balance/rpc}/__init__.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/rpc/aptos.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/rpc/btc.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/rpc/evm.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/rpc/solana.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/token_decimals.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/src/mm_balance/utils.py +0 -0
- /mm_balance-0.1.14/tests/conftest.py → /mm_balance-0.1.15/tests/__init__.py +0 -0
- {mm_balance-0.1.14 → mm_balance-0.1.15}/tests/test_dummy.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mm-balance"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.15"
|
|
4
4
|
description = ""
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
dependencies = [
|
|
@@ -22,11 +22,11 @@ dev-dependencies = [
|
|
|
22
22
|
"pytest~=8.3.3",
|
|
23
23
|
"pytest-xdist~=3.6.1",
|
|
24
24
|
"pytest-httpserver~=1.1.0",
|
|
25
|
-
"coverage~=7.6.
|
|
26
|
-
"ruff~=0.7.
|
|
25
|
+
"coverage~=7.6.4",
|
|
26
|
+
"ruff~=0.7.1",
|
|
27
27
|
"pip-audit~=2.7.0",
|
|
28
28
|
"bandit~=1.7.10",
|
|
29
|
-
"mypy~=1.
|
|
29
|
+
"mypy~=1.13.0",
|
|
30
30
|
"types-python-dateutil~=2.9.0.20241003",
|
|
31
31
|
"types-PyYAML~=6.0.12.20240917",
|
|
32
32
|
]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import getpass
|
|
2
|
+
import pathlib
|
|
3
|
+
import pkgutil
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from mm_std import PrintFormat, fatal
|
|
8
|
+
|
|
9
|
+
from mm_balance.config import Config
|
|
10
|
+
from mm_balance.constants import NETWORKS
|
|
11
|
+
from mm_balance.output.formats import json_format, table_format
|
|
12
|
+
from mm_balance.price import Prices, get_prices
|
|
13
|
+
from mm_balance.result import create_balances_result
|
|
14
|
+
from mm_balance.token_decimals import get_token_decimals
|
|
15
|
+
from mm_balance.workers import Workers
|
|
16
|
+
|
|
17
|
+
app = typer.Typer(no_args_is_help=True, pretty_exceptions_enable=False, add_completion=False)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def example_callback(value: bool) -> None:
|
|
21
|
+
if value:
|
|
22
|
+
data = pkgutil.get_data(__name__, "config/example.yml")
|
|
23
|
+
typer.echo(data)
|
|
24
|
+
raise typer.Exit
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def networks_callback(value: bool) -> None:
|
|
28
|
+
if value:
|
|
29
|
+
for network in NETWORKS:
|
|
30
|
+
typer.echo(network)
|
|
31
|
+
raise typer.Exit
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@app.command()
|
|
35
|
+
def cli(
|
|
36
|
+
config_path: Annotated[pathlib.Path, typer.Argument()],
|
|
37
|
+
print_format: Annotated[PrintFormat | None, typer.Option("--format", "-f", help="Print format.")] = None,
|
|
38
|
+
skip_empty: Annotated[bool | None, typer.Option("--skip-empty", "-s", help="Skip empty balances.")] = None,
|
|
39
|
+
debug: Annotated[bool | None, typer.Option("--debug", "-d", help="Print debug info.")] = None,
|
|
40
|
+
price: Annotated[bool | None, typer.Option("--price/--no-price", help="Print prices.")] = None,
|
|
41
|
+
_example: Annotated[bool | None, typer.Option("--example", callback=example_callback, help="Print a config example.")] = None,
|
|
42
|
+
_networks: Annotated[
|
|
43
|
+
bool | None, typer.Option("--networks", callback=networks_callback, help="Print supported networks.")
|
|
44
|
+
] = None,
|
|
45
|
+
) -> None:
|
|
46
|
+
zip_password = "" # nosec
|
|
47
|
+
if config_path.name.endswith(".zip"):
|
|
48
|
+
zip_password = getpass.getpass("zip password")
|
|
49
|
+
config = Config.read_config(config_path, zip_password=zip_password)
|
|
50
|
+
|
|
51
|
+
if print_format is not None:
|
|
52
|
+
config.print_format = print_format
|
|
53
|
+
if debug is not None:
|
|
54
|
+
config.print_debug = debug
|
|
55
|
+
if skip_empty is not None:
|
|
56
|
+
config.skip_empty = skip_empty
|
|
57
|
+
if price is not None:
|
|
58
|
+
config.price = price
|
|
59
|
+
|
|
60
|
+
if config.print_debug and config.print_format is PrintFormat.TABLE:
|
|
61
|
+
table_format.print_nodes(config)
|
|
62
|
+
|
|
63
|
+
token_decimals = get_token_decimals(config)
|
|
64
|
+
if config.print_debug and config.print_format is PrintFormat.TABLE:
|
|
65
|
+
table_format.print_token_decimals(token_decimals)
|
|
66
|
+
|
|
67
|
+
prices = get_prices(config) if config.price else Prices()
|
|
68
|
+
if config.print_format is PrintFormat.TABLE:
|
|
69
|
+
table_format.print_prices(config, prices)
|
|
70
|
+
|
|
71
|
+
workers = Workers(config, token_decimals)
|
|
72
|
+
workers.process()
|
|
73
|
+
|
|
74
|
+
result = create_balances_result(config, prices, workers)
|
|
75
|
+
if config.print_format is PrintFormat.TABLE:
|
|
76
|
+
table_format.print_result(config, result, workers)
|
|
77
|
+
elif config.print_format is PrintFormat.JSON:
|
|
78
|
+
json_format.print_result(config, token_decimals, prices, workers, result)
|
|
79
|
+
else:
|
|
80
|
+
fatal("Unsupported print format")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
app()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from mm_std import print_json
|
|
2
|
+
|
|
3
|
+
from mm_balance.config import Config
|
|
4
|
+
from mm_balance.price import Prices
|
|
5
|
+
from mm_balance.result import BalancesResult
|
|
6
|
+
from mm_balance.token_decimals import TokenDecimals
|
|
7
|
+
from mm_balance.workers import Workers
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def print_result(config: Config, token_decimals: TokenDecimals, prices: Prices, workers: Workers, result: BalancesResult) -> None:
|
|
11
|
+
data: dict[str, object] = {}
|
|
12
|
+
if config.print_debug:
|
|
13
|
+
data["nodes"] = config.nodes
|
|
14
|
+
data["token_decimals"] = token_decimals
|
|
15
|
+
if config.price:
|
|
16
|
+
data["prices"] = prices
|
|
17
|
+
|
|
18
|
+
data["groups"] = result.groups
|
|
19
|
+
data["total"] = result.total
|
|
20
|
+
if config.has_share():
|
|
21
|
+
data["total_share"] = result.total_share
|
|
22
|
+
|
|
23
|
+
errors = workers.get_errors()
|
|
24
|
+
if errors:
|
|
25
|
+
data["errors"] = errors
|
|
26
|
+
|
|
27
|
+
print_json(data)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from mm_std import print_table
|
|
4
|
+
|
|
5
|
+
from mm_balance.config import Config
|
|
6
|
+
from mm_balance.output.utils import format_number
|
|
7
|
+
from mm_balance.price import Prices
|
|
8
|
+
from mm_balance.result import BalancesResult, GroupResult, Total
|
|
9
|
+
from mm_balance.token_decimals import TokenDecimals
|
|
10
|
+
from mm_balance.workers import Workers
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def print_nodes(config: Config) -> None:
|
|
14
|
+
rows = []
|
|
15
|
+
for network, nodes in config.nodes.items():
|
|
16
|
+
rows.append([network, "\n".join(nodes)])
|
|
17
|
+
print_table("Nodes", ["network", "nodes"], rows)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def print_token_decimals(token_decimals: TokenDecimals) -> None:
|
|
21
|
+
rows = []
|
|
22
|
+
for network, decimals in token_decimals.items():
|
|
23
|
+
rows.append([network, decimals])
|
|
24
|
+
print_table("Token Decimals", ["network", "decimals"], rows)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def print_prices(config: Config, prices: Prices) -> None:
|
|
28
|
+
if config.price:
|
|
29
|
+
rows = []
|
|
30
|
+
for ticker, price in prices.items():
|
|
31
|
+
rows.append([ticker, format_number(round(price, config.round_ndigits), config.format_number_separator, "$")])
|
|
32
|
+
print_table("Prices", ["coin", "usd"], rows)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def print_result(config: Config, result: BalancesResult, workers: Workers) -> None:
|
|
36
|
+
for group in result.groups:
|
|
37
|
+
_print_group(config, group)
|
|
38
|
+
|
|
39
|
+
_print_total(config, result.total, False)
|
|
40
|
+
if config.has_share():
|
|
41
|
+
_print_total(config, result.total_share, True)
|
|
42
|
+
|
|
43
|
+
_print_errors(config, workers)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _print_errors(config: Config, workers: Workers) -> None:
|
|
47
|
+
error_tasks = workers.get_errors()
|
|
48
|
+
if not error_tasks:
|
|
49
|
+
return
|
|
50
|
+
rows = []
|
|
51
|
+
for task in error_tasks:
|
|
52
|
+
group = config.groups[task.group_index]
|
|
53
|
+
rows.append([group.ticker + " / " + group.network, task.wallet_address, task.balance.err]) # type: ignore[union-attr]
|
|
54
|
+
print_table("Errors", ["coin", "address", "error"], rows)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _print_total(config: Config, total: Total, is_share_total: bool) -> None:
|
|
58
|
+
table_name = "Total, share" if is_share_total else "Total"
|
|
59
|
+
headers = ["coin", "balance"]
|
|
60
|
+
|
|
61
|
+
rows = []
|
|
62
|
+
for ticker, balance in total.coin_balances.items():
|
|
63
|
+
balance_str = format_number(balance, config.format_number_separator)
|
|
64
|
+
row = [ticker, balance_str]
|
|
65
|
+
if config.price:
|
|
66
|
+
usd_value_str = format_number(total.coin_usd_values[ticker], config.format_number_separator, "$")
|
|
67
|
+
portfolio_share = total.portfolio_share[ticker]
|
|
68
|
+
row += [usd_value_str, f"{portfolio_share}%"]
|
|
69
|
+
rows.append(row)
|
|
70
|
+
|
|
71
|
+
if config.price:
|
|
72
|
+
headers += ["usd", "portfolio_share"]
|
|
73
|
+
if total.stablecoin_sum > 0:
|
|
74
|
+
rows.append(["stablecoin_sum", format_number(total.stablecoin_sum, config.format_number_separator, "$")])
|
|
75
|
+
rows.append(["total_usd_sum", format_number(total.total_usd_sum, config.format_number_separator, "$")])
|
|
76
|
+
|
|
77
|
+
print_table(table_name, headers, rows)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _print_group(config: Config, group: GroupResult) -> None:
|
|
81
|
+
group_name = group.ticker
|
|
82
|
+
if group.comment:
|
|
83
|
+
group_name += " / " + group.comment
|
|
84
|
+
group_name += " / " + group.network
|
|
85
|
+
|
|
86
|
+
rows = []
|
|
87
|
+
for address in group.addresses:
|
|
88
|
+
if isinstance(address.balance, str):
|
|
89
|
+
rows.append([address.address, address.balance])
|
|
90
|
+
else:
|
|
91
|
+
if config.skip_empty and address.balance.balance == Decimal(0):
|
|
92
|
+
continue
|
|
93
|
+
balance_str = format_number(address.balance.balance, config.format_number_separator)
|
|
94
|
+
row = [address.address, balance_str]
|
|
95
|
+
if config.price:
|
|
96
|
+
usd_value_str = format_number(address.balance.usd_value, config.format_number_separator, "$")
|
|
97
|
+
row.append(usd_value_str)
|
|
98
|
+
rows.append(row)
|
|
99
|
+
|
|
100
|
+
sum_row = ["sum", format_number(group.balance_sum, config.format_number_separator)]
|
|
101
|
+
if config.price:
|
|
102
|
+
sum_row.append(format_number(group.usd_sum, config.format_number_separator, "$"))
|
|
103
|
+
rows.append(sum_row)
|
|
104
|
+
|
|
105
|
+
if group.share < Decimal(1):
|
|
106
|
+
sum_share_str = format_number(group.balance_sum_share, config.format_number_separator)
|
|
107
|
+
sum_share_row = [f"sum_share, {group.share}", sum_share_str]
|
|
108
|
+
if config.price:
|
|
109
|
+
sum_share_row.append(format_number(group.usd_sum_share, config.format_number_separator, "$"))
|
|
110
|
+
rows.append(sum_share_row)
|
|
111
|
+
|
|
112
|
+
table_headers = ["address", "balance"]
|
|
113
|
+
if config.price:
|
|
114
|
+
table_headers += ["usd"]
|
|
115
|
+
print_table(group_name, table_headers, rows)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from rich.progress import BarColumn, MofNCompleteColumn, Progress, TaskID, TextColumn
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def format_number(value: Decimal, separator: str, extra: str | None = None) -> str:
|
|
7
|
+
str_value = f"{value:,}".replace(",", separator)
|
|
8
|
+
if extra == "$":
|
|
9
|
+
return "$" + str_value
|
|
10
|
+
elif extra == "%":
|
|
11
|
+
return str_value + "%"
|
|
12
|
+
return str_value
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create_progress_bar(disable: bool) -> Progress:
|
|
16
|
+
return Progress(TextColumn("[progress.description]{task.description}"), BarColumn(), MofNCompleteColumn(), disable=disable)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def create_progress_task(progress: Progress, description: str, total: int) -> TaskID:
|
|
20
|
+
return progress.add_task("[green]" + description, total=total)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
|
|
5
|
+
from mm_std import Ok
|
|
6
|
+
|
|
7
|
+
from mm_balance.config import Config, Group
|
|
8
|
+
from mm_balance.constants import USD_STABLECOINS, Network
|
|
9
|
+
from mm_balance.price import Prices
|
|
10
|
+
from mm_balance.workers import Task, Workers
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class Balance:
|
|
15
|
+
balance: Decimal
|
|
16
|
+
usd_value: Decimal # 0 if config.price is False
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class AddressBalance:
|
|
21
|
+
address: str
|
|
22
|
+
balance: Balance | str # balance value or error message
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class GroupResult:
|
|
27
|
+
ticker: str
|
|
28
|
+
network: Network
|
|
29
|
+
comment: str
|
|
30
|
+
share: Decimal
|
|
31
|
+
addresses: list[AddressBalance]
|
|
32
|
+
balance_sum: Decimal # sum of all balances in the group
|
|
33
|
+
usd_sum: Decimal # sum of all usd values in the group
|
|
34
|
+
balance_sum_share: Decimal # sum of all balances in the group multiplied by share
|
|
35
|
+
usd_sum_share: Decimal # sum of all usd values in the group multiplied by share
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class Total:
|
|
40
|
+
coin_balances: dict[str, Decimal]
|
|
41
|
+
coin_usd_values: dict[str, Decimal]
|
|
42
|
+
portfolio_share: dict[str, Decimal] # ticker -> usd value % from total usd value
|
|
43
|
+
stablecoin_sum: Decimal # sum of usd stablecoins: usdt, usdc, etc..
|
|
44
|
+
total_usd_sum: Decimal # sum of all coins in USD
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class BalancesResult:
|
|
49
|
+
groups: list[GroupResult]
|
|
50
|
+
total: Total
|
|
51
|
+
total_share: Total
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def create_balances_result(config: Config, prices: Prices, workers: Workers) -> BalancesResult:
|
|
55
|
+
groups = []
|
|
56
|
+
for group_index, group in enumerate(config.groups):
|
|
57
|
+
tasks = workers.get_group_tasks(group_index, group.network)
|
|
58
|
+
groups.append(_create_group_result(config, group, tasks, prices))
|
|
59
|
+
|
|
60
|
+
total = _create_total(False, groups)
|
|
61
|
+
total_share = _create_total(True, groups)
|
|
62
|
+
return BalancesResult(groups=groups, total=total, total_share=total_share)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _create_total(use_share: bool, groups: list[GroupResult]) -> Total:
|
|
66
|
+
coin_balances: dict[str, Decimal] = defaultdict(Decimal) # ticker -> balance
|
|
67
|
+
coin_usd_values: dict[str, Decimal] = defaultdict(Decimal) # ticker -> usd value
|
|
68
|
+
portfolio_share: dict[str, Decimal] = defaultdict(Decimal) # ticker -> usd value % from total usd value
|
|
69
|
+
total_usd_sum = Decimal(0)
|
|
70
|
+
stablecoin_sum = Decimal(0)
|
|
71
|
+
|
|
72
|
+
for group in groups:
|
|
73
|
+
balance_value = group.balance_sum_share if use_share else group.balance_sum
|
|
74
|
+
usd_value = group.usd_sum_share if use_share else group.usd_sum
|
|
75
|
+
coin_balances[group.ticker] += balance_value
|
|
76
|
+
coin_usd_values[group.ticker] += usd_value
|
|
77
|
+
if group.ticker in USD_STABLECOINS:
|
|
78
|
+
stablecoin_sum += usd_value # TODO: or balance_value?
|
|
79
|
+
total_usd_sum += usd_value
|
|
80
|
+
|
|
81
|
+
if total_usd_sum > 0:
|
|
82
|
+
for ticker, usd_value in coin_usd_values.items():
|
|
83
|
+
if ticker in USD_STABLECOINS:
|
|
84
|
+
portfolio_share[ticker] = round(stablecoin_sum * 100 / total_usd_sum, 2)
|
|
85
|
+
else:
|
|
86
|
+
portfolio_share[ticker] = round(usd_value * 100 / total_usd_sum, 2)
|
|
87
|
+
|
|
88
|
+
return Total(
|
|
89
|
+
coin_balances=coin_balances,
|
|
90
|
+
coin_usd_values=coin_usd_values,
|
|
91
|
+
portfolio_share=portfolio_share,
|
|
92
|
+
stablecoin_sum=stablecoin_sum,
|
|
93
|
+
total_usd_sum=total_usd_sum,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _create_group_result(config: Config, group: Group, tasks: list[Task], prices: Prices) -> GroupResult:
|
|
98
|
+
addresses = []
|
|
99
|
+
balance_sum = Decimal(0)
|
|
100
|
+
usd_sum = Decimal(0)
|
|
101
|
+
for task in tasks:
|
|
102
|
+
balance: Balance | str
|
|
103
|
+
if task.balance is None:
|
|
104
|
+
balance = "balance is None! Something went wrong."
|
|
105
|
+
else:
|
|
106
|
+
if isinstance(task.balance, Ok):
|
|
107
|
+
coin_value = task.balance.ok
|
|
108
|
+
usd_value = Decimal(0)
|
|
109
|
+
if group.ticker in prices:
|
|
110
|
+
usd_value = round(coin_value * prices[group.ticker], config.round_ndigits)
|
|
111
|
+
balance = Balance(balance=coin_value, usd_value=usd_value)
|
|
112
|
+
balance_sum += balance.balance
|
|
113
|
+
usd_sum += balance.usd_value
|
|
114
|
+
else:
|
|
115
|
+
balance = task.balance.err
|
|
116
|
+
addresses.append(AddressBalance(address=task.wallet_address, balance=balance))
|
|
117
|
+
|
|
118
|
+
balance_sum_share = balance_sum * group.share
|
|
119
|
+
usd_sum_share = usd_sum * group.share
|
|
120
|
+
|
|
121
|
+
return GroupResult(
|
|
122
|
+
ticker=group.ticker,
|
|
123
|
+
network=group.network,
|
|
124
|
+
comment=group.comment,
|
|
125
|
+
share=group.share,
|
|
126
|
+
addresses=addresses,
|
|
127
|
+
balance_sum=balance_sum,
|
|
128
|
+
usd_sum=usd_sum,
|
|
129
|
+
balance_sum_share=balance_sum_share,
|
|
130
|
+
usd_sum_share=usd_sum_share,
|
|
131
|
+
)
|
|
@@ -1,41 +1,39 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from dataclasses import dataclass
|
|
3
2
|
from decimal import Decimal
|
|
4
3
|
|
|
5
|
-
from mm_std import ConcurrentTasks, Result
|
|
6
|
-
from pydantic import BaseModel
|
|
4
|
+
from mm_std import ConcurrentTasks, PrintFormat, Result
|
|
7
5
|
from rich.progress import TaskID
|
|
8
6
|
|
|
9
|
-
from mm_balance import output
|
|
10
7
|
from mm_balance.config import Config
|
|
11
8
|
from mm_balance.constants import NETWORK_APTOS, NETWORK_BITCOIN, NETWORK_SOLANA, Network
|
|
9
|
+
from mm_balance.output import utils
|
|
12
10
|
from mm_balance.rpc import aptos, btc, evm, solana
|
|
13
11
|
from mm_balance.token_decimals import TokenDecimals
|
|
14
12
|
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
@dataclass
|
|
15
|
+
class Task:
|
|
16
|
+
group_index: int
|
|
17
|
+
wallet_address: str
|
|
18
|
+
token_address: str | None
|
|
19
|
+
balance: Result[Decimal] | None = None
|
|
20
|
+
|
|
22
21
|
|
|
22
|
+
class Workers:
|
|
23
23
|
def __init__(self, config: Config, token_decimals: TokenDecimals):
|
|
24
24
|
self.config = config
|
|
25
25
|
self.token_decimals = token_decimals
|
|
26
|
-
self.tasks: dict[Network, list[
|
|
27
|
-
self.progress_bar =
|
|
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)
|
|
28
28
|
self.progress_bar_task: dict[Network, TaskID] = {}
|
|
29
29
|
|
|
30
30
|
for idx, group in enumerate(config.groups):
|
|
31
|
-
task_list = [
|
|
31
|
+
task_list = [Task(group_index=idx, wallet_address=a, token_address=group.token_address) for a in group.addresses]
|
|
32
32
|
self.tasks[group.network].extend(task_list)
|
|
33
33
|
|
|
34
34
|
for network in config.networks():
|
|
35
35
|
if self.tasks[network]:
|
|
36
|
-
self.progress_bar_task[network] =
|
|
37
|
-
self.progress_bar, network, len(self.tasks[network])
|
|
38
|
-
)
|
|
36
|
+
self.progress_bar_task[network] = utils.create_progress_task(self.progress_bar, network, len(self.tasks[network]))
|
|
39
37
|
|
|
40
38
|
def process(self) -> None:
|
|
41
39
|
with self.progress_bar:
|
|
@@ -44,10 +42,22 @@ class Balances:
|
|
|
44
42
|
job.add_task(network, self._process_network, args=(network,))
|
|
45
43
|
job.execute()
|
|
46
44
|
|
|
45
|
+
def get_group_tasks(self, group_index: int, network: Network) -> list[Task]:
|
|
46
|
+
# TODO: can we get network by group_index?
|
|
47
|
+
return [b for b in self.tasks[network] if b.group_index == group_index]
|
|
48
|
+
|
|
49
|
+
def get_errors(self) -> list[Task]:
|
|
50
|
+
result = []
|
|
51
|
+
for network in self.tasks:
|
|
52
|
+
for task in self.tasks[network]:
|
|
53
|
+
if task.balance is not None and task.balance.is_err():
|
|
54
|
+
result.append(task)
|
|
55
|
+
return result
|
|
56
|
+
|
|
47
57
|
def _process_network(self, network: Network) -> None:
|
|
48
58
|
job = ConcurrentTasks(max_workers=self.config.workers[network])
|
|
49
59
|
for idx, task in enumerate(self.tasks[network]):
|
|
50
|
-
job.add_task(str(idx), self._get_balance, args=(network, task.
|
|
60
|
+
job.add_task(str(idx), self._get_balance, args=(network, task.wallet_address, task.token_address))
|
|
51
61
|
job.execute()
|
|
52
62
|
# TODO: print job.exceptions if present
|
|
53
63
|
for idx, _task in enumerate(self.tasks[network]):
|
|
@@ -72,15 +82,3 @@ class Balances:
|
|
|
72
82
|
|
|
73
83
|
self.progress_bar.update(self.progress_bar_task[network], advance=1)
|
|
74
84
|
return res
|
|
75
|
-
|
|
76
|
-
def get_group_balances(self, group_index: int, network: Network) -> list[Balance]:
|
|
77
|
-
# TODO: can we get network by group_index?
|
|
78
|
-
return [b for b in self.tasks[network] if b.group_index == group_index]
|
|
79
|
-
|
|
80
|
-
def get_errors(self) -> list[Balance]:
|
|
81
|
-
result = []
|
|
82
|
-
for network in self.tasks:
|
|
83
|
-
for task in self.tasks[network]:
|
|
84
|
-
if task.balance is not None and task.balance.is_err():
|
|
85
|
-
result.append(task)
|
|
86
|
-
return result
|
|
File without changes
|
|
@@ -371,40 +371,40 @@ wheels = [
|
|
|
371
371
|
|
|
372
372
|
[[package]]
|
|
373
373
|
name = "coverage"
|
|
374
|
-
version = "7.6.
|
|
375
|
-
source = { registry = "https://pypi.org/simple" }
|
|
376
|
-
sdist = { url = "https://files.pythonhosted.org/packages/12/
|
|
377
|
-
wheels = [
|
|
378
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
379
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
380
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
381
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
382
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
383
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
384
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
385
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
386
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
387
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
388
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
389
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
390
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
391
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
392
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
393
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
394
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
395
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
396
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
397
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
398
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
399
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
400
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
401
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
402
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
403
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
404
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
405
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
406
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
407
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
374
|
+
version = "7.6.4"
|
|
375
|
+
source = { registry = "https://pypi.org/simple" }
|
|
376
|
+
sdist = { url = "https://files.pythonhosted.org/packages/52/12/3669b6382792783e92046730ad3327f53b2726f0603f4c311c4da4824222/coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73", size = 798716 }
|
|
377
|
+
wheels = [
|
|
378
|
+
{ url = "https://files.pythonhosted.org/packages/7d/e7/9291de916d084f41adddfd4b82246e68d61d6a75747f075f7e64628998d2/coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2", size = 207013 },
|
|
379
|
+
{ url = "https://files.pythonhosted.org/packages/27/03/932c2c5717a7fa80cd43c6a07d3177076d97b79f12f40f882f9916db0063/coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117", size = 207251 },
|
|
380
|
+
{ url = "https://files.pythonhosted.org/packages/d5/3f/0af47dcb9327f65a45455fbca846fe96eb57c153af46c4754a3ba678938a/coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613", size = 240268 },
|
|
381
|
+
{ url = "https://files.pythonhosted.org/packages/8a/3c/37a9d81bbd4b23bc7d46ca820e16174c613579c66342faa390a271d2e18b/coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27", size = 237298 },
|
|
382
|
+
{ url = "https://files.pythonhosted.org/packages/c0/70/6b0627e5bd68204ee580126ed3513140b2298995c1233bd67404b4e44d0e/coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52", size = 239367 },
|
|
383
|
+
{ url = "https://files.pythonhosted.org/packages/3c/eb/634d7dfab24ac3b790bebaf9da0f4a5352cbc125ce6a9d5c6cf4c6cae3c7/coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2", size = 238853 },
|
|
384
|
+
{ url = "https://files.pythonhosted.org/packages/d9/0d/8e3ed00f1266ef7472a4e33458f42e39492e01a64281084fb3043553d3f1/coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1", size = 237160 },
|
|
385
|
+
{ url = "https://files.pythonhosted.org/packages/ce/9c/4337f468ef0ab7a2e0887a9c9da0e58e2eada6fc6cbee637a4acd5dfd8a9/coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5", size = 238824 },
|
|
386
|
+
{ url = "https://files.pythonhosted.org/packages/5e/09/3e94912b8dd37251377bb02727a33a67ee96b84bbbe092f132b401ca5dd9/coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17", size = 209639 },
|
|
387
|
+
{ url = "https://files.pythonhosted.org/packages/01/69/d4f3a4101171f32bc5b3caec8ff94c2c60f700107a6aaef7244b2c166793/coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08", size = 210428 },
|
|
388
|
+
{ url = "https://files.pythonhosted.org/packages/c2/4d/2dede4f7cb5a70fb0bb40a57627fddf1dbdc6b9c1db81f7c4dcdcb19e2f4/coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9", size = 207039 },
|
|
389
|
+
{ url = "https://files.pythonhosted.org/packages/3f/f9/d86368ae8c79e28f1fb458ebc76ae9ff3e8bd8069adc24e8f2fed03c58b7/coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba", size = 207298 },
|
|
390
|
+
{ url = "https://files.pythonhosted.org/packages/64/c5/b4cc3c3f64622c58fbfd4d8b9a7a8ce9d355f172f91fcabbba1f026852f6/coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c", size = 239813 },
|
|
391
|
+
{ url = "https://files.pythonhosted.org/packages/8a/86/14c42e60b70a79b26099e4d289ccdfefbc68624d096f4481163085aa614c/coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06", size = 236959 },
|
|
392
|
+
{ url = "https://files.pythonhosted.org/packages/7f/f8/4436a643631a2fbab4b44d54f515028f6099bfb1cd95b13cfbf701e7f2f2/coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f", size = 238950 },
|
|
393
|
+
{ url = "https://files.pythonhosted.org/packages/49/50/1571810ddd01f99a0a8be464a4ac8b147f322cd1e8e296a1528984fc560b/coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b", size = 238610 },
|
|
394
|
+
{ url = "https://files.pythonhosted.org/packages/f3/8c/6312d241fe7cbd1f0cade34a62fea6f333d1a261255d76b9a87074d8703c/coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21", size = 236697 },
|
|
395
|
+
{ url = "https://files.pythonhosted.org/packages/ce/5f/fef33dfd05d87ee9030f614c857deb6df6556b8f6a1c51bbbb41e24ee5ac/coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a", size = 238541 },
|
|
396
|
+
{ url = "https://files.pythonhosted.org/packages/a9/64/6a984b6e92e1ea1353b7ffa08e27f707a5e29b044622445859200f541e8c/coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e", size = 209707 },
|
|
397
|
+
{ url = "https://files.pythonhosted.org/packages/5c/60/ce5a9e942e9543783b3db5d942e0578b391c25cdd5e7f342d854ea83d6b7/coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963", size = 210439 },
|
|
398
|
+
{ url = "https://files.pythonhosted.org/packages/78/53/6719677e92c308207e7f10561a1b16ab8b5c00e9328efc9af7cfd6fb703e/coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f", size = 207784 },
|
|
399
|
+
{ url = "https://files.pythonhosted.org/packages/fa/dd/7054928930671fcb39ae6a83bb71d9ab5f0afb733172543ced4b09a115ca/coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806", size = 208058 },
|
|
400
|
+
{ url = "https://files.pythonhosted.org/packages/b5/7d/fd656ddc2b38301927b9eb3aae3fe827e7aa82e691923ed43721fd9423c9/coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11", size = 250772 },
|
|
401
|
+
{ url = "https://files.pythonhosted.org/packages/90/d0/eb9a3cc2100b83064bb086f18aedde3afffd7de6ead28f69736c00b7f302/coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3", size = 246490 },
|
|
402
|
+
{ url = "https://files.pythonhosted.org/packages/45/44/3f64f38f6faab8a0cfd2c6bc6eb4c6daead246b97cf5f8fc23bf3788f841/coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a", size = 248848 },
|
|
403
|
+
{ url = "https://files.pythonhosted.org/packages/5d/11/4c465a5f98656821e499f4b4619929bd5a34639c466021740ecdca42aa30/coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc", size = 248340 },
|
|
404
|
+
{ url = "https://files.pythonhosted.org/packages/f1/96/ebecda2d016cce9da812f404f720ca5df83c6b29f65dc80d2000d0078741/coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70", size = 246229 },
|
|
405
|
+
{ url = "https://files.pythonhosted.org/packages/16/d9/3d820c00066ae55d69e6d0eae11d6149a5ca7546de469ba9d597f01bf2d7/coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef", size = 247510 },
|
|
406
|
+
{ url = "https://files.pythonhosted.org/packages/8f/c3/4fa1eb412bb288ff6bfcc163c11700ff06e02c5fad8513817186e460ed43/coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e", size = 210353 },
|
|
407
|
+
{ url = "https://files.pythonhosted.org/packages/7e/77/03fc2979d1538884d921c2013075917fc927f41cd8526909852fe4494112/coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1", size = 211502 },
|
|
408
408
|
]
|
|
409
409
|
|
|
410
410
|
[[package]]
|
|
@@ -937,7 +937,7 @@ wheels = [
|
|
|
937
937
|
|
|
938
938
|
[[package]]
|
|
939
939
|
name = "mm-balance"
|
|
940
|
-
version = "0.1.
|
|
940
|
+
version = "0.1.15"
|
|
941
941
|
source = { editable = "." }
|
|
942
942
|
dependencies = [
|
|
943
943
|
{ name = "mm-aptos" },
|
|
@@ -947,7 +947,7 @@ dependencies = [
|
|
|
947
947
|
{ name = "typer" },
|
|
948
948
|
]
|
|
949
949
|
|
|
950
|
-
[package.
|
|
950
|
+
[package.dependency-groups]
|
|
951
951
|
dev = [
|
|
952
952
|
{ name = "bandit" },
|
|
953
953
|
{ name = "coverage" },
|
|
@@ -970,16 +970,16 @@ requires-dist = [
|
|
|
970
970
|
{ name = "typer", specifier = ">=0.12.5" },
|
|
971
971
|
]
|
|
972
972
|
|
|
973
|
-
[package.metadata.
|
|
973
|
+
[package.metadata.dependency-groups]
|
|
974
974
|
dev = [
|
|
975
975
|
{ name = "bandit", specifier = "~=1.7.10" },
|
|
976
|
-
{ name = "coverage", specifier = "~=7.6.
|
|
977
|
-
{ name = "mypy", specifier = "~=1.
|
|
976
|
+
{ name = "coverage", specifier = "~=7.6.4" },
|
|
977
|
+
{ name = "mypy", specifier = "~=1.13.0" },
|
|
978
978
|
{ name = "pip-audit", specifier = "~=2.7.0" },
|
|
979
979
|
{ name = "pytest", specifier = "~=8.3.3" },
|
|
980
980
|
{ name = "pytest-httpserver", specifier = "~=1.1.0" },
|
|
981
981
|
{ name = "pytest-xdist", specifier = "~=3.6.1" },
|
|
982
|
-
{ name = "ruff", specifier = "~=0.7.
|
|
982
|
+
{ name = "ruff", specifier = "~=0.7.1" },
|
|
983
983
|
{ name = "types-python-dateutil", specifier = "~=2.9.0.20241003" },
|
|
984
984
|
{ name = "types-pyyaml", specifier = "~=6.0.12.20240917" },
|
|
985
985
|
]
|
|
@@ -1132,25 +1132,25 @@ wheels = [
|
|
|
1132
1132
|
|
|
1133
1133
|
[[package]]
|
|
1134
1134
|
name = "mypy"
|
|
1135
|
-
version = "1.
|
|
1135
|
+
version = "1.13.0"
|
|
1136
1136
|
source = { registry = "https://pypi.org/simple" }
|
|
1137
1137
|
dependencies = [
|
|
1138
1138
|
{ name = "mypy-extensions" },
|
|
1139
1139
|
{ name = "typing-extensions" },
|
|
1140
1140
|
]
|
|
1141
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1141
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e8/21/7e9e523537991d145ab8a0a2fd98548d67646dc2aaaf6091c31ad883e7c1/mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e", size = 3152532 }
|
|
1142
1142
|
wheels = [
|
|
1143
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1144
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1145
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1146
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1147
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1148
|
-
{ url = "https://files.pythonhosted.org/packages/bb/
|
|
1149
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1150
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1151
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1152
|
-
{ url = "https://files.pythonhosted.org/packages/59/
|
|
1153
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1143
|
+
{ url = "https://files.pythonhosted.org/packages/fb/31/c526a7bd2e5c710ae47717c7a5f53f616db6d9097caf48ad650581e81748/mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5", size = 11077900 },
|
|
1144
|
+
{ url = "https://files.pythonhosted.org/packages/83/67/b7419c6b503679d10bd26fc67529bc6a1f7a5f220bbb9f292dc10d33352f/mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e", size = 10074818 },
|
|
1145
|
+
{ url = "https://files.pythonhosted.org/packages/ba/07/37d67048786ae84e6612575e173d713c9a05d0ae495dde1e68d972207d98/mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2", size = 12589275 },
|
|
1146
|
+
{ url = "https://files.pythonhosted.org/packages/1f/17/b1018c6bb3e9f1ce3956722b3bf91bff86c1cefccca71cec05eae49d6d41/mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0", size = 13037783 },
|
|
1147
|
+
{ url = "https://files.pythonhosted.org/packages/cb/32/cd540755579e54a88099aee0287086d996f5a24281a673f78a0e14dba150/mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2", size = 9726197 },
|
|
1148
|
+
{ url = "https://files.pythonhosted.org/packages/11/bb/ab4cfdc562cad80418f077d8be9b4491ee4fb257440da951b85cbb0a639e/mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7", size = 11069721 },
|
|
1149
|
+
{ url = "https://files.pythonhosted.org/packages/59/3b/a393b1607cb749ea2c621def5ba8c58308ff05e30d9dbdc7c15028bca111/mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62", size = 10063996 },
|
|
1150
|
+
{ url = "https://files.pythonhosted.org/packages/d1/1f/6b76be289a5a521bb1caedc1f08e76ff17ab59061007f201a8a18cc514d1/mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8", size = 12584043 },
|
|
1151
|
+
{ url = "https://files.pythonhosted.org/packages/a6/83/5a85c9a5976c6f96e3a5a7591aa28b4a6ca3a07e9e5ba0cec090c8b596d6/mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7", size = 13036996 },
|
|
1152
|
+
{ url = "https://files.pythonhosted.org/packages/b4/59/c39a6f752f1f893fccbcf1bdd2aca67c79c842402b5283563d006a67cf76/mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc", size = 9737709 },
|
|
1153
|
+
{ url = "https://files.pythonhosted.org/packages/3b/86/72ce7f57431d87a7ff17d442f521146a6585019eb8f4f31b7c02801f78ad/mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a", size = 2647043 },
|
|
1154
1154
|
]
|
|
1155
1155
|
|
|
1156
1156
|
[[package]]
|
|
@@ -1612,27 +1612,27 @@ wheels = [
|
|
|
1612
1612
|
|
|
1613
1613
|
[[package]]
|
|
1614
1614
|
name = "ruff"
|
|
1615
|
-
version = "0.7.
|
|
1615
|
+
version = "0.7.1"
|
|
1616
1616
|
source = { registry = "https://pypi.org/simple" }
|
|
1617
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1618
|
-
wheels = [
|
|
1619
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1620
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1621
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1622
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1623
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1624
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1625
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1626
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1627
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1628
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1629
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1630
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1631
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1632
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1633
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1634
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1635
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1617
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a6/21/5c6e05e0fd3fbb41be4fb92edbc9a04de70baf60adb61435ce0c6b8c3d55/ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4", size = 3181670 }
|
|
1618
|
+
wheels = [
|
|
1619
|
+
{ url = "https://files.pythonhosted.org/packages/65/45/8a20a9920175c9c4892b2420f80ff3cf14949cf3067118e212f9acd9c908/ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89", size = 10389268 },
|
|
1620
|
+
{ url = "https://files.pythonhosted.org/packages/1b/d3/2f8382db2cf4f9488e938602e33e36287f9d26cb283aa31f11c31297ce79/ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35", size = 10188348 },
|
|
1621
|
+
{ url = "https://files.pythonhosted.org/packages/a2/31/7d14e2a88da351200f844b7be889a0845d9e797162cf76b136d21b832a23/ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99", size = 9841448 },
|
|
1622
|
+
{ url = "https://files.pythonhosted.org/packages/db/99/738cafdc768eceeca0bd26c6f03e213aa91203d2278e1d95b1c31c4ece41/ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca", size = 10674864 },
|
|
1623
|
+
{ url = "https://files.pythonhosted.org/packages/fe/12/bcf2836b50eab53c65008383e7d55201e490d75167c474f14a16e1af47d2/ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250", size = 10192105 },
|
|
1624
|
+
{ url = "https://files.pythonhosted.org/packages/2b/71/261d5d668bf98b6c44e89bfb5dfa4cb8cb6c8b490a201a3d8030e136ea4f/ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c", size = 11194144 },
|
|
1625
|
+
{ url = "https://files.pythonhosted.org/packages/90/1f/0926d18a3b566fa6e7b3b36093088e4ffef6b6ba4ea85a462d9a93f7e35c/ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565", size = 11917066 },
|
|
1626
|
+
{ url = "https://files.pythonhosted.org/packages/cd/a8/9fac41f128b6a44ab4409c1493430b4ee4b11521e8aeeca19bfe1ce851f9/ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7", size = 11458821 },
|
|
1627
|
+
{ url = "https://files.pythonhosted.org/packages/25/cd/59644168f086ab13fe4e02943b9489a0aa710171f66b178e179df5383554/ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a", size = 12700379 },
|
|
1628
|
+
{ url = "https://files.pythonhosted.org/packages/fb/30/3bac63619eb97174661829c07fc46b2055a053dee72da29d7c304c1cd2c0/ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad", size = 11019813 },
|
|
1629
|
+
{ url = "https://files.pythonhosted.org/packages/4b/af/f567b885b5cb3bcdbcca3458ebf210cc8c9c7a9f61c332d3c2a050c3b21e/ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112", size = 10662146 },
|
|
1630
|
+
{ url = "https://files.pythonhosted.org/packages/bc/ad/eb930d3ad117a9f2f7261969c21559ebd82bb13b6e8001c7caed0d44be5f/ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378", size = 10256911 },
|
|
1631
|
+
{ url = "https://files.pythonhosted.org/packages/20/d5/af292ce70a016fcec792105ca67f768b403dd480a11888bc1f418fed0dd5/ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8", size = 10767488 },
|
|
1632
|
+
{ url = "https://files.pythonhosted.org/packages/24/85/cc04a3bd027f433bebd2a097e63b3167653c079f7f13d8f9a1178e693412/ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd", size = 11093368 },
|
|
1633
|
+
{ url = "https://files.pythonhosted.org/packages/0b/fb/c39cbf32d1f3e318674b8622f989417231794926b573f76dd4d0ca49f0f1/ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9", size = 8594180 },
|
|
1634
|
+
{ url = "https://files.pythonhosted.org/packages/5a/71/ec8cdea34ecb90c830ca60d54ac7b509a7b5eab50fae27e001d4470fe813/ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307", size = 9419751 },
|
|
1635
|
+
{ url = "https://files.pythonhosted.org/packages/79/7b/884553415e9f0a9bf358ed52fb68b934e67ef6c5a62397ace924a1afdf9a/ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37", size = 8717402 },
|
|
1636
1636
|
]
|
|
1637
1637
|
|
|
1638
1638
|
[[package]]
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import getpass
|
|
2
|
-
import pathlib
|
|
3
|
-
import pkgutil
|
|
4
|
-
from typing import Annotated
|
|
5
|
-
|
|
6
|
-
import typer
|
|
7
|
-
|
|
8
|
-
from mm_balance import output
|
|
9
|
-
from mm_balance.balances import Balances
|
|
10
|
-
from mm_balance.config import Config
|
|
11
|
-
from mm_balance.constants import NETWORKS
|
|
12
|
-
from mm_balance.price import Prices, get_prices
|
|
13
|
-
from mm_balance.token_decimals import get_token_decimals
|
|
14
|
-
|
|
15
|
-
app = typer.Typer(no_args_is_help=True, pretty_exceptions_enable=False, add_completion=False)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def example_callback(value: bool) -> None:
|
|
19
|
-
if value:
|
|
20
|
-
data = pkgutil.get_data(__name__, "config/example.yml")
|
|
21
|
-
typer.echo(data)
|
|
22
|
-
raise typer.Exit
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def networks_callback(value: bool) -> None:
|
|
26
|
-
if value:
|
|
27
|
-
for network in NETWORKS:
|
|
28
|
-
typer.echo(network)
|
|
29
|
-
raise typer.Exit
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@app.command()
|
|
33
|
-
def cli(
|
|
34
|
-
config_path: Annotated[pathlib.Path, typer.Argument()],
|
|
35
|
-
_example: Annotated[bool | None, typer.Option("--example", callback=example_callback, help="Print a config example.")] = None,
|
|
36
|
-
_networks: Annotated[
|
|
37
|
-
bool | None, typer.Option("--networks", callback=networks_callback, help="Print supported networks.")
|
|
38
|
-
] = None,
|
|
39
|
-
) -> None:
|
|
40
|
-
zip_password = "" # nosec
|
|
41
|
-
if config_path.name.endswith(".zip"):
|
|
42
|
-
zip_password = getpass.getpass("zip password")
|
|
43
|
-
config = Config.read_config(config_path, zip_password=zip_password)
|
|
44
|
-
|
|
45
|
-
if config.print_debug:
|
|
46
|
-
output.print_nodes(config)
|
|
47
|
-
|
|
48
|
-
token_decimals = get_token_decimals(config)
|
|
49
|
-
if config.print_debug:
|
|
50
|
-
output.print_token_decimals(token_decimals)
|
|
51
|
-
|
|
52
|
-
prices = get_prices(config) if config.price else Prices()
|
|
53
|
-
output.print_prices(config, prices)
|
|
54
|
-
|
|
55
|
-
balances = Balances(config, token_decimals)
|
|
56
|
-
balances.process()
|
|
57
|
-
|
|
58
|
-
output.print_groups(balances, config, prices)
|
|
59
|
-
output.print_total(config, balances, prices)
|
|
60
|
-
output.print_errors(config, balances)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if __name__ == "__main__":
|
|
64
|
-
app()
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
from decimal import Decimal
|
|
2
|
-
|
|
3
|
-
from mm_std import Err, Ok, fatal, print_table
|
|
4
|
-
from rich.progress import BarColumn, MofNCompleteColumn, Progress, TaskID, TextColumn
|
|
5
|
-
|
|
6
|
-
from mm_balance.balances import Balances
|
|
7
|
-
from mm_balance.config import Config, Group
|
|
8
|
-
from mm_balance.price import Prices
|
|
9
|
-
from mm_balance.token_decimals import TokenDecimals
|
|
10
|
-
from mm_balance.total import Total
|
|
11
|
-
from mm_balance.utils import fnumber
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def print_groups(balances: Balances, config: Config, prices: Prices) -> None:
|
|
15
|
-
for group_index, group in enumerate(config.groups):
|
|
16
|
-
group_balances = balances.get_group_balances(group_index, group.network)
|
|
17
|
-
_print_group(group, group_balances, config, prices)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _print_group(group: Group, group_balances: list[Balances.Balance], config: Config, prices: Prices) -> None:
|
|
21
|
-
rows = []
|
|
22
|
-
balance_sum = Decimal(0)
|
|
23
|
-
usd_sum = Decimal(0)
|
|
24
|
-
for address_task in group_balances:
|
|
25
|
-
if isinstance(address_task.balance, Err):
|
|
26
|
-
row = [address_task.address, address_task.balance.err]
|
|
27
|
-
elif isinstance(address_task.balance, Ok):
|
|
28
|
-
balance = address_task.balance.ok
|
|
29
|
-
balance_sum += balance
|
|
30
|
-
if config.skip_empty and balance == Decimal(0):
|
|
31
|
-
continue
|
|
32
|
-
row = [address_task.address, fnumber(balance, config.format_number_separator)]
|
|
33
|
-
if config.price:
|
|
34
|
-
balance_usd = round(balance * prices[group.ticker], config.round_ndigits)
|
|
35
|
-
usd_sum += balance_usd
|
|
36
|
-
row.append(fnumber(balance_usd, config.format_number_separator, "$"))
|
|
37
|
-
else:
|
|
38
|
-
fatal("address_task is None!")
|
|
39
|
-
rows.append(row)
|
|
40
|
-
|
|
41
|
-
balance_sum_str = fnumber(round(balance_sum, config.round_ndigits), config.format_number_separator)
|
|
42
|
-
sum_row = ["sum", balance_sum_str]
|
|
43
|
-
if config.price:
|
|
44
|
-
usd_sum_str = fnumber(round(usd_sum, config.round_ndigits), config.format_number_separator, "$")
|
|
45
|
-
sum_row.append(usd_sum_str)
|
|
46
|
-
rows.append(sum_row)
|
|
47
|
-
|
|
48
|
-
if group.share < Decimal(1):
|
|
49
|
-
sum_share_str = fnumber(round(balance_sum * group.share, config.round_ndigits), config.format_number_separator)
|
|
50
|
-
sum_share_row = [f"sum_share, {group.share}", sum_share_str]
|
|
51
|
-
if config.price:
|
|
52
|
-
usd_sum_share_str = fnumber(round(usd_sum * group.share, config.round_ndigits), config.format_number_separator, "$")
|
|
53
|
-
sum_share_row.append(usd_sum_share_str)
|
|
54
|
-
rows.append(sum_share_row)
|
|
55
|
-
|
|
56
|
-
table_headers = ["address", "balance"]
|
|
57
|
-
if config.price:
|
|
58
|
-
table_headers += ["usd"]
|
|
59
|
-
print_table(group.name, table_headers, rows)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def print_nodes(config: Config) -> None:
|
|
63
|
-
rows = []
|
|
64
|
-
for network, nodes in config.nodes.items():
|
|
65
|
-
rows.append([network, "\n".join(nodes)])
|
|
66
|
-
print_table("Nodes", ["network", "nodes"], rows)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def print_token_decimals(token_decimals: TokenDecimals) -> None:
|
|
70
|
-
rows = []
|
|
71
|
-
for network, decimals in token_decimals.items():
|
|
72
|
-
rows.append([network, decimals])
|
|
73
|
-
print_table("Token Decimals", ["network", "decimals"], rows)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def print_prices(config: Config, prices: Prices) -> None:
|
|
77
|
-
if config.price:
|
|
78
|
-
rows = []
|
|
79
|
-
for ticker, price in prices.items():
|
|
80
|
-
rows.append([ticker, fnumber(round(price, config.round_ndigits), config.format_number_separator, "$")])
|
|
81
|
-
print_table("Prices", ["coin", "usd"], rows)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def print_total(config: Config, balances: Balances, prices: Prices) -> None:
|
|
85
|
-
total = Total.calc(balances, prices, config)
|
|
86
|
-
total.print()
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def print_errors(config: Config, balances: Balances) -> None:
|
|
90
|
-
error_balances = balances.get_errors()
|
|
91
|
-
if not error_balances:
|
|
92
|
-
return
|
|
93
|
-
rows = []
|
|
94
|
-
for balance in error_balances:
|
|
95
|
-
group = config.groups[balance.group_index]
|
|
96
|
-
rows.append([group.ticker + " / " + group.network, balance.address, balance.balance.err]) # type: ignore[union-attr]
|
|
97
|
-
print_table("Errors", ["coin", "address", "error"], rows)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def create_progress_bar() -> Progress:
|
|
101
|
-
return Progress(
|
|
102
|
-
TextColumn("[progress.description]{task.description}"),
|
|
103
|
-
BarColumn(),
|
|
104
|
-
MofNCompleteColumn(),
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def create_progress_task(progress: Progress, description: str, total: int) -> TaskID:
|
|
109
|
-
return progress.add_task("[green]" + description, total=total)
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
from collections import defaultdict
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from decimal import Decimal
|
|
4
|
-
from typing import Self
|
|
5
|
-
|
|
6
|
-
from mm_std import Ok, PrintFormat, print_table
|
|
7
|
-
|
|
8
|
-
from mm_balance.balances import Balances
|
|
9
|
-
from mm_balance.config import Config
|
|
10
|
-
from mm_balance.constants import USD_STABLECOINS
|
|
11
|
-
from mm_balance.price import Prices
|
|
12
|
-
from mm_balance.utils import fnumber
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass
|
|
16
|
-
class Total:
|
|
17
|
-
coins: dict[str, Decimal]
|
|
18
|
-
coins_share: dict[str, Decimal]
|
|
19
|
-
usd_sum: Decimal # sum of all coins in USD
|
|
20
|
-
usd_sum_share: Decimal
|
|
21
|
-
|
|
22
|
-
stablecoin_sum: Decimal # sum of usd stablecoins: usdt, usdc, etc..
|
|
23
|
-
stablecoin_sum_share: Decimal
|
|
24
|
-
|
|
25
|
-
config: Config
|
|
26
|
-
prices: Prices
|
|
27
|
-
|
|
28
|
-
@classmethod
|
|
29
|
-
def calc(cls, balances: Balances, prices: Prices, config: Config) -> Self:
|
|
30
|
-
coins: dict[str, Decimal] = defaultdict(Decimal)
|
|
31
|
-
coins_share: dict[str, Decimal] = defaultdict(Decimal)
|
|
32
|
-
usd_sum = Decimal(0)
|
|
33
|
-
usd_sum_share = Decimal(0)
|
|
34
|
-
|
|
35
|
-
stablecoin_sum = Decimal(0)
|
|
36
|
-
stablecoin_sum_share = Decimal(0)
|
|
37
|
-
for group_index, group in enumerate(config.groups):
|
|
38
|
-
balance_sum = Decimal(0)
|
|
39
|
-
for address_task in balances.get_group_balances(group_index, group.network):
|
|
40
|
-
if isinstance(address_task.balance, Ok):
|
|
41
|
-
balance_sum += address_task.balance.ok
|
|
42
|
-
if group.ticker in USD_STABLECOINS:
|
|
43
|
-
stablecoin_sum += address_task.balance.ok
|
|
44
|
-
stablecoin_sum_share += address_task.balance.ok * group.share
|
|
45
|
-
if config.price:
|
|
46
|
-
balance_usd = round(address_task.balance.ok * prices[group.ticker], config.round_ndigits)
|
|
47
|
-
usd_sum += balance_usd
|
|
48
|
-
usd_sum_share += group.share * balance_usd
|
|
49
|
-
|
|
50
|
-
coins[group.ticker] += balance_sum
|
|
51
|
-
coins_share[group.ticker] += round(balance_sum * group.share, config.round_ndigits)
|
|
52
|
-
return cls(
|
|
53
|
-
coins=coins,
|
|
54
|
-
coins_share=coins_share,
|
|
55
|
-
usd_sum=usd_sum,
|
|
56
|
-
usd_sum_share=usd_sum_share,
|
|
57
|
-
stablecoin_sum=stablecoin_sum,
|
|
58
|
-
stablecoin_sum_share=stablecoin_sum_share,
|
|
59
|
-
config=config,
|
|
60
|
-
prices=prices,
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
def print(self) -> None:
|
|
64
|
-
if self.config.print_format == PrintFormat.TABLE:
|
|
65
|
-
if self.config.price:
|
|
66
|
-
self._print_total_total_with_price()
|
|
67
|
-
|
|
68
|
-
if self.config.has_share():
|
|
69
|
-
self._print_share_total_with_price()
|
|
70
|
-
else:
|
|
71
|
-
self._print_total_total_without_price()
|
|
72
|
-
|
|
73
|
-
if self.config.has_share():
|
|
74
|
-
self._print_share_total_without_price()
|
|
75
|
-
|
|
76
|
-
def _print_total_total_with_price(self) -> None:
|
|
77
|
-
if self.config.print_format == PrintFormat.TABLE:
|
|
78
|
-
rows = []
|
|
79
|
-
for key, value in self.coins.items():
|
|
80
|
-
usd_value = round(value * self.prices[key], self.config.round_ndigits)
|
|
81
|
-
if key in USD_STABLECOINS:
|
|
82
|
-
usd_share = round(self.stablecoin_sum * 100 / self.usd_sum, self.config.round_ndigits)
|
|
83
|
-
else:
|
|
84
|
-
usd_share = round(usd_value * 100 / self.usd_sum, self.config.round_ndigits)
|
|
85
|
-
|
|
86
|
-
value_str = fnumber(value, self.config.format_number_separator)
|
|
87
|
-
usd_value_str = fnumber(usd_value, self.config.format_number_separator, "$")
|
|
88
|
-
|
|
89
|
-
rows.append([key, value_str, usd_value_str, f"{usd_share}%"])
|
|
90
|
-
|
|
91
|
-
rows.append(["stablecoin_sum", fnumber(self.stablecoin_sum, self.config.format_number_separator, "$")])
|
|
92
|
-
rows.append(["usd_sum", fnumber(self.usd_sum, self.config.format_number_separator, "$")])
|
|
93
|
-
print_table("Total", ["coin", "balance", "usd", "usd_share"], rows)
|
|
94
|
-
|
|
95
|
-
def _print_total_total_without_price(self) -> None:
|
|
96
|
-
if self.config.print_format == PrintFormat.TABLE:
|
|
97
|
-
rows = []
|
|
98
|
-
for key, value in self.coins.items():
|
|
99
|
-
rows.append([key, fnumber(value, self.config.format_number_separator)])
|
|
100
|
-
print_table("Total", ["coin", "balance"], rows)
|
|
101
|
-
|
|
102
|
-
def _print_share_total_with_price(self) -> None:
|
|
103
|
-
rows = []
|
|
104
|
-
for key, _ in self.coins.items():
|
|
105
|
-
usd_value = round(self.coins_share[key] * self.prices[key], self.config.round_ndigits)
|
|
106
|
-
if key in USD_STABLECOINS:
|
|
107
|
-
usd_share = round(self.stablecoin_sum_share * 100 / self.usd_sum_share, self.config.round_ndigits)
|
|
108
|
-
else:
|
|
109
|
-
usd_share = round(usd_value * 100 / self.usd_sum_share, self.config.round_ndigits)
|
|
110
|
-
value = fnumber(self.coins_share[key], self.config.format_number_separator)
|
|
111
|
-
usd_value_str = fnumber(usd_value, self.config.format_number_separator, "$")
|
|
112
|
-
rows.append([key, value, usd_value_str, f"{usd_share}%"])
|
|
113
|
-
rows.append(["stablecoin_sum", fnumber(self.stablecoin_sum_share, self.config.format_number_separator, "$")])
|
|
114
|
-
rows.append(["usd_sum", fnumber(self.usd_sum_share, self.config.format_number_separator, "$")])
|
|
115
|
-
print_table("Total, share", ["coin", "balance", "usd", "usd_share"], rows)
|
|
116
|
-
|
|
117
|
-
def _print_share_total_without_price(self) -> None:
|
|
118
|
-
rows = []
|
|
119
|
-
for key, _ in self.coins.items():
|
|
120
|
-
value = fnumber(self.coins_share[key], self.config.format_number_separator)
|
|
121
|
-
rows.append([key, value])
|
|
122
|
-
print_table("Total, share", ["coin", "balance"], rows)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mm_balance-0.1.14/src/mm_balance/rpc → mm_balance-0.1.15/src/mm_balance/output}/__init__.py
RENAMED
|
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
|