mm-balance 0.1.12__py3-none-any.whl → 0.1.13__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/balances.py CHANGED
@@ -80,3 +80,11 @@ class Balances:
80
80
  def get_group_balances(self, group_index: int, network: Network) -> list[Balance]:
81
81
  # TODO: can we get network by group_index?
82
82
  return [b for b in self.tasks[network] if b.group_index == group_index]
83
+
84
+ def get_errors(self) -> list[Balance]:
85
+ result = []
86
+ for network in self.tasks:
87
+ for task in self.tasks[network]:
88
+ if task.balance is not None and task.balance.is_err():
89
+ result.append(task)
90
+ return result
mm_balance/cli.py CHANGED
@@ -51,6 +51,7 @@ def cli(
51
51
 
52
52
  output.print_groups(balances, config, prices)
53
53
  output.print_total(config, balances, prices)
54
+ output.print_errors(config, balances)
54
55
 
55
56
 
56
57
  if __name__ == "__main__":
@@ -60,3 +60,4 @@ addresses:
60
60
  #- http://123.123.123.124
61
61
  #round_ndigits: 4
62
62
  #price: yes
63
+ #skip_empty: no
mm_balance/config.py CHANGED
@@ -1,19 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from decimal import Decimal
4
+ from pathlib import Path
4
5
  from typing import Self
5
6
 
6
7
  import pydash
7
8
  from mm_std import BaseConfig, PrintFormat, fatal, hr
8
9
  from pydantic import Field, field_validator, model_validator
9
10
 
10
- from mm_balance.constants import (
11
- DEFAULT_ARBITRUM_ONE_NODES,
12
- DEFAULT_ETHEREUM_NODES,
13
- DEFAULT_SOLANA_NODES,
14
- TOKEN_ADDRESS,
15
- Network,
16
- )
11
+ from mm_balance.constants import DEFAULT_NODES, TOKEN_ADDRESS, Network
17
12
 
18
13
 
19
14
  class Group(BaseConfig):
@@ -34,7 +29,7 @@ class Group(BaseConfig):
34
29
  return result
35
30
 
36
31
  @field_validator("ticker", mode="after")
37
- def coin_validator(cls, v: str) -> str:
32
+ def ticker_validator(cls, v: str) -> str:
38
33
  return v.upper()
39
34
 
40
35
  @field_validator("addresses", mode="before")
@@ -63,7 +58,7 @@ class Group(BaseConfig):
63
58
  else:
64
59
  # TODO: check address is valid
65
60
  addresses.append(address)
66
- self.addresses = addresses
61
+ self.addresses = pydash.uniq(process_file_addresses(addresses))
67
62
 
68
63
 
69
64
  class AddressGroup(BaseConfig):
@@ -85,6 +80,7 @@ class Config(BaseConfig):
85
80
  nodes: dict[Network, list[str]] = Field(default_factory=dict)
86
81
  print_format: PrintFormat = PrintFormat.TABLE
87
82
  price: bool = True
83
+ skip_empty: bool = False # don't print the address with an empty balance
88
84
 
89
85
  workers: dict[Network, int] = {network: 5 for network in Network}
90
86
 
@@ -102,36 +98,16 @@ class Config(BaseConfig):
102
98
  group.process_addresses(self.addresses)
103
99
 
104
100
  # load default rpc nodes
105
- if Network.BITCOIN not in self.nodes:
106
- self.nodes[Network.BITCOIN] = []
107
- if Network.ETHEREUM not in self.nodes:
108
- self.nodes[Network.ETHEREUM] = DEFAULT_ETHEREUM_NODES
109
- if Network.ARBITRUM_ONE not in self.nodes:
110
- self.nodes[Network.ARBITRUM_ONE] = DEFAULT_ARBITRUM_ONE_NODES
111
- if Network.OP_MAINNET not in self.nodes:
112
- self.nodes[Network.OP_MAINNET] = DEFAULT_ARBITRUM_ONE_NODES
113
- if Network.SOLANA not in self.nodes:
114
- self.nodes[Network.SOLANA] = DEFAULT_SOLANA_NODES
101
+ for network in Network:
102
+ if network not in self.nodes:
103
+ self.nodes[network] = DEFAULT_NODES[network]
115
104
 
116
105
  return self
117
106
 
118
107
 
119
- # def detect_network(coin: str) -> Network:
120
- #
121
- # # coin = coin.lower()
122
- # # if coin == "btc":
123
- # # return Network.BTC
124
- # # if coin == "eth":
125
- # # return Network.ETH
126
- # # if coin == "sol":
127
- # # return Network.SOL
128
- # # return Network.ETH
129
- # # # TODO: raise ValueError(f"can't get network for the coin: {coin}")
130
-
131
-
132
- def detect_token_address(coin: str, network: Network) -> str | None:
108
+ def detect_token_address(ticker: str, network: Network) -> str | None:
133
109
  if network in TOKEN_ADDRESS:
134
- return TOKEN_ADDRESS[network].get(coin)
110
+ return TOKEN_ADDRESS[network].get(ticker)
135
111
 
136
112
 
137
113
  def get_proxies(proxies_url: str) -> list[str]:
@@ -147,3 +123,17 @@ def get_proxies(proxies_url: str) -> list[str]:
147
123
 
148
124
  def get_address_group_by_name(address_groups: list[AddressGroup], name: str) -> AddressGroup | None:
149
125
  return pydash.find(address_groups, lambda g: g.name == name)
126
+
127
+
128
+ def process_file_addresses(addresses: list[str]) -> list[str]:
129
+ result = []
130
+ for address in addresses:
131
+ if address.startswith("file://"):
132
+ path = Path(address.removeprefix("file://"))
133
+ if path.is_file():
134
+ result.extend(path.read_text().strip().splitlines())
135
+ else:
136
+ fatal(f"File with addresses not found: {path}")
137
+ else:
138
+ result.append(address)
139
+ return result
mm_balance/constants.py CHANGED
@@ -27,7 +27,14 @@ TOKEN_ADDRESS: dict[Network, dict[str, str]] = {
27
27
  "USDT": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
28
28
  "USDC": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
29
29
  },
30
- # TODO: Add for Arbitrum and Optimism, usdt + usdc
30
+ Network.ARBITRUM_ONE: {
31
+ "USDT": "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9",
32
+ "USDC": "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8",
33
+ },
34
+ Network.OP_MAINNET: {
35
+ "USDT": "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58",
36
+ "USDC": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
37
+ },
31
38
  }
