bittensor-cli 9.4.4__py3-none-any.whl → 9.5.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.
- bittensor_cli/cli.py +3 -10
- bittensor_cli/src/bittensor/utils.py +8 -38
- bittensor_cli/src/commands/stake/add.py +1 -1
- bittensor_cli/src/commands/stake/remove.py +1 -1
- bittensor_cli/src/commands/subnets/price.py +48 -289
- bittensor_cli/src/commands/sudo.py +10 -6
- bittensor_cli/src/commands/view.py +24 -2615
- bittensor_cli/src/commands/wallets.py +1 -7
- {bittensor_cli-9.4.4.dist-info → bittensor_cli-9.5.0.dist-info}/METADATA +5 -5
- {bittensor_cli-9.4.4.dist-info → bittensor_cli-9.5.0.dist-info}/RECORD +13 -13
- {bittensor_cli-9.4.4.dist-info → bittensor_cli-9.5.0.dist-info}/WHEEL +0 -0
- {bittensor_cli-9.4.4.dist-info → bittensor_cli-9.5.0.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.4.4.dist-info → bittensor_cli-9.5.0.dist-info}/top_level.txt +0 -0
bittensor_cli/cli.py
CHANGED
@@ -67,8 +67,6 @@ from bittensor_cli.src.bittensor.utils import (
|
|
67
67
|
prompt_for_identity,
|
68
68
|
validate_uri,
|
69
69
|
prompt_for_subnet_identity,
|
70
|
-
print_linux_dependency_message,
|
71
|
-
is_linux,
|
72
70
|
validate_rate_tolerance,
|
73
71
|
)
|
74
72
|
|
@@ -4875,9 +4873,6 @@ class CLIManager:
|
|
4875
4873
|
if all_netuids and not json_output:
|
4876
4874
|
html_output = True
|
4877
4875
|
|
4878
|
-
if html_output and is_linux():
|
4879
|
-
print_linux_dependency_message()
|
4880
|
-
|
4881
4876
|
return self._run_command(
|
4882
4877
|
price.price(
|
4883
4878
|
self.initialize_chain(network),
|
@@ -5655,7 +5650,7 @@ class CLIManager:
|
|
5655
5650
|
help="Coldkey SS58 address to view dashboard for",
|
5656
5651
|
),
|
5657
5652
|
use_wry: bool = typer.Option(
|
5658
|
-
False, "--use-wry", help="
|
5653
|
+
False, "--use-wry", "--html", help="Display output in browser window."
|
5659
5654
|
),
|
5660
5655
|
save_file: bool = typer.Option(
|
5661
5656
|
False, "--save-file", "--save", help="Save the dashboard HTML file"
|
@@ -5668,12 +5663,10 @@ class CLIManager:
|
|
5668
5663
|
Display html dashboard with subnets list, stake, and neuron information.
|
5669
5664
|
"""
|
5670
5665
|
self.verbosity_handler(quiet, verbose)
|
5671
|
-
if use_wry and is_linux():
|
5672
|
-
print_linux_dependency_message()
|
5673
5666
|
|
5674
5667
|
if use_wry and save_file:
|
5675
|
-
print_error("Cannot save file when using
|
5676
|
-
|
5668
|
+
print_error("Cannot save file when using browser output.")
|
5669
|
+
return
|
5677
5670
|
|
5678
5671
|
if save_file:
|
5679
5672
|
if not dashboard_path:
|
@@ -15,7 +15,7 @@ from bittensor_wallet import Wallet, Keypair
|
|
15
15
|
from bittensor_wallet.utils import SS58_FORMAT
|
16
16
|
from bittensor_wallet.errors import KeyFileError, PasswordError
|
17
17
|
from bittensor_wallet import utils
|
18
|
-
from jinja2 import Template
|
18
|
+
from jinja2 import Template, Environment, PackageLoader, select_autoescape
|
19
19
|
from markupsafe import Markup
|
20
20
|
import numpy as np
|
21
21
|
from numpy.typing import NDArray
|
@@ -38,6 +38,11 @@ json_console = Console()
|
|
38
38
|
err_console = Console(stderr=True)
|
39
39
|
verbose_console = Console(quiet=True)
|
40
40
|
|
41
|
+
jinja_env = Environment(
|
42
|
+
loader=PackageLoader("bittensor_cli", "src/bittensor/templates"),
|
43
|
+
autoescape=select_autoescape(),
|
44
|
+
)
|
45
|
+
|
41
46
|
UnlockStatus = namedtuple("UnlockStatus", ["success", "message"])
|
42
47
|
|
43
48
|
|
@@ -835,11 +840,7 @@ def update_metadata_table(table_name: str, values: dict[str, str]) -> None:
|
|
835
840
|
"""
|
836
841
|
with DB() as (conn, cursor):
|
837
842
|
cursor.execute(
|
838
|
-
"CREATE TABLE IF NOT EXISTS metadata ("
|
839
|
-
"TableName TEXT, "
|
840
|
-
"Key TEXT, "
|
841
|
-
"Value TEXT"
|
842
|
-
")"
|
843
|
+
"CREATE TABLE IF NOT EXISTS metadata (TableName TEXT, Key TEXT, Value TEXT)"
|
843
844
|
)
|
844
845
|
conn.commit()
|
845
846
|
for key, value in values.items():
|
@@ -1295,37 +1296,6 @@ def get_subnet_name(subnet_info, max_length: int = 20) -> str:
|
|
1295
1296
|
return name
|
1296
1297
|
|
1297
1298
|
|
1298
|
-
def print_linux_dependency_message():
|
1299
|
-
"""Prints the WebKit dependency message for Linux systems."""
|
1300
|
-
console.print("[red]This command requires WebKit dependencies on Linux.[/red]")
|
1301
|
-
console.print(
|
1302
|
-
"\nPlease make sure these packages are installed on your system for PyWry to work:"
|
1303
|
-
)
|
1304
|
-
console.print("\nArch Linux / Manjaro:")
|
1305
|
-
console.print("[green]sudo pacman -S webkit2gtk[/green]")
|
1306
|
-
console.print("\nDebian / Ubuntu:")
|
1307
|
-
console.print("[green]sudo apt install libwebkit2gtk-4.0-dev[/green]")
|
1308
|
-
console.print("\nNote for Ubuntu 24.04+ & Debian 13+:")
|
1309
|
-
console.print("You may need these additional steps to install libwebkit2gtk:")
|
1310
|
-
console.print(
|
1311
|
-
"\tCreate a new source file with: [green]sudo vim /etc/apt/sources.list.d/jammy-temp.list[/green]"
|
1312
|
-
)
|
1313
|
-
console.print(
|
1314
|
-
"\tAdd this into the file and save: [green]deb http://archive.ubuntu.com/ubuntu jammy main universe[/green]"
|
1315
|
-
)
|
1316
|
-
console.print(
|
1317
|
-
"\tUpdate the repository and install the webkit dependency: [green]sudo apt update && sudo apt install "
|
1318
|
-
"libwebkit2gtk-4.0-dev[/green]"
|
1319
|
-
)
|
1320
|
-
console.print("\nFedora / CentOS / AlmaLinux:")
|
1321
|
-
console.print("[green]sudo dnf install gtk3-devel webkit2gtk3-devel[/green]\n\n")
|
1322
|
-
|
1323
|
-
|
1324
|
-
def is_linux():
|
1325
|
-
"""Returns True if the operating system is Linux."""
|
1326
|
-
return platform.system().lower() == "linux"
|
1327
|
-
|
1328
|
-
|
1329
1299
|
def validate_rate_tolerance(value: Optional[float]) -> Optional[float]:
|
1330
1300
|
"""Validates rate tolerance input"""
|
1331
1301
|
if value is not None:
|
@@ -1337,7 +1307,7 @@ def validate_rate_tolerance(value: Optional[float]) -> Optional[float]:
|
|
1337
1307
|
raise typer.BadParameter("Rate tolerance cannot be greater than 1 (100%).")
|
1338
1308
|
if value > 0.5:
|
1339
1309
|
console.print(
|
1340
|
-
f"[yellow]Warning: High rate tolerance of {value*100}% specified. "
|
1310
|
+
f"[yellow]Warning: High rate tolerance of {value * 100}% specified. "
|
1341
1311
|
"This may result in unfavorable transaction execution.[/yellow]"
|
1342
1312
|
)
|
1343
1313
|
return value
|
@@ -586,7 +586,7 @@ def _define_stake_table(
|
|
586
586
|
|
587
587
|
if safe_staking:
|
588
588
|
table.add_column(
|
589
|
-
f"Rate with tolerance: [blue]({rate_tolerance*100}%)[/blue]",
|
589
|
+
f"Rate with tolerance: [blue]({rate_tolerance * 100}%)[/blue]",
|
590
590
|
justify="center",
|
591
591
|
style=COLOR_PALETTE["POOLS"]["RATE"],
|
592
592
|
)
|
@@ -1266,7 +1266,7 @@ def _create_unstake_table(
|
|
1266
1266
|
)
|
1267
1267
|
if safe_staking:
|
1268
1268
|
table.add_column(
|
1269
|
-
f"Rate with tolerance: [blue]({rate_tolerance*100}%)[/blue]",
|
1269
|
+
f"Rate with tolerance: [blue]({rate_tolerance * 100}%)[/blue]",
|
1270
1270
|
justify="center",
|
1271
1271
|
style=COLOR_PALETTE["POOLS"]["RATE"],
|
1272
1272
|
)
|
@@ -1,7 +1,9 @@
|
|
1
1
|
import asyncio
|
2
2
|
import json
|
3
3
|
import math
|
4
|
-
|
4
|
+
import tempfile
|
5
|
+
import webbrowser
|
6
|
+
|
5
7
|
from typing import TYPE_CHECKING
|
6
8
|
|
7
9
|
import plotille
|
@@ -14,6 +16,7 @@ from bittensor_cli.src.bittensor.utils import (
|
|
14
16
|
get_subnet_name,
|
15
17
|
print_error,
|
16
18
|
json_console,
|
19
|
+
jinja_env,
|
17
20
|
)
|
18
21
|
|
19
22
|
if TYPE_CHECKING:
|
@@ -180,11 +183,7 @@ def _process_subnet_data(block_numbers, all_subnet_infos, netuids, all_netuids):
|
|
180
183
|
|
181
184
|
|
182
185
|
def _generate_html_single_subnet(
|
183
|
-
netuid,
|
184
|
-
data,
|
185
|
-
block_numbers,
|
186
|
-
interval_hours,
|
187
|
-
log_scale,
|
186
|
+
netuid, data, block_numbers, interval_hours, log_scale, title: str
|
188
187
|
):
|
189
188
|
"""
|
190
189
|
Generate an HTML chart for a single subnet.
|
@@ -242,119 +241,22 @@ def _generate_html_single_subnet(
|
|
242
241
|
type="log" if log_scale else "linear",
|
243
242
|
)
|
244
243
|
|
245
|
-
|
246
|
-
price_change_class = "text-green" if stats["change_pct"] > 0 else "text-red"
|
247
|
-
# Change sign
|
248
|
-
sign_icon = "▲" if stats["change_pct"] > 0 else "▼"
|
249
|
-
|
250
|
-
fig_dict = fig.to_dict()
|
251
|
-
fig_json = json.dumps(fig_dict)
|
252
|
-
html_content = f"""
|
253
|
-
<!DOCTYPE html>
|
254
|
-
<html lang="en">
|
255
|
-
<head>
|
256
|
-
<meta charset="UTF-8">
|
257
|
-
<title>Subnet Price View</title>
|
258
|
-
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
259
|
-
<style>
|
260
|
-
body {{
|
261
|
-
background-color: #000;
|
262
|
-
color: #fff;
|
263
|
-
font-family: Arial, sans-serif;
|
264
|
-
margin: 0;
|
265
|
-
padding: 20px;
|
266
|
-
}}
|
267
|
-
.header-container {{
|
268
|
-
display: flex;
|
269
|
-
align-items: flex-start;
|
270
|
-
justify-content: space-between;
|
271
|
-
margin-bottom: 20px;
|
272
|
-
}}
|
273
|
-
.price-info {{
|
274
|
-
max-width: 60%;
|
275
|
-
}}
|
276
|
-
.main-price {{
|
277
|
-
font-size: 36px;
|
278
|
-
font-weight: 600;
|
279
|
-
margin-bottom: 5px;
|
280
|
-
}}
|
281
|
-
.price-change {{
|
282
|
-
font-size: 18px;
|
283
|
-
margin-left: 8px;
|
284
|
-
font-weight: 500;
|
285
|
-
}}
|
286
|
-
.text-green {{ color: #00FF00; }}
|
287
|
-
.text-red {{ color: #FF5555; }}
|
288
|
-
.text-blue {{ color: #87CEEB; }}
|
289
|
-
.text-steel {{ color: #4682B4; }}
|
290
|
-
.text-purple{{ color: #DDA0DD; }}
|
291
|
-
.text-gold {{ color: #FFD700; }}
|
292
|
-
|
293
|
-
.sub-stats-row {{
|
294
|
-
display: flex;
|
295
|
-
flex-wrap: wrap;
|
296
|
-
margin-top: 10px;
|
297
|
-
}}
|
298
|
-
.stat-item {{
|
299
|
-
margin-right: 20px;
|
300
|
-
margin-bottom: 6px;
|
301
|
-
font-size: 14px;
|
302
|
-
}}
|
303
|
-
.side-stats {{
|
304
|
-
min-width: 220px;
|
305
|
-
display: flex;
|
306
|
-
flex-direction: column;
|
307
|
-
align-items: flex-start;
|
308
|
-
}}
|
309
|
-
.side-stats div {{
|
310
|
-
margin-bottom: 6px;
|
311
|
-
font-size: 14px;
|
312
|
-
}}
|
313
|
-
#chart-container {{
|
314
|
-
margin-top: 20px;
|
315
|
-
width: 100%;
|
316
|
-
height: 600px;
|
317
|
-
}}
|
318
|
-
</style>
|
319
|
-
</head>
|
320
|
-
<body>
|
321
|
-
<div class="header-container">
|
322
|
-
<div class="price-info">
|
323
|
-
<div class="main-price">
|
324
|
-
{stats['current_price']:.6f} {stats['symbol']}
|
325
|
-
<span class="price-change {price_change_class}">
|
326
|
-
{sign_icon} {abs(stats['change_pct']):.2f}%
|
327
|
-
</span>
|
328
|
-
</div>
|
329
|
-
<div class="sub-stats-row">
|
330
|
-
<div class="stat-item">
|
331
|
-
{interval_hours}h High: <span class="text-green">{stats['high']:.6f} {stats['symbol']}</span>
|
332
|
-
</div>
|
333
|
-
<div class="stat-item">
|
334
|
-
{interval_hours}h Low: <span class="text-red">{stats['low']:.6f} {stats['symbol']}</span>
|
335
|
-
</div>
|
336
|
-
</div>
|
337
|
-
</div>
|
338
|
-
<div class="side-stats">
|
339
|
-
<div>Supply: <span class="text-blue">{stats['supply']:.2f} {stats['symbol']}</span></div>
|
340
|
-
<div>Market Cap: <span class="text-steel">{stats['market_cap']:.2f} τ</span></div>
|
341
|
-
<div>Emission: <span class="text-purple">{stats['emission']:.2f} {stats['symbol']}</span></div>
|
342
|
-
<div>Stake: <span class="text-gold">{stats['stake']:.2f} {stats['symbol']}</span></div>
|
343
|
-
</div>
|
344
|
-
</div>
|
345
|
-
<div id="chart-container"></div>
|
346
|
-
<script>
|
347
|
-
var figData = {fig_json};
|
348
|
-
Plotly.newPlot('chart-container', figData.data, figData.layout);
|
349
|
-
</script>
|
350
|
-
</body>
|
351
|
-
</html>
|
352
|
-
"""
|
244
|
+
fig_json = fig.to_json()
|
353
245
|
|
246
|
+
template = jinja_env.get_template("price-single.j2")
|
247
|
+
html_content = template.render(
|
248
|
+
fig_json=fig_json,
|
249
|
+
stats=stats,
|
250
|
+
change_pct=abs(stats["change_pct"]),
|
251
|
+
interval_hours=interval_hours,
|
252
|
+
title=title,
|
253
|
+
)
|
354
254
|
return html_content
|
355
255
|
|
356
256
|
|
357
|
-
def _generate_html_multi_subnet(
|
257
|
+
def _generate_html_multi_subnet(
|
258
|
+
subnet_data, block_numbers, interval_hours, log_scale, title: str
|
259
|
+
):
|
358
260
|
"""
|
359
261
|
Generate an HTML chart for multiple subnets.
|
360
262
|
"""
|
@@ -573,143 +475,17 @@ def _generate_html_multi_subnet(subnet_data, block_numbers, interval_hours, log_
|
|
573
475
|
}
|
574
476
|
|
575
477
|
fig_json = fig.to_json()
|
576
|
-
all_visibility_json = json.dumps(all_visibility)
|
577
|
-
all_annotations_json = json.dumps(all_annotations)
|
578
|
-
|
579
|
-
subnet_modes_json = {}
|
580
|
-
for netuid, mode_data in subnet_modes.items():
|
581
|
-
subnet_modes_json[netuid] = {
|
582
|
-
"visible": json.dumps(mode_data["visible"]),
|
583
|
-
"annotations": json.dumps(mode_data["annotations"]),
|
584
|
-
}
|
585
478
|
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
479
|
+
template = jinja_env.get_template("price-multi.j2")
|
480
|
+
html_content = template.render(
|
481
|
+
title=title,
|
482
|
+
# We sort netuids by market cap but for buttons, they are ordered by netuid
|
483
|
+
sorted_subnet_keys=sorted(subnet_data.keys()),
|
484
|
+
fig_json=fig_json,
|
485
|
+
all_visibility=all_visibility,
|
486
|
+
all_annotations=all_annotations,
|
487
|
+
subnet_modes=subnet_modes,
|
590
488
|
)
|
591
|
-
subnet_buttons_html = ""
|
592
|
-
for netuid in sorted_subnet_keys:
|
593
|
-
subnet_buttons_html += f'<button class="subnet-button" onclick="setSubnet({netuid})">S{netuid}</button> '
|
594
|
-
|
595
|
-
html_content = f"""
|
596
|
-
<!DOCTYPE html>
|
597
|
-
<html>
|
598
|
-
<head>
|
599
|
-
<title>Multi-Subnet Price Chart</title>
|
600
|
-
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
601
|
-
<style>
|
602
|
-
body {{
|
603
|
-
background-color: #000;
|
604
|
-
color: #fff;
|
605
|
-
font-family: Arial, sans-serif;
|
606
|
-
margin: 0;
|
607
|
-
padding: 20px;
|
608
|
-
}}
|
609
|
-
.container {{
|
610
|
-
display: flex;
|
611
|
-
flex-direction: column;
|
612
|
-
gap: 60px;
|
613
|
-
}}
|
614
|
-
#multi-subnet-chart {{
|
615
|
-
width: 90vw;
|
616
|
-
height: 70vh;
|
617
|
-
margin-bottom: 40px;
|
618
|
-
}}
|
619
|
-
.subnet-buttons {{
|
620
|
-
display: grid;
|
621
|
-
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
622
|
-
gap: 8px;
|
623
|
-
max-height: 120px;
|
624
|
-
overflow-y: auto;
|
625
|
-
padding-right: 10px;
|
626
|
-
margin-top: 50px;
|
627
|
-
border-top: 1px solid rgba(255,255,255,0.1);
|
628
|
-
padding-top: 50px;
|
629
|
-
position: relative;
|
630
|
-
bottom: 0;
|
631
|
-
}}
|
632
|
-
.subnet-buttons::-webkit-scrollbar {{
|
633
|
-
width: 8px;
|
634
|
-
}}
|
635
|
-
.subnet-buttons::-webkit-scrollbar-track {{
|
636
|
-
background: rgba(50,50,50,0.3);
|
637
|
-
border-radius: 4px;
|
638
|
-
}}
|
639
|
-
.subnet-buttons::-webkit-scrollbar-thumb {{
|
640
|
-
background: rgba(100,100,100,0.8);
|
641
|
-
border-radius: 4px;
|
642
|
-
}}
|
643
|
-
.subnet-button {{
|
644
|
-
background-color: rgba(50,50,50,0.8);
|
645
|
-
border: 1px solid rgba(70,70,70,0.9);
|
646
|
-
color: white;
|
647
|
-
padding: 8px 16px;
|
648
|
-
cursor: pointer;
|
649
|
-
border-radius: 4px;
|
650
|
-
font-size: 14px;
|
651
|
-
transition: background-color 0.2s;
|
652
|
-
white-space: nowrap;
|
653
|
-
overflow: hidden;
|
654
|
-
text-overflow: ellipsis;
|
655
|
-
}}
|
656
|
-
.subnet-button:hover {{
|
657
|
-
background-color: rgba(70,70,70,0.9);
|
658
|
-
}}
|
659
|
-
.subnet-button.active {{
|
660
|
-
background-color: rgba(100,100,100,0.9);
|
661
|
-
border-color: rgba(120,120,120,1);
|
662
|
-
}}
|
663
|
-
</style>
|
664
|
-
</head>
|
665
|
-
<body>
|
666
|
-
<div class="container">
|
667
|
-
<div id="multi-subnet-chart"></div>
|
668
|
-
<div class="subnet-buttons">
|
669
|
-
{all_button_html}
|
670
|
-
{subnet_buttons_html}
|
671
|
-
</div>
|
672
|
-
</div>
|
673
|
-
<script>
|
674
|
-
var figData = {fig_json};
|
675
|
-
var allVisibility = {all_visibility_json};
|
676
|
-
var allAnnotations = {all_annotations_json};
|
677
|
-
|
678
|
-
var subnetModes = {json.dumps(subnet_modes_json)};
|
679
|
-
// parse back to arrays/objects
|
680
|
-
for (var netuid in subnetModes) {{
|
681
|
-
subnetModes[netuid].visible = JSON.parse(subnetModes[netuid].visible);
|
682
|
-
subnetModes[netuid].annotations = JSON.parse(subnetModes[netuid].annotations);
|
683
|
-
}}
|
684
|
-
|
685
|
-
Plotly.newPlot('multi-subnet-chart', figData.data, figData.layout);
|
686
|
-
|
687
|
-
function clearActiveButtons() {{
|
688
|
-
document.querySelectorAll('.subnet-button').forEach(btn => btn.classList.remove('active'));
|
689
|
-
}}
|
690
|
-
|
691
|
-
function setAll() {{
|
692
|
-
clearActiveButtons();
|
693
|
-
event.currentTarget.classList.add('active');
|
694
|
-
Plotly.update('multi-subnet-chart',
|
695
|
-
{{visible: allVisibility}},
|
696
|
-
{{annotations: allAnnotations}}
|
697
|
-
);
|
698
|
-
}}
|
699
|
-
|
700
|
-
function setSubnet(netuid) {{
|
701
|
-
clearActiveButtons();
|
702
|
-
event.currentTarget.classList.add('active');
|
703
|
-
var mode = subnetModes[netuid];
|
704
|
-
Plotly.update('multi-subnet-chart',
|
705
|
-
{{visible: mode.visible}},
|
706
|
-
{{annotations: mode.annotations}}
|
707
|
-
);
|
708
|
-
}}
|
709
|
-
</script>
|
710
|
-
</body>
|
711
|
-
</html>
|
712
|
-
"""
|
713
489
|
return html_content
|
714
490
|
|
715
491
|
|
@@ -720,7 +496,7 @@ async def _generate_html_output(
|
|
720
496
|
log_scale: bool = False,
|
721
497
|
):
|
722
498
|
"""
|
723
|
-
|
499
|
+
Display HTML output in browser
|
724
500
|
"""
|
725
501
|
try:
|
726
502
|
subnet_keys = list(subnet_data.keys())
|
@@ -730,42 +506,34 @@ async def _generate_html_output(
|
|
730
506
|
netuid = subnet_keys[0]
|
731
507
|
data = subnet_data[netuid]
|
732
508
|
html_content = _generate_html_single_subnet(
|
733
|
-
netuid,
|
509
|
+
netuid,
|
510
|
+
data,
|
511
|
+
block_numbers,
|
512
|
+
interval_hours,
|
513
|
+
log_scale,
|
514
|
+
title=f"Subnet {netuid} Price View",
|
734
515
|
)
|
735
|
-
|
516
|
+
|
736
517
|
else:
|
737
518
|
# Multi-subnet
|
738
519
|
html_content = _generate_html_multi_subnet(
|
739
|
-
subnet_data,
|
520
|
+
subnet_data,
|
521
|
+
block_numbers,
|
522
|
+
interval_hours,
|
523
|
+
log_scale,
|
524
|
+
title="Subnets Price Chart",
|
740
525
|
)
|
741
|
-
|
526
|
+
|
742
527
|
console.print(
|
743
|
-
"[dark_sea_green3]Opening price chart in a window.
|
744
|
-
)
|
745
|
-
handler = PyWry()
|
746
|
-
handler.send_html(
|
747
|
-
html=html_content,
|
748
|
-
title=title,
|
749
|
-
width=1200,
|
750
|
-
height=800,
|
528
|
+
"[dark_sea_green3]Opening price chart in a window.[/dark_sea_green3]"
|
751
529
|
)
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
break
|
760
|
-
await asyncio.sleep(1)
|
761
|
-
except KeyboardInterrupt:
|
762
|
-
pass
|
763
|
-
finally:
|
764
|
-
if not _has_exited(handler):
|
765
|
-
try:
|
766
|
-
handler.close()
|
767
|
-
except Exception:
|
768
|
-
pass
|
530
|
+
with tempfile.NamedTemporaryFile(
|
531
|
+
"w", delete=False, suffix=".html"
|
532
|
+
) as dashboard_file:
|
533
|
+
url = f"file://{dashboard_file.name}"
|
534
|
+
dashboard_file.write(html_content)
|
535
|
+
|
536
|
+
webbrowser.open(url, new=1)
|
769
537
|
except Exception as e:
|
770
538
|
print_error(f"Error generating price chart: {e}")
|
771
539
|
|
@@ -858,12 +626,3 @@ def _generate_cli_output(subnet_data, block_numbers, interval_hours, log_scale):
|
|
858
626
|
)
|
859
627
|
|
860
628
|
console.print(stats_text)
|
861
|
-
|
862
|
-
|
863
|
-
def _has_exited(handler) -> bool:
|
864
|
-
"""Check if PyWry process has cleanly exited with returncode 0."""
|
865
|
-
return (
|
866
|
-
hasattr(handler, "runner")
|
867
|
-
and handler.runner is not None
|
868
|
-
and handler.runner.returncode == 0
|
869
|
-
)
|
@@ -525,7 +525,8 @@ async def set_take_extrinsic(
|
|
525
525
|
|
526
526
|
if current_take_u16 < take_u16:
|
527
527
|
console.print(
|
528
|
-
f"Current take is [{COLOR_PALETTE
|
528
|
+
f"Current take is [{COLOR_PALETTE.P.RATE}]{current_take * 100.0:.2f}%[/{COLOR_PALETTE.P.RATE}]. "
|
529
|
+
f"Increasing to [{COLOR_PALETTE.P.RATE}]{take * 100:.2f}%."
|
529
530
|
)
|
530
531
|
with console.status(
|
531
532
|
f":satellite: Sending decrease_take_extrinsic call on [white]{subtensor}[/white] ..."
|
@@ -542,7 +543,8 @@ async def set_take_extrinsic(
|
|
542
543
|
|
543
544
|
else:
|
544
545
|
console.print(
|
545
|
-
f"Current take is [{COLOR_PALETTE
|
546
|
+
f"Current take is [{COLOR_PALETTE.P.RATE}]{current_take * 100.0:.2f}%[/{COLOR_PALETTE.P.RATE}]. "
|
547
|
+
f"Decreasing to [{COLOR_PALETTE.P.RATE}]{take * 100:.2f}%."
|
546
548
|
)
|
547
549
|
with console.status(
|
548
550
|
f":satellite: Sending increase_take_extrinsic call on [white]{subtensor}[/white] ..."
|
@@ -871,7 +873,7 @@ async def get_current_take(subtensor: "SubtensorInterface", wallet: Wallet):
|
|
871
873
|
async def display_current_take(subtensor: "SubtensorInterface", wallet: Wallet) -> None:
|
872
874
|
current_take = await get_current_take(subtensor, wallet)
|
873
875
|
console.print(
|
874
|
-
f"Current take is [{COLOR_PALETTE
|
876
|
+
f"Current take is [{COLOR_PALETTE.P.RATE}]{current_take * 100.0:.2f}%"
|
875
877
|
)
|
876
878
|
|
877
879
|
|
@@ -891,7 +893,9 @@ async def set_take(
|
|
891
893
|
)
|
892
894
|
if not len(netuids_registered) > 0:
|
893
895
|
err_console.print(
|
894
|
-
f"Hotkey [{COLOR_PALETTE
|
896
|
+
f"Hotkey [{COLOR_PALETTE.G.HK}]{wallet.hotkey.ss58_address}[/{COLOR_PALETTE.G.HK}] is not registered to"
|
897
|
+
f" any subnet. Please register using [{COLOR_PALETTE.G.SUBHEAD}]`btcli subnets register`"
|
898
|
+
f"[{COLOR_PALETTE.G.SUBHEAD}] and try again."
|
895
899
|
)
|
896
900
|
return False
|
897
901
|
|
@@ -908,12 +912,12 @@ async def set_take(
|
|
908
912
|
else:
|
909
913
|
new_take = await get_current_take(subtensor, wallet)
|
910
914
|
console.print(
|
911
|
-
f"New take is [{COLOR_PALETTE
|
915
|
+
f"New take is [{COLOR_PALETTE.P.RATE}]{new_take * 100.0:.2f}%"
|
912
916
|
)
|
913
917
|
return True
|
914
918
|
|
915
919
|
console.print(
|
916
|
-
f"Setting take on [{COLOR_PALETTE
|
920
|
+
f"Setting take on [{COLOR_PALETTE.G.LINKS}]network: {subtensor.network}"
|
917
921
|
)
|
918
922
|
|
919
923
|
if not unlock_key(wallet, "hot").success and unlock_key(wallet, "cold").success:
|