mm-balance 0.2.3__py3-none-any.whl → 0.3.0__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 +16 -2
- mm_balance/config/example.toml +2 -2
- mm_balance/config.py +6 -6
- mm_balance/diff.py +168 -0
- mm_balance/output/formats/table_format.py +1 -1
- mm_balance/output/utils.py +3 -1
- mm_balance/result.py +15 -1
- mm_balance/rpc/btc.py +2 -1
- mm_balance/rpc/evm.py +2 -1
- mm_balance/rpc/solana.py +2 -2
- mm_balance/token_decimals.py +9 -9
- mm_balance/utils.py +12 -0
- mm_balance/workers.py +1 -1
- {mm_balance-0.2.3.dist-info → mm_balance-0.3.0.dist-info}/METADATA +4 -3
- mm_balance-0.3.0.dist-info/RECORD +25 -0
- mm_balance-0.2.3.dist-info/RECORD +0 -24
- {mm_balance-0.2.3.dist-info → mm_balance-0.3.0.dist-info}/WHEEL +0 -0
- {mm_balance-0.2.3.dist-info → mm_balance-0.3.0.dist-info}/entry_points.txt +0 -0
mm_balance/cli.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import getpass
|
|
2
2
|
import importlib.metadata
|
|
3
|
-
import pathlib
|
|
4
3
|
import pkgutil
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
from typing import Annotated
|
|
6
6
|
|
|
7
7
|
import typer
|
|
@@ -9,6 +9,7 @@ from mm_std import PrintFormat, fatal, pretty_print_toml
|
|
|
9
9
|
|
|
10
10
|
from mm_balance.config import Config
|
|
11
11
|
from mm_balance.constants import NETWORKS
|
|
12
|
+
from mm_balance.diff import BalancesDict, Diff
|
|
12
13
|
from mm_balance.output.formats import json_format, table_format
|
|
13
14
|
from mm_balance.price import Prices, get_prices
|
|
14
15
|
from mm_balance.result import create_balances_result
|
|
@@ -42,12 +43,16 @@ def networks_callback(value: bool) -> None:
|
|
|
42
43
|
|
|
43
44
|
@app.command()
|
|
44
45
|
def cli(
|
|
45
|
-
config_path: Annotated[
|
|
46
|
+
config_path: Annotated[Path, typer.Argument()],
|
|
46
47
|
print_format: Annotated[PrintFormat | None, typer.Option("--format", "-f", help="Print format.")] = None,
|
|
47
48
|
skip_empty: Annotated[bool | None, typer.Option("--skip-empty", "-s", help="Skip empty balances.")] = None,
|
|
48
49
|
debug: Annotated[bool | None, typer.Option("--debug", "-d", help="Print debug info.")] = None,
|
|
49
50
|
print_config: Annotated[bool | None, typer.Option("--config", "-c", help="Print config and exit.")] = None,
|
|
50
51
|
price: Annotated[bool | None, typer.Option("--price/--no-price", help="Print prices.")] = None,
|
|
52
|
+
save_balances_file: Annotated[Path | None, typer.Option("--save-balances-file", help="Save balances file.")] = None,
|
|
53
|
+
diff_from_balances_file: Annotated[
|
|
54
|
+
Path | None, typer.Option("--diff-from-balances-file", help="Diff from balances file.")
|
|
55
|
+
] = None,
|
|
51
56
|
_example: Annotated[bool | None, typer.Option("--example", callback=example_callback, help="Print a config example.")] = None,
|
|
52
57
|
_networks: Annotated[
|
|
53
58
|
bool | None, typer.Option("--networks", callback=networks_callback, help="Print supported networks.")
|
|
@@ -93,6 +98,15 @@ def cli(
|
|
|
93
98
|
else:
|
|
94
99
|
fatal("Unsupported print format")
|
|
95
100
|
|
|
101
|
+
if save_balances_file:
|
|
102
|
+
BalancesDict.from_balances_result(result).save_to_path(save_balances_file)
|
|
103
|
+
|
|
104
|
+
if diff_from_balances_file:
|
|
105
|
+
old_balances = BalancesDict.from_file(diff_from_balances_file)
|
|
106
|
+
new_balances = BalancesDict.from_balances_result(result)
|
|
107
|
+
diff = Diff.calc(old_balances, new_balances)
|
|
108
|
+
diff.print(config.settings.print_format)
|
|
109
|
+
|
|
96
110
|
|
|
97
111
|
if __name__ == "__main__":
|
|
98
112
|
app()
|
mm_balance/config/example.toml
CHANGED
|
@@ -47,8 +47,8 @@ addresses = "group: binance_eth"
|
|
|
47
47
|
ticker = "USDC"
|
|
48
48
|
network = "aptos"
|
|
49
49
|
comment = "swap.thala.apt"
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
token = "0xf22bede237a07e121b56d91a491eb7bcdfd1f5907926a9e58338f964a01b17fa::asset::USDC"
|
|
51
|
+
decimals = 6
|
|
52
52
|
addresses = "0x48271d39d0b05bd6efca2278f22277d6fcc375504f9839fd73f74ace240861af"
|
|
53
53
|
|
|
54
54
|
|
mm_balance/config.py
CHANGED
|
@@ -20,8 +20,8 @@ class Group(BaseConfig):
|
|
|
20
20
|
comment: str = ""
|
|
21
21
|
ticker: Annotated[str, StringConstraints(to_upper=True)]
|
|
22
22
|
network: Network
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
token: str | None = None # Token address. If None, it's a native token, for example ETH
|
|
24
|
+
decimals: int | None = None
|
|
25
25
|
coingecko_id: str | None = None
|
|
26
26
|
addresses: Annotated[list[str], BeforeValidator(Validators.addresses(unique=True))]
|
|
27
27
|
share: Decimal = Decimal(1)
|
|
@@ -36,10 +36,10 @@ class Group(BaseConfig):
|
|
|
36
36
|
|
|
37
37
|
@model_validator(mode="after")
|
|
38
38
|
def final_validator(self) -> Self:
|
|
39
|
-
if self.
|
|
40
|
-
self.
|
|
41
|
-
if self.
|
|
42
|
-
self.
|
|
39
|
+
if self.token is None:
|
|
40
|
+
self.token = detect_token_address(self.ticker, self.network)
|
|
41
|
+
if self.token is not None and self.network.is_evm_network():
|
|
42
|
+
self.token = self.token.lower()
|
|
43
43
|
return self
|
|
44
44
|
|
|
45
45
|
def process_addresses(self, address_groups: list[AddressGroup]) -> None:
|
mm_balance/diff.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from deepdiff.diff import DeepDiff
|
|
9
|
+
from mm_std import PrintFormat, print_json, print_plain, print_table
|
|
10
|
+
from pydantic import BaseModel, RootModel
|
|
11
|
+
|
|
12
|
+
from mm_balance.result import Balance, BalancesResult
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BalancesDict(RootModel[dict[str, dict[str, dict[str, Decimal]]]]): # network->ticker->address->balance
|
|
16
|
+
def networks(self) -> set[str]:
|
|
17
|
+
return set(self.model_dump().keys())
|
|
18
|
+
|
|
19
|
+
def tickers(self, network: str) -> set[str]:
|
|
20
|
+
return set(self.model_dump()[network].keys())
|
|
21
|
+
|
|
22
|
+
def save_to_path(self, balances_file: Path) -> None:
|
|
23
|
+
json.dump(self.model_dump(), balances_file.open("w"), default=str, indent=2)
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def from_balances_result(result: BalancesResult) -> BalancesDict:
|
|
27
|
+
data: dict[str, dict[str, dict[str, Decimal]]] = {} # network->ticker->address->balance
|
|
28
|
+
for group in result.groups:
|
|
29
|
+
if group.network not in data:
|
|
30
|
+
data[group.network] = {}
|
|
31
|
+
if group.ticker not in data[group.network]:
|
|
32
|
+
data[group.network][group.ticker] = {}
|
|
33
|
+
for address in group.addresses:
|
|
34
|
+
if isinstance(address.balance, Balance):
|
|
35
|
+
data[group.network][group.ticker][address.address] = address.balance.balance
|
|
36
|
+
return BalancesDict(data)
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def from_file(path: Path) -> BalancesDict:
|
|
40
|
+
return BalancesDict(json.load(path.open("r")))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Diff(BaseModel):
|
|
44
|
+
network_added: list[str]
|
|
45
|
+
network_removed: list[str]
|
|
46
|
+
|
|
47
|
+
ticker_added: dict[str, list[str]] # network -> tickers
|
|
48
|
+
ticker_removed: dict[str, list[str]] # network -> tickers
|
|
49
|
+
|
|
50
|
+
address_added: dict[str, dict[str, list[str]]] # network -> ticker -> addresses
|
|
51
|
+
address_removed: dict[str, dict[str, list[str]]] # network -> ticker -> addresses
|
|
52
|
+
|
|
53
|
+
balance_changed: dict[str, dict[str, dict[str, tuple[Decimal, Decimal]]]] # network->ticker->address->(old_value,new_value)
|
|
54
|
+
|
|
55
|
+
def print(self, print_format: PrintFormat) -> None:
|
|
56
|
+
match print_format:
|
|
57
|
+
case PrintFormat.TABLE:
|
|
58
|
+
self._print_table()
|
|
59
|
+
case PrintFormat.JSON:
|
|
60
|
+
self._print_json()
|
|
61
|
+
case _:
|
|
62
|
+
raise ValueError(f"Unsupported print format: {print_format}")
|
|
63
|
+
|
|
64
|
+
def _print_table(self) -> None:
|
|
65
|
+
if (
|
|
66
|
+
not self.network_added
|
|
67
|
+
and not self.network_removed
|
|
68
|
+
and not self.ticker_added
|
|
69
|
+
and not self.ticker_removed
|
|
70
|
+
and not self.address_added
|
|
71
|
+
and not self.address_removed
|
|
72
|
+
and not self.balance_changed
|
|
73
|
+
):
|
|
74
|
+
print_plain("Diff: no changes")
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
print_plain("\nDiff")
|
|
78
|
+
|
|
79
|
+
if self.network_added:
|
|
80
|
+
print_plain(f"networks added: {self.network_added}")
|
|
81
|
+
if self.network_removed:
|
|
82
|
+
print_plain(f"networks removed: {self.network_removed}")
|
|
83
|
+
if self.ticker_added:
|
|
84
|
+
print_plain(f"tickers added: {self.ticker_added}")
|
|
85
|
+
if self.ticker_removed:
|
|
86
|
+
print_plain(f"tickers removed: {self.ticker_removed}")
|
|
87
|
+
if self.address_added:
|
|
88
|
+
print_plain(f"addresses added: {self.address_added}")
|
|
89
|
+
if self.address_removed:
|
|
90
|
+
print_plain(f"addresses removed: {self.address_removed}")
|
|
91
|
+
|
|
92
|
+
if self.balance_changed:
|
|
93
|
+
rows = []
|
|
94
|
+
for network in self.balance_changed:
|
|
95
|
+
for ticker in self.balance_changed[network]:
|
|
96
|
+
for address in self.balance_changed[network][ticker]:
|
|
97
|
+
old_value, new_value = self.balance_changed[network][ticker][address]
|
|
98
|
+
rows.append([network, ticker, address, old_value, new_value, new_value - old_value])
|
|
99
|
+
print_table("", ["Network", "Ticker", "Address", "Old", "New", "Change"], rows)
|
|
100
|
+
|
|
101
|
+
def _print_json(self) -> None:
|
|
102
|
+
print_json(data=self.model_dump(), default_serializer=str)
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def calc(balances1: BalancesDict, balances2: BalancesDict) -> Diff:
|
|
106
|
+
dd = DeepDiff(balances1.model_dump(), balances2.model_dump(), ignore_order=True)
|
|
107
|
+
# Initialize empty collections for Diff fields.
|
|
108
|
+
|
|
109
|
+
network_added = []
|
|
110
|
+
network_removed = []
|
|
111
|
+
ticker_added: dict[str, list[str]] = {}
|
|
112
|
+
ticker_removed: dict[str, list[str]] = {}
|
|
113
|
+
address_added: dict[str, dict[str, list[str]]] = {}
|
|
114
|
+
address_removed: dict[str, dict[str, list[str]]] = {}
|
|
115
|
+
balance_changed: dict[str, dict[str, dict[str, tuple[Decimal, Decimal]]]] = {}
|
|
116
|
+
|
|
117
|
+
# Helper to extract keys from DeepDiff paths.
|
|
118
|
+
def extract_keys(path: str) -> list[str]:
|
|
119
|
+
# DeepDiff paths look like "root['network']['ticker']['address']"
|
|
120
|
+
return re.findall(r"\['([^']+)'\]", path)
|
|
121
|
+
|
|
122
|
+
# Process dictionary_item_added
|
|
123
|
+
for path in dd.get("dictionary_item_added", []):
|
|
124
|
+
keys = extract_keys(path)
|
|
125
|
+
if len(keys) == 1:
|
|
126
|
+
# New network added.
|
|
127
|
+
network_added.append(keys[0])
|
|
128
|
+
elif len(keys) == 2:
|
|
129
|
+
# New ticker added under an existing network.
|
|
130
|
+
network, ticker = keys
|
|
131
|
+
ticker_added.setdefault(network, []).append(ticker)
|
|
132
|
+
elif len(keys) == 3:
|
|
133
|
+
# New address added under an existing network and ticker.
|
|
134
|
+
network, ticker, address = keys
|
|
135
|
+
address_added.setdefault(network, {}).setdefault(ticker, []).append(address)
|
|
136
|
+
|
|
137
|
+
# Process dictionary_item_removed
|
|
138
|
+
for path in dd.get("dictionary_item_removed", []):
|
|
139
|
+
keys = extract_keys(path)
|
|
140
|
+
if len(keys) == 1:
|
|
141
|
+
network_removed.append(keys[0])
|
|
142
|
+
elif len(keys) == 2:
|
|
143
|
+
network, ticker = keys
|
|
144
|
+
ticker_removed.setdefault(network, []).append(ticker)
|
|
145
|
+
elif len(keys) == 3:
|
|
146
|
+
network, ticker, address = keys
|
|
147
|
+
address_removed.setdefault(network, {}).setdefault(ticker, []).append(address)
|
|
148
|
+
|
|
149
|
+
# Process values_changed for balance differences.
|
|
150
|
+
for path, change in dd.get("values_changed", {}).items():
|
|
151
|
+
keys = extract_keys(path)
|
|
152
|
+
if len(keys) != 3:
|
|
153
|
+
continue
|
|
154
|
+
network, ticker, address = keys
|
|
155
|
+
balance_changed.setdefault(network, {}).setdefault(ticker, {})[address] = (
|
|
156
|
+
Decimal(change["old_value"]),
|
|
157
|
+
Decimal(change["new_value"]),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
return Diff(
|
|
161
|
+
network_added=sorted(network_added),
|
|
162
|
+
network_removed=sorted(network_removed),
|
|
163
|
+
ticker_added={k: sorted(v) for k, v in ticker_added.items()},
|
|
164
|
+
ticker_removed={k: sorted(v) for k, v in ticker_removed.items()},
|
|
165
|
+
address_added={k: {tk: sorted(vv) for tk, vv in v.items()} for k, v in address_added.items()},
|
|
166
|
+
address_removed={k: {tk: sorted(vv) for tk, vv in v.items()} for k, v in address_removed.items()},
|
|
167
|
+
balance_changed=balance_changed,
|
|
168
|
+
)
|
|
@@ -33,7 +33,7 @@ def print_prices(config: Config, prices: Prices) -> None:
|
|
|
33
33
|
rows = []
|
|
34
34
|
for ticker, price in prices.items():
|
|
35
35
|
rows.append(
|
|
36
|
-
[ticker, format_number(
|
|
36
|
+
[ticker, format_number(price, config.settings.format_number_separator, "$", config.settings.round_ndigits)]
|
|
37
37
|
)
|
|
38
38
|
print_table("Prices", ["coin", "usd"], rows)
|
|
39
39
|
|
mm_balance/output/utils.py
CHANGED
|
@@ -3,7 +3,9 @@ from decimal import Decimal
|
|
|
3
3
|
from rich.progress import BarColumn, MofNCompleteColumn, Progress, TaskID, TextColumn
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def format_number(value: Decimal, separator: str, extra: str | None = None) -> str:
|
|
6
|
+
def format_number(value: Decimal, separator: str, extra: str | None = None, round_ndigits: int | None = None) -> str:
|
|
7
|
+
if round_ndigits is not None and value > 0:
|
|
8
|
+
value = round(value, round_ndigits)
|
|
7
9
|
str_value = f"{value:,}".replace(",", separator)
|
|
8
10
|
if extra == "$":
|
|
9
11
|
return "$" + str_value
|
mm_balance/result.py
CHANGED
|
@@ -7,6 +7,7 @@ from mm_std import Ok
|
|
|
7
7
|
from mm_balance.config import Config, Group
|
|
8
8
|
from mm_balance.constants import USD_STABLECOINS, Network
|
|
9
9
|
from mm_balance.price import Prices
|
|
10
|
+
from mm_balance.utils import round_decimal
|
|
10
11
|
from mm_balance.workers import Task, Workers
|
|
11
12
|
|
|
12
13
|
|
|
@@ -62,6 +63,19 @@ def create_balances_result(config: Config, prices: Prices, workers: Workers) ->
|
|
|
62
63
|
return BalancesResult(groups=groups, total=total, total_share=total_share)
|
|
63
64
|
|
|
64
65
|
|
|
66
|
+
# def save_balances_file(result: BalancesResult, balances_file: Path) -> None:
|
|
67
|
+
# data = {}
|
|
68
|
+
# for group in result.groups:
|
|
69
|
+
# if group.network not in data:
|
|
70
|
+
# data[group.network] = {}
|
|
71
|
+
# if group.ticker not in data[group.network]:
|
|
72
|
+
# data[group.network][group.ticker] = {}
|
|
73
|
+
# for address in group.addresses:
|
|
74
|
+
# if isinstance(address.balance, Balance):
|
|
75
|
+
# data[group.network][group.ticker][address.address] = float(address.balance.balance)
|
|
76
|
+
# json.dump(data, balances_file.open("w"), indent=2)
|
|
77
|
+
|
|
78
|
+
|
|
65
79
|
def _create_total(use_share: bool, groups: list[GroupResult]) -> Total:
|
|
66
80
|
coin_balances: dict[str, Decimal] = defaultdict(Decimal) # ticker -> balance
|
|
67
81
|
coin_usd_values: dict[str, Decimal] = defaultdict(Decimal) # ticker -> usd value
|
|
@@ -106,7 +120,7 @@ def _create_group_result(config: Config, group: Group, tasks: list[Task], prices
|
|
|
106
120
|
coin_value = task.balance.ok
|
|
107
121
|
usd_value = Decimal(0)
|
|
108
122
|
if group.ticker in prices:
|
|
109
|
-
usd_value =
|
|
123
|
+
usd_value = round_decimal(coin_value * prices[group.ticker], config.settings.round_ndigits)
|
|
110
124
|
balance = Balance(balance=coin_value, usd_value=usd_value)
|
|
111
125
|
balance_sum += balance.balance
|
|
112
126
|
usd_sum += balance.usd_value
|
mm_balance/rpc/btc.py
CHANGED
|
@@ -4,11 +4,12 @@ from mm_btc.blockstream import BlockstreamClient
|
|
|
4
4
|
from mm_std import Ok, Result
|
|
5
5
|
|
|
6
6
|
from mm_balance.constants import RETRIES_BALANCE
|
|
7
|
+
from mm_balance.utils import scale_and_round
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def get_balance(address: str, proxies: list[str], round_ndigits: int) -> Result[Decimal]:
|
|
10
11
|
return (
|
|
11
12
|
BlockstreamClient(proxies=proxies, attempts=RETRIES_BALANCE)
|
|
12
13
|
.get_confirmed_balance(address)
|
|
13
|
-
.and_then(lambda b: Ok(
|
|
14
|
+
.and_then(lambda b: Ok(scale_and_round(b, 8, round_ndigits)))
|
|
14
15
|
)
|
mm_balance/rpc/evm.py
CHANGED
|
@@ -4,6 +4,7 @@ from mm_eth import erc20, rpc
|
|
|
4
4
|
from mm_std import Ok, Result
|
|
5
5
|
|
|
6
6
|
from mm_balance.constants import RETRIES_BALANCE, RETRIES_DECIMALS, TIMEOUT_BALANCE, TIMEOUT_DECIMALS
|
|
7
|
+
from mm_balance.utils import scale_and_round
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def get_balance(
|
|
@@ -20,7 +21,7 @@ def get_balance(
|
|
|
20
21
|
)
|
|
21
22
|
else:
|
|
22
23
|
res = rpc.eth_get_balance(nodes, wallet, proxies=proxies, attempts=RETRIES_BALANCE, timeout=TIMEOUT_BALANCE)
|
|
23
|
-
return res.and_then(lambda b: Ok(
|
|
24
|
+
return res.and_then(lambda b: Ok(scale_and_round(b, decimals, round_ndigits)))
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def get_token_decimals(nodes: list[str], token_address: str, proxies: list[str]) -> Result[int]:
|
mm_balance/rpc/solana.py
CHANGED
|
@@ -4,6 +4,7 @@ from mm_sol import balance, token
|
|
|
4
4
|
from mm_std import Ok, Result
|
|
5
5
|
|
|
6
6
|
from mm_balance.constants import RETRIES_BALANCE, RETRIES_DECIMALS, TIMEOUT_BALANCE, TIMEOUT_DECIMALS
|
|
7
|
+
from mm_balance.utils import scale_and_round
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def get_balance(
|
|
@@ -17,8 +18,7 @@ def get_balance(
|
|
|
17
18
|
res = balance.get_token_balance_with_retries(
|
|
18
19
|
nodes, wallet, token, retries=RETRIES_BALANCE, timeout=TIMEOUT_BALANCE, proxies=proxies
|
|
19
20
|
)
|
|
20
|
-
|
|
21
|
-
return res.and_then(lambda b: Ok(round(Decimal(b / 10**decimals), round_ndigits)))
|
|
21
|
+
return res.and_then(lambda b: Ok(scale_and_round(b, decimals, round_ndigits)))
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def get_token_decimals(nodes: list[str], token_address: str, proxies: list[str]) -> Result[int]:
|
mm_balance/token_decimals.py
CHANGED
|
@@ -18,12 +18,12 @@ def get_token_decimals(config: Config) -> TokenDecimals:
|
|
|
18
18
|
|
|
19
19
|
for group in config.groups:
|
|
20
20
|
# token_decimals is already known
|
|
21
|
-
if group.
|
|
22
|
-
result[group.network][group.
|
|
21
|
+
if group.decimals is not None:
|
|
22
|
+
result[group.network][group.token] = group.decimals
|
|
23
23
|
continue
|
|
24
24
|
|
|
25
25
|
# get token_decimals for known native tokens
|
|
26
|
-
if group.
|
|
26
|
+
if group.token is None:
|
|
27
27
|
if group.network.is_evm_network():
|
|
28
28
|
result[group.network][None] = 18
|
|
29
29
|
elif group.network == NETWORK_SOLANA:
|
|
@@ -36,18 +36,18 @@ def get_token_decimals(config: Config) -> TokenDecimals:
|
|
|
36
36
|
|
|
37
37
|
# get token_decimals via RPC
|
|
38
38
|
# TODO: group.token_address must be in normalized form, otherwise it can be different for the same token
|
|
39
|
-
if group.
|
|
39
|
+
if group.token in result[group.network]:
|
|
40
40
|
continue # don't request for a token_decimals twice
|
|
41
41
|
|
|
42
42
|
nodes = config.nodes[group.network]
|
|
43
43
|
if group.network.is_evm_network():
|
|
44
|
-
res = evm.get_token_decimals(nodes, group.
|
|
44
|
+
res = evm.get_token_decimals(nodes, group.token, proxies)
|
|
45
45
|
elif group.network == NETWORK_SOLANA:
|
|
46
|
-
res = solana.get_token_decimals(nodes, group.
|
|
46
|
+
res = solana.get_token_decimals(nodes, group.token, proxies)
|
|
47
47
|
else:
|
|
48
|
-
fatal(f"unsupported network: {group.network}. Cant get token decimals for {group.
|
|
48
|
+
fatal(f"unsupported network: {group.network}. Cant get token decimals for {group.token}")
|
|
49
49
|
if isinstance(res, Err):
|
|
50
|
-
fatal(f"can't get decimals for token {group.ticker} / {group.
|
|
51
|
-
result[group.network][group.
|
|
50
|
+
fatal(f"can't get decimals for token {group.ticker} / {group.token}, error={res.err}")
|
|
51
|
+
result[group.network][group.token] = res.ok
|
|
52
52
|
|
|
53
53
|
return result
|
mm_balance/utils.py
CHANGED
|
@@ -8,3 +8,15 @@ def fnumber(value: Decimal, separator: str, extra: str | None = None) -> str:
|
|
|
8
8
|
if extra == "%":
|
|
9
9
|
return str_value + "%"
|
|
10
10
|
return str_value
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def scale_and_round(value: int, decimals: int, round_ndigits: int) -> Decimal:
|
|
14
|
+
if value == 0:
|
|
15
|
+
return Decimal(0)
|
|
16
|
+
return round(Decimal(value / 10**decimals), round_ndigits)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def round_decimal(value: Decimal, round_ndigits: int) -> Decimal:
|
|
20
|
+
if value == Decimal(0):
|
|
21
|
+
return Decimal(0)
|
|
22
|
+
return round(value, round_ndigits)
|
mm_balance/workers.py
CHANGED
|
@@ -28,7 +28,7 @@ class Workers:
|
|
|
28
28
|
self.progress_bar_task: dict[Network, TaskID] = {}
|
|
29
29
|
|
|
30
30
|
for idx, group in enumerate(config.groups):
|
|
31
|
-
task_list = [Task(group_index=idx, wallet_address=a, token_address=group.
|
|
31
|
+
task_list = [Task(group_index=idx, wallet_address=a, token_address=group.token) for a in group.addresses]
|
|
32
32
|
self.tasks[group.network].extend(task_list)
|
|
33
33
|
|
|
34
34
|
for network in config.networks():
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mm-balance
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Requires-Python: >=3.12
|
|
5
|
+
Requires-Dist: deepdiff==8.2.0
|
|
5
6
|
Requires-Dist: mm-aptos==0.2.0
|
|
6
7
|
Requires-Dist: mm-btc==0.3.0
|
|
7
|
-
Requires-Dist: mm-eth==0.5.
|
|
8
|
+
Requires-Dist: mm-eth==0.5.3
|
|
8
9
|
Requires-Dist: mm-sol==0.5.1
|
|
9
|
-
Requires-Dist: typer
|
|
10
|
+
Requires-Dist: typer==0.15.1
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
mm_balance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
mm_balance/cli.py,sha256=nRDVejN-dnr1iRezBxNk4z0oLa59tiwzIVhgeOGzvbo,4498
|
|
3
|
+
mm_balance/config.py,sha256=gywHNWEQL1xeG778cuca5aBoXElLIfoGle3xxW6m_0U,4564
|
|
4
|
+
mm_balance/constants.py,sha256=kqG2zuwv0l-PzDHIrMJVQpfQWiXjr2DsqGPcKmqNJLo,2334
|
|
5
|
+
mm_balance/diff.py,sha256=GPRbykty2TIBBM8jpYXOV9Itjyd_mz0BTUsQ8KX7cNo,7099
|
|
6
|
+
mm_balance/price.py,sha256=DzvcQngS6wgi_4YWoXxGvOuOkwJvUbN0KI8DeIxbB5A,1494
|
|
7
|
+
mm_balance/result.py,sha256=-Ebq07JMLcQAmRs82cA6aYMbsT1qbZSyAOixr9K_wbg,5157
|
|
8
|
+
mm_balance/token_decimals.py,sha256=N3YppB2F3J_OuNkawpAHLVD-4MRCoVjBI01_OoRg5sY,2201
|
|
9
|
+
mm_balance/utils.py,sha256=_UMX3TV350Sr222tAnxGUf0R5McpwloNTQC-U-xiuHc,636
|
|
10
|
+
mm_balance/workers.py,sha256=eg0Ve1xVu3Kd_thfVmPsp6tEdJsYYvs1ipXiu5rKItY,3758
|
|
11
|
+
mm_balance/config/example.toml,sha256=f3Jr40ziOCv_Txf-BysS89c9r7uS-IYHuvwbQ-iftUs,1802
|
|
12
|
+
mm_balance/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
mm_balance/output/utils.py,sha256=zyN9igdaXGY_vKfc-3dJ13mH1T7JDke3AeB4MY_3AsA,842
|
|
14
|
+
mm_balance/output/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
mm_balance/output/formats/json_format.py,sha256=japDhWBVaZ9Znwrhi6BLsUEyn2zyNVxPFYhH7SiILb8,893
|
|
16
|
+
mm_balance/output/formats/table_format.py,sha256=QtWq6ETIccfBTw-9SPypeZrX2zfZwpoCFi_Qe_qOLAA,4751
|
|
17
|
+
mm_balance/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
mm_balance/rpc/aptos.py,sha256=1JCYCqDim4tk1axXscaAJRXPd4J6vV1ABFbwMbPgrL0,641
|
|
19
|
+
mm_balance/rpc/btc.py,sha256=wBMxUjbqdQipVsTFVkj4tk7loErA2czLVfvG8vjFLeE,493
|
|
20
|
+
mm_balance/rpc/evm.py,sha256=LaU2csGL-VlQauCiTX_WFnstvTyZLMP5gDw2LyV53m8,1048
|
|
21
|
+
mm_balance/rpc/solana.py,sha256=10rJ4eEr9sfEfhXx-X2R7bdJ5dL7bVMwHHfJ4R3QR7U,1071
|
|
22
|
+
mm_balance-0.3.0.dist-info/METADATA,sha256=Owy1HtXoCk7RhiqeEwijv80KKLuDBHbgQdHOrjAxvEs,256
|
|
23
|
+
mm_balance-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
24
|
+
mm_balance-0.3.0.dist-info/entry_points.txt,sha256=rSnP0ZW1a3ACNwTWM7T53CmOycKbzhG43m2_wseENng,50
|
|
25
|
+
mm_balance-0.3.0.dist-info/RECORD,,
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
mm_balance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
mm_balance/cli.py,sha256=AHaD6w97H8ru8nnQv2dcqh9v-d9BVirnlsClbLyHf6M,3796
|
|
3
|
-
mm_balance/config.py,sha256=3qEg9xGCoH6WX91dlmjXVVhkCjJf8KFOmfqE8NgSc64,4603
|
|
4
|
-
mm_balance/constants.py,sha256=kqG2zuwv0l-PzDHIrMJVQpfQWiXjr2DsqGPcKmqNJLo,2334
|
|
5
|
-
mm_balance/price.py,sha256=DzvcQngS6wgi_4YWoXxGvOuOkwJvUbN0KI8DeIxbB5A,1494
|
|
6
|
-
mm_balance/result.py,sha256=WsVLbf54cCJ76rIM2zHaOCQXMfZZMczdg8rlC2tDGWY,4533
|
|
7
|
-
mm_balance/token_decimals.py,sha256=7XL_x_1ZIhp_cr1TvXwnqLqINUWFGo0j5_5lWuxmwpE,2277
|
|
8
|
-
mm_balance/utils.py,sha256=bngYS2WFIakGZO31_ey4MPsllvDhgOxkAnGiXqom3J4,286
|
|
9
|
-
mm_balance/workers.py,sha256=vEmmUI-ioMOv1C_Jcz0rnzI3vn7BaVWFnyXddteJkoA,3766
|
|
10
|
-
mm_balance/config/example.toml,sha256=15Bjk_zkbhx9CqFgx1kbb2cRSBY9-kPKrej7N_8ulTk,1816
|
|
11
|
-
mm_balance/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
mm_balance/output/utils.py,sha256=WUFwshFMKZKdcwRtO21nhxqW78JeLAatDyHPZhdV96A,716
|
|
13
|
-
mm_balance/output/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
mm_balance/output/formats/json_format.py,sha256=japDhWBVaZ9Znwrhi6BLsUEyn2zyNVxPFYhH7SiILb8,893
|
|
15
|
-
mm_balance/output/formats/table_format.py,sha256=o_3MGOb0Ug9sqfaR61JDucSEtmFF22KlJoqpFWRuFu8,4758
|
|
16
|
-
mm_balance/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
-
mm_balance/rpc/aptos.py,sha256=1JCYCqDim4tk1axXscaAJRXPd4J6vV1ABFbwMbPgrL0,641
|
|
18
|
-
mm_balance/rpc/btc.py,sha256=iGDoO7HXP6reLI0bWm0UhEl7-UFZTo5xCWNzFngbITw,458
|
|
19
|
-
mm_balance/rpc/evm.py,sha256=ewlMmRrcXKlky3DPNbnUBTVwnvyw7N9iCZLsCX2V14w,1007
|
|
20
|
-
mm_balance/rpc/solana.py,sha256=c756415rlhQkHsVsqxHcA7wIaSM64IqyxMohjz8IGhY,1031
|
|
21
|
-
mm_balance-0.2.3.dist-info/METADATA,sha256=5FAngHypNiHnXLneqRH9E3n1jX45TIdJekZ4uAIKs4M,225
|
|
22
|
-
mm_balance-0.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
23
|
-
mm_balance-0.2.3.dist-info/entry_points.txt,sha256=rSnP0ZW1a3ACNwTWM7T53CmOycKbzhG43m2_wseENng,50
|
|
24
|
-
mm_balance-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|