mm-balance 0.1.14__py3-none-any.whl → 0.1.16__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 CHANGED
@@ -4,13 +4,15 @@ import pkgutil
4
4
  from typing import Annotated
5
5
 
6
6
  import typer
7
+ from mm_std import PrintFormat, fatal
7
8
 
8
- from mm_balance import output
9
- from mm_balance.balances import Balances
10
9
  from mm_balance.config import Config
11
10
  from mm_balance.constants import NETWORKS
11
+ from mm_balance.output.formats import json_format, table_format
12
12
  from mm_balance.price import Prices, get_prices
13
+ from mm_balance.result import create_balances_result
13
14
  from mm_balance.token_decimals import get_token_decimals
15
+ from mm_balance.workers import Workers
14
16
 
15
17
  app = typer.Typer(no_args_is_help=True, pretty_exceptions_enable=False, add_completion=False)
16
18
 
@@ -32,6 +34,10 @@ def networks_callback(value: bool) -> None:
32
34
  @app.command()
33
35
  def cli(
34
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,
35
41
  _example: Annotated[bool | None, typer.Option("--example", callback=example_callback, help="Print a config example.")] = None,
36
42
  _networks: Annotated[
37
43
  bool | None, typer.Option("--networks", callback=networks_callback, help="Print supported networks.")
@@ -42,22 +48,37 @@ def cli(
42
48
  zip_password = getpass.getpass("zip password")
43
49
  config = Config.read_config(config_path, zip_password=zip_password)
44
50
 
45
- if config.print_debug:
46
- output.print_nodes(config)
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
+ table_format.print_proxy_count(config)
47
63
 
48
64
  token_decimals = get_token_decimals(config)
49
- if config.print_debug:
50
- output.print_token_decimals(token_decimals)
65
+ if config.print_debug and config.print_format is PrintFormat.TABLE:
66
+ table_format.print_token_decimals(token_decimals)
51
67
 
52
68
  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)
69
+ if config.print_format is PrintFormat.TABLE:
70
+ table_format.print_prices(config, prices)
71
+
72
+ workers = Workers(config, token_decimals)
73
+ workers.process()
74
+
75
+ result = create_balances_result(config, prices, workers)
76
+ if config.print_format is PrintFormat.TABLE:
77
+ table_format.print_result(config, result, workers)
78
+ elif config.print_format is PrintFormat.JSON:
79
+ json_format.print_result(config, token_decimals, prices, workers, result)
80
+ else:
81
+ fatal("Unsupported print format")
61
82
 
62
83
 
63
84
  if __name__ == "__main__":
@@ -39,11 +39,11 @@ coins:
39
39
  addresses: binance_eth
40
40
 
41
41
  - ticker: USDC
42
- comment: okx aptos
42
+ comment: swap.thala.apt
43
43
  network: aptos
44
44
  token_address: 0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC
45
45
  token_decimals: 6
46
- addresses: "0x834d639b10d20dcb894728aa4b9b572b2ea2d97073b10eacb111f338b20ea5d7" # for a single line it's necessary to use quotes
46
+ addresses: "0x48271d39d0b05bd6efca2278f22277d6fcc375504f9839fd73f74ace240861af" # for a single line it's necessary to use quotes
47
47
 
48
48
 
49
49
  addresses:
@@ -70,4 +70,5 @@ addresses:
70
70
  #price: yes
71
71
  #skip_empty: no
72
72
  #print_debug: no
73
+ #print_format: table # table, json
73
74
  #format_number_separator: ","
mm_balance/config.py CHANGED
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import os
3
4
  from decimal import Decimal
4
5
  from pathlib import Path
5
- from typing import Self
6
+ from typing import Self, cast
6
7
 
7
8
  import pydash
8
9
  from mm_std import BaseConfig, PrintFormat, fatal, hr
@@ -97,6 +98,8 @@ class Config(BaseConfig):
97
98
  # load from proxies_url
98
99
  if self.proxies_url is not None:
99
100
  self.proxies = get_proxies(self.proxies_url)
101
+ elif os.getenv("MM_BALANCE_PROXIES_URL"):
102
+ self.proxies = get_proxies(cast(str, os.getenv("MM_BALANCE_PROXIES_URL")))
100
103
 
101
104
  # load addresses from address_group
102
105
  for group in self.groups:
File without changes
@@ -0,0 +1,28 @@
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
+ data["proxies"] = len(config.proxies)
16
+ if config.price:
17
+ data["prices"] = prices
18
+
19
+ data["groups"] = result.groups
20
+ data["total"] = result.total
21
+ if config.has_share():
22
+ data["total_share"] = result.total_share
23
+
24
+ errors = workers.get_errors()
25
+ if errors:
26
+ data["errors"] = errors
27
+
28
+ print_json(data)
@@ -0,0 +1,119 @@
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_proxy_count(config: Config) -> None:
21
+ print_table("Proxies", ["count"], [[len(config.proxies)]])
22
+
23
+
24
+ def print_token_decimals(token_decimals: TokenDecimals) -> None:
25
+ rows = []
26
+ for network, decimals in token_decimals.items():
27
+ rows.append([network, decimals])
28
+ print_table("Token Decimals", ["network", "decimals"], rows)
29
+
30
+
31
+ def print_prices(config: Config, prices: Prices) -> None:
32
+ if config.price:
33
+ rows = []
34
+ for ticker, price in prices.items():
35
+ rows.append([ticker, format_number(round(price, config.round_ndigits), config.format_number_separator, "$")])
36
+ print_table("Prices", ["coin", "usd"], rows)
37
+
38
+
39
+ def print_result(config: Config, result: BalancesResult, workers: Workers) -> None:
40
+ for group in result.groups:
41
+ _print_group(config, group)
42
+
43
+ _print_total(config, result.total, False)
44
+ if config.has_share():
45
+ _print_total(config, result.total_share, True)
46
+
47
+ _print_errors(config, workers)
48
+
49
+
50
+ def _print_errors(config: Config, workers: Workers) -> None:
51
+ error_tasks = workers.get_errors()
52
+ if not error_tasks:
53
+ return
54
+ rows = []
55
+ for task in error_tasks:
56
+ group = config.groups[task.group_index]
57
+ rows.append([group.ticker + " / " + group.network, task.wallet_address, task.balance.err]) # type: ignore[union-attr]
58
+ print_table("Errors", ["coin", "address", "error"], rows)
59
+
60
+
61
+ def _print_total(config: Config, total: Total, is_share_total: bool) -> None:
62
+ table_name = "Total, share" if is_share_total else "Total"
63
+ headers = ["coin", "balance"]
64
+
65
+ rows = []
66
+ for ticker, balance in total.coin_balances.items():
67
+ balance_str = format_number(balance, config.format_number_separator)
68
+ row = [ticker, balance_str]
69
+ if config.price:
70
+ usd_value_str = format_number(total.coin_usd_values[ticker], config.format_number_separator, "$")
71
+ portfolio_share = total.portfolio_share[ticker]
72
+ row += [usd_value_str, f"{portfolio_share}%"]
73
+ rows.append(row)
74
+
75
+ if config.price:
76
+ headers += ["usd", "portfolio_share"]
77
+ 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
+
81
+ print_table(table_name, headers, rows)
82
+
83
+
84
+ def _print_group(config: Config, group: GroupResult) -> None:
85
+ group_name = group.ticker
86
+ if group.comment:
87
+ group_name += " / " + group.comment
88
+ group_name += " / " + group.network
89
+
90
+ rows = []
91
+ for address in group.addresses:
92
+ if isinstance(address.balance, str):
93
+ rows.append([address.address, address.balance])
94
+ else:
95
+ if config.skip_empty and address.balance.balance == Decimal(0):
96
+ continue
97
+ balance_str = format_number(address.balance.balance, config.format_number_separator)
98
+ row = [address.address, balance_str]
99
+ if config.price:
100
+ usd_value_str = format_number(address.balance.usd_value, config.format_number_separator, "$")
101
+ row.append(usd_value_str)
102
+ rows.append(row)
103
+
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, "$"))
107
+ rows.append(sum_row)
108
+
109
+ if group.share < Decimal(1):
110
+ sum_share_str = format_number(group.balance_sum_share, config.format_number_separator)
111
+ 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
+ rows.append(sum_share_row)
115
+
116
+ table_headers = ["address", "balance"]
117
+ if config.price:
118
+ table_headers += ["usd"]
119
+ 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)
mm_balance/result.py ADDED
@@ -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 __future__ import annotations
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
- class Balances:
17
- class Balance(BaseModel):
18
- group_index: int
19
- address: str
20
- token_address: str | None
21
- balance: Result[Decimal] | None = None
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[Balances.Balance]] = {network: [] for network in config.networks()}
27
- self.progress_bar = output.create_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 = [Balances.Balance(group_index=idx, address=a, token_address=group.token_address) for a in group.addresses]
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] = output.create_progress_task(
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.address, task.token_address))
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mm-balance
3
- Version: 0.1.14
3
+ Version: 0.1.16
4
4
  Requires-Python: >=3.12