32
39
 
33
40
  TICKER_TO_COINGECKO_ID = {
@@ -40,7 +47,10 @@ TICKER_TO_COINGECKO_ID = {
40
47
 
41
48
  USD_STABLECOINS = ["USDT", "USDC"]
42
49
 
43
- DEFAULT_ETHEREUM_NODES = ["https://ethereum.publicnode.com", "https://rpc.ankr.com/eth"]
44
- DEFAULT_SOLANA_NODES = ["https://api.mainnet-beta.solana.com"]
45
- DEFAULT_ARBITRUM_ONE_NODES = ["https://arb1.arbitrum.io/rpc", "https://arbitrum.llamarpc.com"]
46
- DEFAULT_OP_MAINNET_NODES = ["https://mainnet.optimism.io", "https://optimism.llamarpc.com"]
50
+ DEFAULT_NODES: dict[Network, list[str]] = {
51
+ Network.ARBITRUM_ONE: ["https://arb1.arbitrum.io/rpc", "https://arbitrum.llamarpc.com"],
52
+ Network.BITCOIN: [],
53
+ Network.ETHEREUM: ["https://ethereum.publicnode.com", "https://rpc.ankr.com/eth"],
54
+ Network.SOLANA: ["https://api.mainnet-beta.solana.com"],
55
+ Network.OP_MAINNET: ["https://mainnet.optimism.io", "https://optimism.llamarpc.com"],
56
+ }
mm_balance/output.py CHANGED
@@ -20,6 +20,8 @@ def _print_group(group: Group, group_balances: list[Balances.Balance], config: C
20
20
  balance_sum = Decimal(0)
21
21
  usd_sum = Decimal(0)
22
22
  for address_task in group_balances:
23
+ if config.skip_empty and isinstance(address_task.balance, Ok) and address_task.balance.ok == Decimal(0):
24
+ continue
23
25
  row = [address_task.address, address_task.balance.ok_or_err()] # type: ignore[union-attr]
24
26
  if isinstance(address_task.balance, Ok):
25
27
  balance_sum += address_task.balance.ok
@@ -57,6 +59,17 @@ def print_total(config: Config, balances: Balances, prices: Prices) -> None:
57
59
  total.print()
58
60
 
59
61
 
62
+ def print_errors(config: Config, balances: Balances) -> None:
63
+ error_balances = balances.get_errors()
64
+ if not error_balances:
65
+ return
66
+ rows = []
67
+ for balance in error_balances:
68
+ group = config.groups[balance.group_index]
69
+ rows.append([group.ticker + " / " + group.network, balance.address, balance.balance.err]) # type: ignore[union-attr]
70
+ print_table("Errors", ["coin", "address", "error"], rows)
71
+
72
+
60
73
  def create_progress_bar() -> Progress:
61
74
  return Progress(
62
75
  TextColumn("[progress.description]{task.description}"),
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mm-balance
3
- Version: 0.1.12
3
+ Version: 0.1.13
4
4
  Requires-Python: >=3.12
5
5
  Requires-Dist: mm-btc==0.1.0
6
6
  Requires-Dist: mm-eth==0.1.3
7
- Requires-Dist: mm-solana==0.1.4
7
+ Requires-Dist: mm-solana==0.1.5
8
8
  Requires-Dist: typer>=0.12.5
@@ -0,0 +1,18 @@
1
+ mm_balance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ mm_balance/balances.py,sha256=yk4gqfdBiBuq8SX4BzObBz_yYh3d6DJ-iDcvaSZFrFM,3939
3
+ mm_balance/cli.py,sha256=B8YDx2ltqIu1jJoxLA514Y1A-bcrNPEN6nK73ZYIyZc,1742
4
+ mm_balance/config.py,sha256=4NOp8CploUsDCYAJedBBEfgyGtREhcmmg3r2q2emeX4,4744
5
+ mm_balance/constants.py,sha256=vtISLm13dAVaJEtZYAyNlvZp8LwiYoEsXjcMBmr1xzE,1632
6
+ mm_balance/output.py,sha256=r2h3NQ5cZ9uWyGP0bfzYwVgS8TnNwj5zTcJbcY01zBo,3141
7
+ mm_balance/price.py,sha256=wlClQNW03BSzq8gxQxH9f5LnwRFZBqeI-tfY9vtlSJE,2005
8
+ mm_balance/token_decimals.py,sha256=iNhAEEPfg8MvjxBGnFdxh82pYg3v26w4s9xro2Emoi8,1307
9
+ mm_balance/total.py,sha256=PERRR9m77LBgu30tUEdyfGdtmVDFa-dICq8S9zcH1eA,4716
10
+ mm_balance/config/example.yml,sha256=oMg0bBygKFprcf3anW5nb0lkpl0Bc3cQ5eKTeEpiM_k,1498
11
+ mm_balance/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ mm_balance/rpc/btc.py,sha256=ugp90H7YW0kiXIh98bQWk9mQTW20yE-jBiCpRvfoH-U,481
13
+ mm_balance/rpc/eth.py,sha256=G7aYjTw6xJwcsAyIp9eVW8NRVDUGeTCpdbd-CqgqHyw,1167
14
+ mm_balance/rpc/solana.py,sha256=vmCAaeQKwxg95qdSmUEjFS9bAPUJVx8irtWv6nlRBvU,1174
15
+ mm_balance-0.1.13.dist-info/METADATA,sha256=pEBa6u5nBDTruCLZzIDBjSub0hk6jaLzZuWTvE5v9dQ,198
16
+ mm_balance-0.1.13.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
17
+ mm_balance-0.1.13.dist-info/entry_points.txt,sha256=rSnP0ZW1a3ACNwTWM7T53CmOycKbzhG43m2_wseENng,50
18
+ mm_balance-0.1.13.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- mm_balance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- mm_balance/balances.py,sha256=4PjyxEynkv_59cs6-HujU3rn18DebotnWIu8Sa1Jj9s,3662
3
- mm_balance/cli.py,sha256=GZB3MgYxOV5AWhX2NvU9LWKF7I2iNQzyhgC2oKlOODU,1700
4
- mm_balance/config.py,sha256=I1QmL5Nlyyifn78wmndnw5zi-zk_B-paK42KTd4pRb4,4995
5
- mm_balance/constants.py,sha256=J0N8GmWAagKGyJ8DMnmxhU6LXEkvKhYiJpacKqxkQ4s,1308
6
- mm_balance/output.py,sha256=5y35jJUDJqrz86EVNklBVa06b1ia0ppy2ypTy9Jkbb0,2570
7
- mm_balance/price.py,sha256=wlClQNW03BSzq8gxQxH9f5LnwRFZBqeI-tfY9vtlSJE,2005
8
- mm_balance/token_decimals.py,sha256=iNhAEEPfg8MvjxBGnFdxh82pYg3v26w4s9xro2Emoi8,1307
9
- mm_balance/total.py,sha256=PERRR9m77LBgu30tUEdyfGdtmVDFa-dICq8S9zcH1eA,4716
10
- mm_balance/config/example.yml,sha256=KxSmjIKY1LXjUzfA4sv3j352fVePRDEeqG48xEwKWkc,1482
11
- mm_balance/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- mm_balance/rpc/btc.py,sha256=ugp90H7YW0kiXIh98bQWk9mQTW20yE-jBiCpRvfoH-U,481
13
- mm_balance/rpc/eth.py,sha256=G7aYjTw6xJwcsAyIp9eVW8NRVDUGeTCpdbd-CqgqHyw,1167
14
- mm_balance/rpc/solana.py,sha256=vmCAaeQKwxg95qdSmUEjFS9bAPUJVx8irtWv6nlRBvU,1174
15
- mm_balance-0.1.12.dist-info/METADATA,sha256=fYBCpx8m2vk02CTT-tJskdEZoi_wZkBdXc8cprDU4_8,198
16
- mm_balance-0.1.12.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
17
- mm_balance-0.1.12.dist-info/entry_points.txt,sha256=rSnP0ZW1a3ACNwTWM7T53CmOycKbzhG43m2_wseENng,50
18
- mm_balance-0.1.12.dist-info/RECORD,,