meshtensor-cli 9.18.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. meshtensor_cli/__init__.py +22 -0
  2. meshtensor_cli/cli.py +10742 -0
  3. meshtensor_cli/doc_generation_helper.py +4 -0
  4. meshtensor_cli/src/__init__.py +1085 -0
  5. meshtensor_cli/src/commands/__init__.py +0 -0
  6. meshtensor_cli/src/commands/axon/__init__.py +0 -0
  7. meshtensor_cli/src/commands/axon/axon.py +132 -0
  8. meshtensor_cli/src/commands/crowd/__init__.py +0 -0
  9. meshtensor_cli/src/commands/crowd/contribute.py +621 -0
  10. meshtensor_cli/src/commands/crowd/contributors.py +200 -0
  11. meshtensor_cli/src/commands/crowd/create.py +783 -0
  12. meshtensor_cli/src/commands/crowd/dissolve.py +219 -0
  13. meshtensor_cli/src/commands/crowd/refund.py +233 -0
  14. meshtensor_cli/src/commands/crowd/update.py +418 -0
  15. meshtensor_cli/src/commands/crowd/utils.py +124 -0
  16. meshtensor_cli/src/commands/crowd/view.py +991 -0
  17. meshtensor_cli/src/commands/governance/__init__.py +0 -0
  18. meshtensor_cli/src/commands/governance/governance.py +794 -0
  19. meshtensor_cli/src/commands/liquidity/__init__.py +0 -0
  20. meshtensor_cli/src/commands/liquidity/liquidity.py +699 -0
  21. meshtensor_cli/src/commands/liquidity/utils.py +202 -0
  22. meshtensor_cli/src/commands/proxy.py +700 -0
  23. meshtensor_cli/src/commands/stake/__init__.py +0 -0
  24. meshtensor_cli/src/commands/stake/add.py +799 -0
  25. meshtensor_cli/src/commands/stake/auto_staking.py +306 -0
  26. meshtensor_cli/src/commands/stake/children_hotkeys.py +865 -0
  27. meshtensor_cli/src/commands/stake/claim.py +770 -0
  28. meshtensor_cli/src/commands/stake/list.py +738 -0
  29. meshtensor_cli/src/commands/stake/move.py +1211 -0
  30. meshtensor_cli/src/commands/stake/remove.py +1466 -0
  31. meshtensor_cli/src/commands/stake/wizard.py +323 -0
  32. meshtensor_cli/src/commands/subnets/__init__.py +0 -0
  33. meshtensor_cli/src/commands/subnets/mechanisms.py +515 -0
  34. meshtensor_cli/src/commands/subnets/price.py +733 -0
  35. meshtensor_cli/src/commands/subnets/subnets.py +2908 -0
  36. meshtensor_cli/src/commands/sudo.py +1294 -0
  37. meshtensor_cli/src/commands/tc/__init__.py +0 -0
  38. meshtensor_cli/src/commands/tc/tc.py +190 -0
  39. meshtensor_cli/src/commands/treasury/__init__.py +0 -0
  40. meshtensor_cli/src/commands/treasury/treasury.py +194 -0
  41. meshtensor_cli/src/commands/view.py +354 -0
  42. meshtensor_cli/src/commands/wallets.py +2311 -0
  43. meshtensor_cli/src/commands/weights.py +467 -0
  44. meshtensor_cli/src/meshtensor/__init__.py +0 -0
  45. meshtensor_cli/src/meshtensor/balances.py +313 -0
  46. meshtensor_cli/src/meshtensor/chain_data.py +1263 -0
  47. meshtensor_cli/src/meshtensor/extrinsics/__init__.py +0 -0
  48. meshtensor_cli/src/meshtensor/extrinsics/mev_shield.py +174 -0
  49. meshtensor_cli/src/meshtensor/extrinsics/registration.py +1861 -0
  50. meshtensor_cli/src/meshtensor/extrinsics/root.py +550 -0
  51. meshtensor_cli/src/meshtensor/extrinsics/serving.py +255 -0
  52. meshtensor_cli/src/meshtensor/extrinsics/transfer.py +239 -0
  53. meshtensor_cli/src/meshtensor/meshtensor_interface.py +2598 -0
  54. meshtensor_cli/src/meshtensor/minigraph.py +254 -0
  55. meshtensor_cli/src/meshtensor/networking.py +12 -0
  56. meshtensor_cli/src/meshtensor/templates/main-filters.j2 +24 -0
  57. meshtensor_cli/src/meshtensor/templates/main-header.j2 +36 -0
  58. meshtensor_cli/src/meshtensor/templates/neuron-details.j2 +111 -0
  59. meshtensor_cli/src/meshtensor/templates/price-multi.j2 +113 -0
  60. meshtensor_cli/src/meshtensor/templates/price-single.j2 +99 -0
  61. meshtensor_cli/src/meshtensor/templates/subnet-details-header.j2 +49 -0
  62. meshtensor_cli/src/meshtensor/templates/subnet-details.j2 +32 -0
  63. meshtensor_cli/src/meshtensor/templates/subnet-metrics.j2 +57 -0
  64. meshtensor_cli/src/meshtensor/templates/subnets-table.j2 +28 -0
  65. meshtensor_cli/src/meshtensor/templates/table.j2 +267 -0
  66. meshtensor_cli/src/meshtensor/templates/view.css +1058 -0
  67. meshtensor_cli/src/meshtensor/templates/view.j2 +43 -0
  68. meshtensor_cli/src/meshtensor/templates/view.js +1053 -0
  69. meshtensor_cli/src/meshtensor/utils.py +2007 -0
  70. meshtensor_cli/version.py +23 -0
  71. meshtensor_cli-9.18.1.dist-info/METADATA +261 -0
  72. meshtensor_cli-9.18.1.dist-info/RECORD +74 -0
  73. meshtensor_cli-9.18.1.dist-info/WHEEL +4 -0
  74. meshtensor_cli-9.18.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,354 @@