5
5
  Requires-Dist: mm-aptos==0.1.2
6
6
  Requires-Dist: mm-btc==0.1.0
@@ -0,0 +1,23 @@
1
+ mm_balance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ mm_balance/cli.py,sha256=LbOyGiF3IFCl2oBJzAzR-CE7QZWiR-Q-p9wIgfqv_r8,3111
3
+ mm_balance/config.py,sha256=LsmfluUFpTfXMvXUUzGQtiZlz1gliiDrDx4c4SqgPws,5383
4
+ mm_balance/constants.py,sha256=4C2BxYRY_i83Rba5NOXeLLeA0ZTWR9WD6BeyQwiCj_U,2335
5
+ mm_balance/price.py,sha256=f6fYhpPVqZE9hH22ypfYb9m-nfE41LeVIvksEvqmz54,1615
6
+ mm_balance/result.py,sha256=fG63HrHXWS3kBLHmefxKsRh9tj53dloamO_nv3xFboM,4576
7
+ mm_balance/token_decimals.py,sha256=WAz9wbvwjPJEcQnrLyPAf23eGp89t9oP_xBmaEecEG0,2348
8
+ mm_balance/utils.py,sha256=YjQMfyAaLiiprjt9CuNbDQSJrLbRoTkb2rurAb6bHDc,288
9
+ mm_balance/workers.py,sha256=PGhLVhYYVm2jIAzIiJQYby_UqW0xsRv-wQJURqOUYxg,3767
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=Vp-6ev-9YIa3kGZxKzMdoJZQB_KLUaAdQGvgOxmZtrc,718
13
+ mm_balance/output/formats/json_format.py,sha256=yMkZTA6vOUpLSixF7H4H4lVZKShRs4B9qmUR8L-CRvw,866
14
+ mm_balance/output/formats/table_format.py,sha256=qN1uMF1cyAVOH31Gohoc_a4l9-pWTSZ8WRxBBSAwj54,4539
15
+ mm_balance/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ mm_balance/rpc/aptos.py,sha256=6hKGKTs2DPQMa4RmPXzfvjCFjPjN8wjJhRRI6HdI9GM,628
17
+ mm_balance/rpc/btc.py,sha256=ugp90H7YW0kiXIh98bQWk9mQTW20yE-jBiCpRvfoH-U,481
18
+ mm_balance/rpc/evm.py,sha256=ewlMmRrcXKlky3DPNbnUBTVwnvyw7N9iCZLsCX2V14w,1007
19
+ mm_balance/rpc/solana.py,sha256=Lwl5Otqy48g4LWfz9uN7_rkICKUIYJvf88PbIAnfMdc,1051
20
+ mm_balance-0.1.16.dist-info/METADATA,sha256=MuArrbQbWKMo5Xx9Auu7YzciGl1oKfjCT1PWnBgyhP0,229
21
+ mm_balance-0.1.16.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
22
+ mm_balance-0.1.16.dist-info/entry_points.txt,sha256=rSnP0ZW1a3ACNwTWM7T53CmOycKbzhG43m2_wseENng,50
23
+ mm_balance-0.1.16.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.25.0
2
+ Generator: hatchling 1.26.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
mm_balance/output.py DELETED
@@ -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)
mm_balance/total.py DELETED
@@ -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)
@@ -1,20 +0,0 @@
1
- mm_balance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- mm_balance/balances.py,sha256=HLIMdhayd59SIjzaYPwJek4PvwGnpTESvUt8SoF1Ktc,3812
3
- mm_balance/cli.py,sha256=AIIxj7msKD3d5vr6mTT5x-D3-AD7JyU4sxoNCI3Uozk,1887
4
- mm_balance/config.py,sha256=5qOsU1JjWu2TbH1VgikK4oZpBcuPMqsOe8DOwMRtrWM,5230
5
- mm_balance/constants.py,sha256=4C2BxYRY_i83Rba5NOXeLLeA0ZTWR9WD6BeyQwiCj_U,2335
6
- mm_balance/output.py,sha256=ZkYh0gU32OAJIoXz2ExG44ong9mzAgohU9SG3rn3yqg,4281
7
- mm_balance/price.py,sha256=f6fYhpPVqZE9hH22ypfYb9m-nfE41LeVIvksEvqmz54,1615
8
- mm_balance/token_decimals.py,sha256=WAz9wbvwjPJEcQnrLyPAf23eGp89t9oP_xBmaEecEG0,2348
9
- mm_balance/total.py,sha256=FrORCE3_gW9tMh8UCgqLcHK15wQuwOwX3Fp6QhaRrgY,5504
10
- mm_balance/utils.py,sha256=YjQMfyAaLiiprjt9CuNbDQSJrLbRoTkb2rurAb6bHDc,288
11
- mm_balance/config/example.yml,sha256=EMWgogMkdxDUkBKDcDStbsCR-fnUD6SFYgCdfl7S2yg,1859
12
- mm_balance/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- mm_balance/rpc/aptos.py,sha256=6hKGKTs2DPQMa4RmPXzfvjCFjPjN8wjJhRRI6HdI9GM,628
14
- mm_balance/rpc/btc.py,sha256=ugp90H7YW0kiXIh98bQWk9mQTW20yE-jBiCpRvfoH-U,481
15
- mm_balance/rpc/evm.py,sha256=ewlMmRrcXKlky3DPNbnUBTVwnvyw7N9iCZLsCX2V14w,1007
16
- mm_balance/rpc/solana.py,sha256=Lwl5Otqy48g4LWfz9uN7_rkICKUIYJvf88PbIAnfMdc,1051
17
- mm_balance-0.1.14.dist-info/METADATA,sha256=kZgluU7iJnTzbuLyh-NDAgKOuy0H-5QCzguVia3jQFM,229
18
- mm_balance-0.1.14.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
19
- mm_balance-0.1.14.dist-info/entry_points.txt,sha256=rSnP0ZW1a3ACNwTWM7T53CmOycKbzhG43m2_wseENng,50
20
- mm_balance-0.1.14.dist-info/RECORD,,