1
+ import asyncio
2
+ import os
3
+ import tempfile
4
+ import webbrowser
5
+ import netaddr
6
+ from typing import Any
7
+
8
+ from meshtensor_cli.src.meshtensor.balances import Balance
9
+ from meshtensor_cli.src.meshtensor.meshtensor_interface import MeshtensorInterface
10
+ from meshtensor_cli.src.meshtensor.utils import console, WalletLike, jinja_env
11
+ from meshtensor_wallet import Wallet
12
+ from meshtensor_cli.src import defaults
13
+
14
+
15
+ ROOT_SYMBOL_HTML = f"&#x{ord('τ'):X};"
16
+
17
+
18
+ async def display_network_dashboard(
19
+ wallet: Wallet,
20
+ meshtensor: "MeshtensorInterface",
21
+ use_wry: bool = False,
22
+ save_file: bool = False,
23
+ dashboard_path: str = None,
24
+ coldkey_ss58: str = None,
25
+ ) -> bool:
26
+ """
27
+ Generate and display the HTML interface.
28
+ """
29
+ if coldkey_ss58:
30
+ wallet = WalletLike(coldkeypub_ss58=coldkey_ss58, name=coldkey_ss58[:7])
31
+ try:
32
+ with console.status("[dark_sea_green3]Fetching data...", spinner="earth"):
33
+ _subnet_data = await fetch_subnet_data(wallet, meshtensor)
34
+ subnet_data = process_subnet_data(_subnet_data)
35
+ html_content = generate_full_page(subnet_data)
36
+
37
+ if use_wry:
38
+ console.print(
39
+ "[dark_sea_green3]Opening dashboard in a window.[/dark_sea_green3]"
40
+ )
41
+ with tempfile.NamedTemporaryFile(
42
+ "w", delete=False, suffix=".html"
43
+ ) as dashboard_file:
44
+ url = f"file://{dashboard_file.name}"
45
+ dashboard_file.write(html_content)
46
+
47
+ webbrowser.open(url, new=1)
48
+ else:
49
+ if save_file:
50
+ dir_path = os.path.expanduser(dashboard_path)
51
+ else:
52
+ dir_path = os.path.expanduser(defaults.dashboard.path)
53
+ if not os.path.exists(dir_path):
54
+ os.makedirs(dir_path)
55
+
56
+ with tempfile.NamedTemporaryFile(
57
+ delete=not save_file,
58
+ suffix=".html",
59
+ mode="w",
60
+ dir=dir_path,
61
+ prefix=f"{wallet.name}_{subnet_data['block_number']}_",
62
+ ) as f:
63
+ f.write(html_content)
64
+ temp_path = f.name
65
+ file_url = f"file://{os.path.abspath(temp_path)}"
66
+
67
+ if not save_file:
68
+ with console.status(
69
+ "[dark_sea_green3]Loading dashboard...[/dark_sea_green3]",
70
+ spinner="material",
71
+ ):
72
+ webbrowser.open(file_url)
73
+ await asyncio.sleep(10)
74
+ return True
75
+
76
+ console.print("[green]Dashboard View opened in your browser[/green]")
77
+ console.print(f"[yellow]The HTML file is saved at: {temp_path}[/yellow]")
78
+ webbrowser.open(file_url)
79
+ return True
80
+
81
+ except Exception as e:
82
+ print(f"Error: {e}")
83
+ return False
84
+
85
+
86
+ def int_to_ip(int_val: int) -> str:
87
+ """Maps to an ip string"""
88
+ return str(netaddr.IPAddress(int_val))
89
+
90
+
91
+ def get_identity(
92
+ hotkey_ss58: str,
93
+ identities: dict,
94
+ old_identities: dict,
95
+ truncate_length: int = 4,
96
+ return_bool: bool = False,
97
+ lookup_hk: bool = True,
98
+ ) -> str:
99
+ """Fetch identity of hotkey from both sources"""
100
+ if lookup_hk:
101
+ if hk_identity := identities["hotkeys"].get(hotkey_ss58):
102
+ return hk_identity.get("identity", {}).get("name", "") or hk_identity.get(
103
+ "display", "~"
104
+ )
105
+ else:
106
+ if ck_identity := identities["coldkeys"].get(hotkey_ss58):
107
+ return ck_identity.get("identity", {}).get("name", "") or ck_identity.get(
108
+ "display", "~"
109
+ )
110
+
111
+ if old_identity := old_identities.get(hotkey_ss58):
112
+ return old_identity.display
113
+ else:
114
+ if return_bool:
115
+ return False
116
+ else:
117
+ return f"{hotkey_ss58[:truncate_length]}...{hotkey_ss58[-truncate_length:]}"
118
+
119
+
120
+ async def fetch_subnet_data(
121
+ wallet: Wallet, meshtensor: "MeshtensorInterface"
122
+ ) -> dict[str, Any]:
123
+ """
124
+ Fetch subnet data from the network.
125
+ """
126
+ block_hash = await meshtensor.substrate.get_chain_head()
127
+
128
+ (
129
+ balance,
130
+ stake_info,
131
+ metagraphs_info,
132
+ subnets_info,
133
+ ck_hk_identities,
134
+ old_identities,
135
+ block_number,
136
+ ) = await asyncio.gather(
137
+ meshtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash=block_hash),
138
+ meshtensor.get_stake_for_coldkey(
139
+ wallet.coldkeypub.ss58_address, block_hash=block_hash
140
+ ),
141
+ meshtensor.get_all_metagraphs_info(block_hash=block_hash),
142
+ meshtensor.all_subnets(block_hash=block_hash),
143
+ meshtensor.fetch_coldkey_hotkey_identities(block_hash=block_hash),
144
+ meshtensor.get_delegate_identities(block_hash=block_hash),
145
+ meshtensor.substrate.get_block_number(block_hash=block_hash),
146
+ )
147
+
148
+ return {
149
+ "balance": balance,
150
+ "stake_info": stake_info,
151
+ "metagraphs_info": metagraphs_info,
152
+ "subnets_info": subnets_info,
153
+ "ck_hk_identities": ck_hk_identities,
154
+ "old_identities": old_identities,
155
+ "wallet": wallet,
156
+ "block_number": block_number,
157
+ }
158
+
159
+
160
+ def process_subnet_data(raw_data: dict[str, Any]) -> dict[str, Any]:
161
+ """
162
+ Process and prepare subnet data.
163
+ """
164
+ balance = raw_data["balance"]
165
+ stake_info = raw_data["stake_info"]
166
+ metagraphs_info = raw_data["metagraphs_info"]
167
+ subnets_info = raw_data["subnets_info"]
168
+ ck_hk_identities = raw_data["ck_hk_identities"]
169
+ old_identities = raw_data["old_identities"]
170
+ wallet = raw_data["wallet"]
171
+ block_number = raw_data["block_number"]
172
+
173
+ pool_info = {info.netuid: info for info in subnets_info}
174
+
175
+ total_ideal_stake_value = Balance.from_tao(0)
176
+ total_slippage_value = Balance.from_tao(0)
177
+
178
+ # Process stake
179
+ stake_dict: dict[int, list[dict[str, Any]]] = {}
180
+ for stake in stake_info:
181
+ if stake.stake.tao > 0:
182
+ slippage_value, _, slippage_percentage = pool_info[
183
+ stake.netuid
184
+ ].alpha_to_tao_with_slippage(stake.stake)
185
+ ideal_value = pool_info[stake.netuid].alpha_to_tao(stake.stake)
186
+ total_ideal_stake_value += ideal_value
187
+ total_slippage_value += slippage_value
188
+ stake_dict.setdefault(stake.netuid, []).append(
189
+ {
190
+ "hotkey": stake.hotkey_ss58,
191
+ "hotkey_identity": get_identity(
192
+ stake.hotkey_ss58, ck_hk_identities, old_identities
193
+ ),
194
+ "amount": stake.stake.tao,
195
+ "emission": stake.emission.tao,
196
+ "is_registered": stake.is_registered,
197
+ "tao_emission": stake.tao_emission.tao,
198
+ "ideal_value": ideal_value.tao,
199
+ "slippage_value": slippage_value.tao,
200
+ "slippage_percentage": slippage_percentage,
201
+ }
202
+ )
203
+
204
+ # Process metagraph
205
+ subnets = []
206
+ for meta_info in metagraphs_info:
207
+ subnet_stakes = stake_dict.get(meta_info.netuid, [])
208
+ metagraph_info = {
209
+ "netuid": meta_info.netuid,
210
+ "name": meta_info.name,
211
+ "symbol": meta_info.symbol,
212
+ "alpha_in": 0 if meta_info.netuid == 0 else meta_info.alpha_in.tao,
213
+ "alpha_out": meta_info.alpha_out.tao,
214
+ "tao_in": 0 if meta_info.netuid == 0 else meta_info.tao_in.tao,
215
+ "tao_in_emission": meta_info.tao_in_emission.tao,
216
+ "num_uids": meta_info.num_uids,
217
+ "max_uids": meta_info.max_uids,
218
+ "moving_price": meta_info.moving_price.tao,
219
+ "blocks_since_last_step": "~"
220
+ if meta_info.netuid == 0
221
+ else meta_info.blocks_since_last_step,
222
+ "tempo": "~" if meta_info.netuid == 0 else meta_info.tempo,
223
+ "registration_allowed": meta_info.registration_allowed,
224
+ "commit_reveal_weights_enabled": meta_info.commit_reveal_weights_enabled,
225
+ "hotkeys": meta_info.hotkeys,
226
+ "coldkeys": meta_info.coldkeys,
227
+ "updated_identities": [],
228
+ "processed_axons": [],
229
+ "rank": meta_info.rank,
230
+ "trust": meta_info.trust,
231
+ "consensus": meta_info.consensus,
232
+ "incentives": meta_info.incentives,
233
+ "dividends": meta_info.dividends,
234
+ "active": meta_info.active,
235
+ "validator_permit": meta_info.validator_permit,
236
+ "pruning_score": meta_info.pruning_score,
237
+ "last_update": meta_info.last_update,
238
+ "block_at_registration": meta_info.block_at_registration,
239
+ }
240
+
241
+ # Process axon data and convert IPs
242
+ for axon in meta_info.axons:
243
+ if axon:
244
+ processed_axon = {
245
+ "ip": int_to_ip(axon["ip"]) if axon["ip"] else "N/A",
246
+ "port": axon["port"],
247
+ "ip_type": axon["ip_type"],
248
+ }
249
+ metagraph_info["processed_axons"].append(processed_axon)
250
+ else:
251
+ metagraph_info["processed_axons"].append(None)
252
+
253
+ # Add identities
254
+ for hotkey in meta_info.hotkeys:
255
+ identity = get_identity(
256
+ hotkey, ck_hk_identities, old_identities, truncate_length=2
257
+ )
258
+ metagraph_info["updated_identities"].append(identity)
259
+
260
+ # Balance conversion
261
+ for field in [
262
+ "emission",
263
+ "alpha_stake",
264
+ "tao_stake",
265
+ "total_stake",
266
+ ]:
267
+ if hasattr(meta_info, field):
268
+ raw_data = getattr(meta_info, field)
269
+ if isinstance(raw_data, list):
270
+ metagraph_info[field] = [
271
+ x.tao if hasattr(x, "mesh") else x for x in raw_data
272
+ ]
273
+ else:
274
+ metagraph_info[field] = raw_data
275
+
276
+ # Calculate price
277
+ price = (
278
+ 1
279
+ if metagraph_info["netuid"] == 0
280
+ else metagraph_info["tao_in"] / metagraph_info["alpha_in"]
281
+ if metagraph_info["alpha_in"] > 0
282
+ else 0
283
+ )
284
+
285
+ # Package it all up
286
+ symbol_html = f"&#x{ord(meta_info.symbol):X};"
287
+ subnets.append(
288
+ {
289
+ "netuid": meta_info.netuid,
290
+ "name": meta_info.name,
291
+ "symbol": symbol_html,
292
+ "price": price,
293
+ "market_cap": float(
294
+ (metagraph_info["alpha_in"] + metagraph_info["alpha_out"]) * price
295
+ )
296
+ if price
297
+ else 0,
298
+ "emission": metagraph_info["tao_in_emission"],
299
+ "total_stake": metagraph_info["alpha_out"],
300
+ "your_stakes": subnet_stakes,
301
+ "metagraph_info": metagraph_info,
302
+ }
303
+ )
304
+ subnets.sort(key=lambda x: x["market_cap"], reverse=True)
305
+
306
+ wallet_identity = get_identity(
307
+ wallet.coldkeypub.ss58_address,
308
+ ck_hk_identities,
309
+ old_identities,
310
+ return_bool=True,
311
+ lookup_hk=False,
312
+ )
313
+ if not wallet_identity:
314
+ wallet_identity = wallet.name
315
+ else:
316
+ wallet_identity = f"{wallet_identity} ({wallet.name})"
317
+
318
+ return {
319
+ "wallet_info": {
320
+ "name": wallet_identity,
321
+ "balance": balance.tao,
322
+ "coldkey": wallet.coldkeypub.ss58_address,
323
+ "total_ideal_stake_value": total_ideal_stake_value.tao,
324
+ "total_slippage_value": total_slippage_value.tao,
325
+ },
326
+ "subnets": subnets,
327
+ "block_number": block_number,
328
+ }
329
+
330
+
331
+ def generate_full_page(data: dict[str, Any]) -> str:
332
+ """
333
+ Generate full HTML content for the interface.
334
+ """
335
+ wallet_info = data["wallet_info"]
336
+ truncated_coldkey = f"{wallet_info['coldkey'][:6]}...{wallet_info['coldkey'][-6:]}"
337
+ block_number = data["block_number"]
338
+ # Calculate slippage percentage
339
+ ideal_value = wallet_info["total_ideal_stake_value"]
340
+ slippage_value = wallet_info["total_slippage_value"]
341
+ slippage_percentage = (
342
+ ((ideal_value - slippage_value) / ideal_value * 100) if ideal_value > 0 else 0
343
+ )
344
+
345
+ template = jinja_env.get_template("view.j2")
346
+
347
+ return template.render(
348
+ root_symbol_html=ROOT_SYMBOL_HTML,
349
+ block_number=block_number,
350
+ truncated_coldkey=truncated_coldkey,
351
+ slippage_percentage=slippage_percentage,
352
+ wallet_info=wallet_info,
353
+ subnets=data["subnets"],
354
+ )