bittensor-cli 9.10.2__py3-none-any.whl → 9.11.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 CHANGED
@@ -4,6 +4,7 @@ import copy
4
4
  import curses
5
5
  import importlib
6
6
  import json
7
+ import logging
7
8
  import os.path
8
9
  import re
9
10
  import ssl
@@ -12,7 +13,7 @@ import traceback
12
13
  import warnings
13
14
  from dataclasses import fields
14
15
  from pathlib import Path
15
- from typing import Coroutine, Optional, Union
16
+ from typing import Coroutine, Optional, Union, Literal
16
17
 
17
18
  import numpy as np
18
19
  import rich
@@ -42,7 +43,10 @@ from bittensor_cli.src import (
42
43
  from bittensor_cli.src.bittensor import utils
43
44
  from bittensor_cli.src.bittensor.balances import Balance
44
45
  from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters
45
- from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
46
+ from bittensor_cli.src.bittensor.subtensor_interface import (
47
+ SubtensorInterface,
48
+ best_connection,
49
+ )
46
50
  from bittensor_cli.src.bittensor.utils import (
47
51
  console,
48
52
  err_console,
@@ -86,11 +90,19 @@ except ImportError:
86
90
  pass
87
91
 
88
92
 
93
+ logger = logging.getLogger("btcli")
89
94
  _epilog = "Made with [bold red]:heart:[/bold red] by The Openτensor Foundaτion"
90
95
 
91
96
  np.set_printoptions(precision=8, suppress=True, floatmode="fixed")
92
97
 
93
98
 
99
+ def arg__(arg_name: str) -> str:
100
+ """
101
+ Helper function to 'arg' format a string for rich console
102
+ """
103
+ return f"[{COLORS.G.ARG}]{arg_name}[/{COLORS.G.ARG}]"
104
+
105
+
94
106
  class Options:
95
107
  """
96
108
  Re-usable typer args
@@ -592,6 +604,42 @@ def commands_callback(value: bool):
592
604
  if value:
593
605
  cli = CLIManager()
594
606
  console.print(cli.generate_command_tree())
607
+
608
+
609
+ def debug_callback(value: bool):
610
+ if value:
611
+ debug_file_loc = Path(
612
+ os.getenv("BTCLI_DEBUG_FILE")
613
+ or os.path.expanduser(defaults.config.debug_file_path)
614
+ )
615
+ if not debug_file_loc.exists():
616
+ err_console.print(
617
+ f"[red]Error: The debug file '{arg__(str(debug_file_loc))}' does not exist. This indicates that you have"
618
+ f" not run a command which has logged debug output, or you deleted this file. Debug logging only occurs"
619
+ f" if {arg__('use_cache')} is set to True in your config ({arg__('btcli config set')}). If the debug "
620
+ f"file was created using the {arg__('BTCLI_DEBUG_FILE')} environment variable, please set the value for"
621
+ f" the same location, and re-run this {arg__('btcli --debug')} command.[/red]"
622
+ )
623
+ raise typer.Exit()
624
+ save_file_loc_ = Prompt.ask(
625
+ "Enter the file location to save the debug log for the previous command.",
626
+ default="~/.bittensor/debug-export",
627
+ ).strip()
628
+ save_file_loc = Path(os.path.expanduser(save_file_loc_))
629
+ if not save_file_loc.parent.exists():
630
+ if Confirm.ask(
631
+ f"The directory '{save_file_loc.parent}' does not exist. Would you like to create it?"
632
+ ):
633
+ save_file_loc.parent.mkdir(parents=True, exist_ok=True)
634
+ try:
635
+ with (
636
+ open(save_file_loc, "w+") as save_file,
637
+ open(debug_file_loc, "r") as current_file,
638
+ ):
639
+ save_file.write(current_file.read())
640
+ console.print(f"Saved debug log to {save_file_loc}")
641
+ except FileNotFoundError as e:
642
+ print_error(str(e))
595
643
  raise typer.Exit()
596
644
 
597
645
 
@@ -612,7 +660,7 @@ class CLIManager:
612
660
  wallet_app: typer.Typer
613
661
  subnets_app: typer.Typer
614
662
  weights_app: typer.Typer
615
- utils_app = typer.Typer(epilog=_epilog)
663
+ utils_app: typer.Typer
616
664
  view_app: typer.Typer
617
665
  asyncio_runner = asyncio
618
666
 
@@ -623,6 +671,7 @@ class CLIManager:
623
671
  "wallet_hotkey": None,
624
672
  "network": None,
625
673
  "use_cache": True,
674
+ "disk_cache": False,
626
675
  "rate_tolerance": None,
627
676
  "safe_staking": True,
628
677
  "allow_partial_stake": False,
@@ -664,6 +713,9 @@ class CLIManager:
664
713
  self.config_path = os.getenv("BTCLI_CONFIG_PATH") or os.path.expanduser(
665
714
  defaults.config.path
666
715
  )
716
+ self.debug_file_path = os.getenv("BTCLI_DEBUG_FILE") or os.path.expanduser(
717
+ defaults.config.debug_file_path
718
+ )
667
719
 
668
720
  self.app = typer.Typer(
669
721
  rich_markup_mode="rich",
@@ -674,8 +726,8 @@ class CLIManager:
674
726
  self.config_app = typer.Typer(
675
727
  epilog=_epilog,
676
728
  help=f"Allows for getting/setting the config. "
677
- f"Default path for the config file is [{COLORS.G.ARG}]{defaults.config.path}[/{COLORS.G.ARG}]. "
678
- f"You can set your own with the env var [{COLORS.G.ARG}]BTCLI_CONFIG_PATH[/{COLORS.G.ARG}]",
729
+ f"Default path for the config file is {arg__(defaults.config.path)}. "
730
+ f"You can set your own with the env var {arg__('BTCLI_CONFIG_PATH')}",
679
731
  )
680
732
  self.wallet_app = typer.Typer(epilog=_epilog)
681
733
  self.stake_app = typer.Typer(epilog=_epilog)
@@ -684,6 +736,7 @@ class CLIManager:
684
736
  self.weights_app = typer.Typer(epilog=_epilog)
685
737
  self.view_app = typer.Typer(epilog=_epilog)
686
738
  self.liquidity_app = typer.Typer(epilog=_epilog)
739
+ self.utils_app = typer.Typer(epilog=_epilog)
687
740
 
688
741
  # config alias
689
742
  self.app.add_typer(
@@ -758,7 +811,7 @@ class CLIManager:
758
811
 
759
812
  # utils app
760
813
  self.app.add_typer(
761
- self.utils_app, name="utils", no_args_is_help=True, hidden=True
814
+ self.utils_app, name="utils", no_args_is_help=True, hidden=False
762
815
  )
763
816
 
764
817
  # view app
@@ -948,6 +1001,9 @@ class CLIManager:
948
1001
  self.subnets_app.command(
949
1002
  "check-start", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
950
1003
  )(self.subnets_check_start)
1004
+ self.subnets_app.command(
1005
+ "set-symbol", rich_help_panel=HELP_PANELS["SUBNETS"]["IDENTITY"]
1006
+ )(self.subnets_set_symbol)
951
1007
 
952
1008
  # weights commands
953
1009
  self.weights_app.command(
@@ -1040,6 +1096,10 @@ class CLIManager:
1040
1096
  "remove", rich_help_panel=HELP_PANELS["LIQUIDITY"]["LIQUIDITY_MGMT"]
1041
1097
  )(self.liquidity_remove)
1042
1098
 
1099
+ # utils app
1100
+ self.utils_app.command("convert")(self.convert)
1101
+ self.utils_app.command("latency")(self.best_connection)
1102
+
1043
1103
  def generate_command_tree(self) -> Tree:
1044
1104
  """
1045
1105
  Generates a rich.Tree of the commands, subcommands, and groups of this app
@@ -1092,6 +1152,7 @@ class CLIManager:
1092
1152
  "Verify this is intended.",
1093
1153
  )
1094
1154
  if not self.subtensor:
1155
+ use_disk_cache = self.config.get("disk_cache", False)
1095
1156
  if network:
1096
1157
  network_ = None
1097
1158
  for item in network:
@@ -1105,18 +1166,24 @@ class CLIManager:
1105
1166
  if not_selected_networks:
1106
1167
  console.print(
1107
1168
  f"Networks not selected: "
1108
- f"[{COLORS.G.ARG}]{', '.join(not_selected_networks)}[/{COLORS.G.ARG}]"
1169
+ f"{arg__(', '.join(not_selected_networks))}"
1109
1170
  )
1110
1171
 
1111
- self.subtensor = SubtensorInterface(network_)
1172
+ self.subtensor = SubtensorInterface(
1173
+ network_, use_disk_cache=use_disk_cache
1174
+ )
1112
1175
  elif self.config["network"]:
1113
1176
  console.print(
1114
1177
  f"Using the specified network [{COLORS.G.LINKS}]{self.config['network']}"
1115
1178
  f"[/{COLORS.G.LINKS}] from config"
1116
1179
  )
1117
- self.subtensor = SubtensorInterface(self.config["network"])
1180
+ self.subtensor = SubtensorInterface(
1181
+ self.config["network"], use_disk_cache=use_disk_cache
1182
+ )
1118
1183
  else:
1119
- self.subtensor = SubtensorInterface(defaults.subtensor.network)
1184
+ self.subtensor = SubtensorInterface(
1185
+ defaults.subtensor.network, use_disk_cache=use_disk_cache
1186
+ )
1120
1187
  return self.subtensor
1121
1188
 
1122
1189
  def _run_command(self, cmd: Coroutine, exit_early: bool = True):
@@ -1179,10 +1246,18 @@ class CLIManager:
1179
1246
  "--commands", callback=commands_callback, help="Show BTCLI commands"
1180
1247
  ),
1181
1248
  ] = None,
1249
+ debug_log: Annotated[
1250
+ Optional[bool],
1251
+ typer.Option(
1252
+ "--debug",
1253
+ callback=debug_callback,
1254
+ help="Saves the debug log from the last used command",
1255
+ ),
1256
+ ] = None,
1182
1257
  ):
1183
1258
  """
1184
1259
  Command line interface (CLI) for Bittensor. Uses the values in the configuration file. These values can be
1185
- overriden by passing them explicitly in the command line.
1260
+ overridden by passing them explicitly in the command line.
1186
1261
  """
1187
1262
  # Load or create the config file
1188
1263
  if os.path.exists(self.config_path):
@@ -1206,6 +1281,9 @@ class CLIManager:
1206
1281
  if sub_key not in config[key]:
1207
1282
  config[key][sub_key] = sub_value
1208
1283
  updated = True
1284
+ elif isinstance(value, bool) and config[key] is None:
1285
+ config[key] = value
1286
+ updated = True
1209
1287
  if updated:
1210
1288
  with open(self.config_path, "w") as f:
1211
1289
  safe_dump(config, f)
@@ -1213,6 +1291,27 @@ class CLIManager:
1213
1291
  for k, v in config.items():
1214
1292
  if k in self.config.keys():
1215
1293
  self.config[k] = v
1294
+ if self.config.get("use_cache", False):
1295
+ with open(self.debug_file_path, "w+") as f:
1296
+ f.write(
1297
+ f"BTCLI {__version__}\n"
1298
+ f"Async-Substrate-Interface: {importlib.metadata.version('async-substrate-interface')}\n"
1299
+ f"Bittensor-Wallet: {importlib.metadata.version('bittensor-wallet')}\n"
1300
+ f"Command: {' '.join(sys.argv)}\n"
1301
+ f"Config: {self.config}\n"
1302
+ f"Python: {sys.version}\n"
1303
+ f"System: {sys.platform}\n\n"
1304
+ )
1305
+ asi_logger = logging.getLogger("async_substrate_interface")
1306
+ asi_logger.setLevel(logging.DEBUG)
1307
+ logger.setLevel(logging.DEBUG)
1308
+ formatter = logging.Formatter(
1309
+ "%(asctime)s - %(levelname)s - %(name)s - %(module)s:%(lineno)d - %(message)s"
1310
+ )
1311
+ handler = logging.FileHandler(self.debug_file_path)
1312
+ handler.setFormatter(formatter)
1313
+ asi_logger.addHandler(handler)
1314
+ logger.addHandler(handler)
1216
1315
 
1217
1316
  def verbosity_handler(
1218
1317
  self, quiet: bool, verbose: bool, json_output: bool = False
@@ -1273,6 +1372,13 @@ class CLIManager:
1273
1372
  help="Disable caching of some commands. This will disable the `--reuse-last` and `--html` flags on "
1274
1373
  "commands such as `subnets metagraph`, `stake show` and `subnets list`.",
1275
1374
  ),
1375
+ disk_cache: Optional[bool] = typer.Option(
1376
+ None,
1377
+ "--disk-cache/--no-disk-cache",
1378
+ " /--no-disk-cache",
1379
+ help="Enables or disables the caching on disk. Enabling this can significantly speed up commands run "
1380
+ "sequentially",
1381
+ ),
1276
1382
  rate_tolerance: Optional[float] = typer.Option(
1277
1383
  None,
1278
1384
  "--tolerance",
@@ -1319,12 +1425,13 @@ class CLIManager:
1319
1425
  "wallet_hotkey": wallet_hotkey,
1320
1426
  "network": network,
1321
1427
  "use_cache": use_cache,
1428
+ "disk_cache": disk_cache,
1322
1429
  "rate_tolerance": rate_tolerance,
1323
1430
  "safe_staking": safe_staking,
1324
1431
  "allow_partial_stake": allow_partial_stake,
1325
1432
  "dashboard_path": dashboard_path,
1326
1433
  }
1327
- bools = ["use_cache", "safe_staking", "allow_partial_stake"]
1434
+ bools = ["use_cache", "disk_cache", "safe_staking", "allow_partial_stake"]
1328
1435
  if all(v is None for v in args.values()):
1329
1436
  # Print existing configs
1330
1437
  self.get_config()
@@ -1373,8 +1480,7 @@ class CLIManager:
1373
1480
  if n := args.get("network"):
1374
1481
  if n in Constants.networks:
1375
1482
  if not Confirm.ask(
1376
- f"You provided a network [{COLORS.G.ARG}]{n}[/{COLORS.G.ARG}] which is mapped to "
1377
- f"[{COLORS.G.ARG}]{Constants.network_map[n]}[/{COLORS.G.ARG}]\n"
1483
+ f"You provided a network {arg__(n)} which is mapped to {arg__(Constants.network_map[n])}\n"
1378
1484
  "Do you want to continue?"
1379
1485
  ):
1380
1486
  typer.Exit()
@@ -1389,14 +1495,13 @@ class CLIManager:
1389
1495
  )
1390
1496
  args["network"] = known_network
1391
1497
  if not Confirm.ask(
1392
- f"You provided an endpoint [{COLORS.G.ARG}]{n}[/{COLORS.G.ARG}] which is mapped to "
1393
- f"[{COLORS.G.ARG}]{known_network}[/{COLORS.G.ARG}]\n"
1498
+ f"You provided an endpoint {arg__(n)} which is mapped to {arg__(known_network)}\n"
1394
1499
  "Do you want to continue?"
1395
1500
  ):
1396
1501
  raise typer.Exit()
1397
1502
  else:
1398
1503
  if not Confirm.ask(
1399
- f"You provided a chain endpoint URL [{COLORS.G.ARG}]{n}[/{COLORS.G.ARG}]\n"
1504
+ f"You provided a chain endpoint URL {arg__(n)}\n"
1400
1505
  "Do you want to continue?"
1401
1506
  ):
1402
1507
  raise typer.Exit()
@@ -1406,6 +1511,7 @@ class CLIManager:
1406
1511
 
1407
1512
  for arg, val in args.items():
1408
1513
  if val is not None:
1514
+ logger.debug(f"Config: setting {arg} to {val}")
1409
1515
  self.config[arg] = val
1410
1516
  with open(self.config_path, "w") as f:
1411
1517
  safe_dump(self.config, f)
@@ -1470,17 +1576,12 @@ class CLIManager:
1470
1576
  if not any(args.values()):
1471
1577
  for arg in args.keys():
1472
1578
  if self.config.get(arg) is not None:
1473
- if Confirm.ask(
1474
- f"Do you want to clear the [{COLORS.G.ARG}]{arg}[/{COLORS.G.ARG}] config?"
1475
- ):
1579
+ if Confirm.ask(f"Do you want to clear the {arg__(arg)} config?"):
1580
+ logger.debug(f"Config: clearing {arg}.")
1476
1581
  self.config[arg] = None
1477
- console.print(
1478
- f"Cleared [{COLORS.G.ARG}]{arg}[/{COLORS.G.ARG}] config and set to 'None'."
1479
- )
1582
+ console.print(f"Cleared {arg__(arg)} config and set to 'None'.")
1480
1583
  else:
1481
- console.print(
1482
- f"Skipped clearing [{COLORS.G.ARG}]{arg}[/{COLORS.G.ARG}] config."
1483
- )
1584
+ console.print(f"Skipped clearing {arg__(arg)} config.")
1484
1585
 
1485
1586
  else:
1486
1587
  # Check each specified argument
@@ -1488,21 +1589,19 @@ class CLIManager:
1488
1589
  if should_clear:
1489
1590
  if self.config.get(arg) is not None:
1490
1591
  if Confirm.ask(
1491
- f"Do you want to clear the [{COLORS.G.ARG}]{arg}[/{COLORS.G.ARG}]"
1592
+ f"Do you want to clear the {arg__(arg)}"
1492
1593
  f" [bold cyan]({self.config.get(arg)})[/bold cyan] config?"
1493
1594
  ):
1494
1595
  self.config[arg] = None
1596
+ logger.debug(f"Config: clearing {arg}.")
1495
1597
  console.print(
1496
- f"Cleared [{COLORS.G.ARG}]{arg}[/{COLORS.G.ARG}] config and set to 'None'."
1598
+ f"Cleared {arg__(arg)} config and set to 'None'."
1497
1599
  )
1498
1600
  else:
1499
- console.print(
1500
- f"Skipped clearing [{COLORS.G.ARG}]{arg}[/{COLORS.G.ARG}] config."
1501
- )
1601
+ console.print(f"Skipped clearing {arg__(arg)} config.")
1502
1602
  else:
1503
1603
  console.print(
1504
- f"No config set for [{COLORS.G.ARG}]{arg}[/{COLORS.G.ARG}]."
1505
- f" Use [{COLORS.G.ARG}]`btcli config set`[/{COLORS.G.ARG}] to set it."
1604
+ f"No config set for {arg__(arg)}. Use {arg__('btcli config set')} to set it."
1506
1605
  )
1507
1606
  with open(self.config_path, "w") as f:
1508
1607
  safe_dump(self.config, f)
@@ -1518,8 +1617,7 @@ class CLIManager:
1518
1617
  Column("[bold white]Value", style="gold1"),
1519
1618
  Column("", style="medium_purple"),
1520
1619
  box=box.SIMPLE_HEAD,
1521
- title=f"[{COLORS.G.HEADER}]BTCLI Config[/{COLORS.G.HEADER}]: "
1522
- f"[{COLORS.G.ARG}]{self.config_path}[/{COLORS.G.ARG}]",
1620
+ title=f"[{COLORS.G.HEADER}]BTCLI Config[/{COLORS.G.HEADER}]: {arg__(self.config_path)}",
1523
1621
  )
1524
1622
 
1525
1623
  for key, value in self.config.items():
@@ -1593,26 +1691,31 @@ class CLIManager:
1593
1691
  bool: Safe staking setting
1594
1692
  """
1595
1693
  if safe_staking is not None:
1694
+ enabled = "enabled" if safe_staking else "disabled"
1596
1695
  console.print(
1597
- f"[dim][blue]Safe staking[/blue]: [bold cyan]{'enabled' if safe_staking else 'disabled'}[/bold cyan]."
1696
+ f"[dim][blue]Safe staking[/blue]: [bold cyan]{enabled}[/bold cyan]."
1598
1697
  )
1698
+ logger.debug(f"Safe staking {enabled}")
1599
1699
  return safe_staking
1600
1700
  elif self.config.get("safe_staking") is not None:
1601
1701
  safe_staking = self.config["safe_staking"]
1702
+ enabled = "enabled" if safe_staking else "disabled"
1602
1703
  console.print(
1603
- f"[dim][blue]Safe staking[/blue]: [bold cyan]{'enabled' if safe_staking else 'disabled'}[/bold cyan] (from config)."
1704
+ f"[dim][blue]Safe staking[/blue]: [bold cyan]{enabled}[/bold cyan] (from config)."
1604
1705
  )
1706
+ logger.debug(f"Safe staking {enabled}")
1605
1707
  return safe_staking
1606
1708
  else:
1607
1709
  safe_staking = True
1608
1710
  console.print(
1609
1711
  "[dim][blue]Safe staking[/blue]: "
1610
- + f"[bold cyan]{'enabled' if safe_staking else 'disabled'}[/bold cyan] "
1611
- + "by default. Set this using "
1612
- + "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
1613
- + "or "
1614
- + "[dark_sea_green3 italic]`--safe/--unsafe`[/dark_sea_green3 italic] flag[/dim]"
1712
+ f"[bold cyan]enabled[/bold cyan] "
1713
+ "by default. Set this using "
1714
+ "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
1715
+ "or "
1716
+ "[dark_sea_green3 italic]`--safe/--unsafe`[/dark_sea_green3 italic] flag[/dim]"
1615
1717
  )
1718
+ logger.debug(f"Safe staking enabled.")
1616
1719
  return safe_staking
1617
1720
 
1618
1721
  def ask_partial_stake(
@@ -1629,25 +1732,31 @@ class CLIManager:
1629
1732
  bool: Partial stake setting
1630
1733
  """
1631
1734
  if allow_partial_stake is not None:
1735
+ partial_staking = "enabled" if allow_partial_stake else "disabled"
1632
1736
  console.print(
1633
- f"[dim][blue]Partial staking[/blue]: [bold cyan]{'enabled' if allow_partial_stake else 'disabled'}[/bold cyan]."
1737
+ f"[dim][blue]Partial staking[/blue]: [bold cyan]{partial_staking}[/bold cyan]."
1634
1738
  )
1739
+ logger.debug(f"Partial staking {partial_staking}")
1635
1740
  return allow_partial_stake
1636
1741
  elif self.config.get("allow_partial_stake") is not None:
1637
1742
  config_partial = self.config["allow_partial_stake"]
1743
+ partial_staking = "enabled" if allow_partial_stake else "disabled"
1638
1744
  console.print(
1639
- f"[dim][blue]Partial staking[/blue]: [bold cyan]{'enabled' if config_partial else 'disabled'}[/bold cyan] (from config)."
1745
+ f"[dim][blue]Partial staking[/blue]: [bold cyan]{partial_staking}[/bold cyan] (from config)."
1640
1746
  )
1747
+ logger.debug(f"Partial staking {partial_staking}")
1641
1748
  return config_partial
1642
1749
  else:
1750
+ partial_staking = "enabled" if allow_partial_stake else "disabled"
1643
1751
  console.print(
1644
1752
  "[dim][blue]Partial staking[/blue]: "
1645
- + f"[bold cyan]{'enabled' if allow_partial_stake else 'disabled'}[/bold cyan] "
1646
- + "by default. Set this using "
1647
- + "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
1648
- + "or "
1649
- + "[dark_sea_green3 italic]`--partial/--no-partial`[/dark_sea_green3 italic] flag[/dim]"
1753
+ f"[bold cyan]{partial_staking}[/bold cyan] "
1754
+ "by default. Set this using "
1755
+ "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
1756
+ "or "
1757
+ "[dark_sea_green3 italic]`--partial/--no-partial`[/dark_sea_green3 italic] flag[/dim]"
1650
1758
  )
1759
+ logger.debug(f"Partial staking {partial_staking}")
1651
1760
  return False
1652
1761
 
1653
1762
  def wallet_ask(
@@ -1655,7 +1764,7 @@ class CLIManager:
1655
1764
  wallet_name: Optional[str],
1656
1765
  wallet_path: Optional[str],
1657
1766
  wallet_hotkey: Optional[str],
1658
- ask_for: Optional[list[str]] = None,
1767
+ ask_for: Optional[list[Literal[WO.NAME, WO.PATH, WO.HOTKEY]]] = None,
1659
1768
  validate: WV = WV.WALLET,
1660
1769
  return_wallet_and_hotkey: bool = False,
1661
1770
  ) -> Union[Wallet, tuple[Wallet, str]]:
@@ -1720,13 +1829,14 @@ class CLIManager:
1720
1829
  if wallet_path:
1721
1830
  wallet_path = os.path.expanduser(wallet_path)
1722
1831
  wallet = Wallet(name=wallet_name, path=wallet_path, hotkey=wallet_hotkey)
1832
+ logger.debug(f"Using wallet {wallet}")
1723
1833
 
1724
1834
  # Validate the wallet if required
1725
1835
  if validate == WV.WALLET or validate == WV.WALLET_AND_HOTKEY:
1726
1836
  valid = utils.is_valid_wallet(wallet)
1727
1837
  if not valid[0]:
1728
1838
  utils.err_console.print(
1729
- f"[red]Error: Wallet does not not exist. \n"
1839
+ f"[red]Error: Wallet does not exist. \n"
1730
1840
  f"Please verify your wallet information: {wallet}[/red]"
1731
1841
  )
1732
1842
  raise typer.Exit()
@@ -1875,6 +1985,15 @@ class CLIManager:
1875
1985
  str,
1876
1986
  "Hotkeys names must be a comma-separated list, e.g., `--exclude-hotkeys hk1,hk2`.",
1877
1987
  )
1988
+ logger.debug(
1989
+ "args:\n"
1990
+ f"all_wallets: {all_wallets}\n"
1991
+ f"sort_by: {sort_by}\n"
1992
+ f"sort_order: {sort_order}\n"
1993
+ f"include_hotkeys: {include_hotkeys}\n"
1994
+ f"exclude_hotkeys: {exclude_hotkeys}\n"
1995
+ f"netuids: {netuids}\n"
1996
+ )
1878
1997
 
1879
1998
  return self._run_command(
1880
1999
  wallets.overview(
@@ -1964,6 +2083,15 @@ class CLIManager:
1964
2083
  amount = 0
1965
2084
  elif not amount:
1966
2085
  amount = FloatPrompt.ask("Enter amount (in TAO) to transfer.")
2086
+ logger.debug(
2087
+ "args:\n"
2088
+ f"destination: {destination_ss58_address}\n"
2089
+ f"amount: {amount}\n"
2090
+ f"transfer_all: {transfer_all}\n"
2091
+ f"allow_death: {allow_death}\n"
2092
+ f"period: {period}\n"
2093
+ f"prompt: {prompt}\n"
2094
+ )
1967
2095
  return self._run_command(
1968
2096
  wallets.transfer(
1969
2097
  wallet=wallet,
@@ -2033,6 +2161,13 @@ class CLIManager:
2033
2161
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
2034
2162
  validate=WV.WALLET_AND_HOTKEY,
2035
2163
  )
2164
+ logger.debug(
2165
+ "args:\n"
2166
+ f"original_wallet: {original_wallet}\n"
2167
+ f"new_wallet: {new_wallet}\n"
2168
+ f"netuid: {netuid}\n"
2169
+ f"prompt: {prompt}\n"
2170
+ )
2036
2171
  self.initialize_chain(network)
2037
2172
  return self._run_command(
2038
2173
  wallets.swap_hotkey(
@@ -2196,6 +2331,17 @@ class CLIManager:
2196
2331
  ask_for=[WO.NAME, WO.PATH],
2197
2332
  validate=WV.WALLET,
2198
2333
  )
2334
+ logger.debug(
2335
+ "args:\n"
2336
+ f"network {network}\n"
2337
+ f"threads_per_block {threads_per_block}\n"
2338
+ f"update_interval {update_interval}\n"
2339
+ f"processors {processors}\n"
2340
+ f"use_cuda {use_cuda}\n"
2341
+ f"dev_id {dev_id}\n"
2342
+ f"output_in_place {output_in_place}\n"
2343
+ f"max_successes {max_successes}\n"
2344
+ )
2199
2345
  return self._run_command(
2200
2346
  wallets.faucet(
2201
2347
  wallet,
@@ -2263,6 +2409,7 @@ class CLIManager:
2263
2409
  mnemonic, seed, json_path, json_password = get_creation_data(
2264
2410
  mnemonic, seed, json_path, json_password
2265
2411
  )
2412
+ # logger.debug should NOT be used here, it's simply too risky
2266
2413
  return self._run_command(
2267
2414
  wallets.regen_coldkey(
2268
2415
  wallet,
@@ -2332,6 +2479,7 @@ class CLIManager:
2332
2479
  ):
2333
2480
  rich.print("[red]Error: Invalid SS58 address or public key![/red]")
2334
2481
  return
2482
+ # do not logger.debug any creation cmds
2335
2483
  return self._run_command(
2336
2484
  wallets.regen_coldkey_pub(
2337
2485
  wallet, ss58_address, public_key_hex, overwrite, json_output
@@ -2384,6 +2532,7 @@ class CLIManager:
2384
2532
  mnemonic, seed, json_path, json_password = get_creation_data(
2385
2533
  mnemonic, seed, json_path, json_password
2386
2534
  )
2535
+ # do not logger.debug any creation cmds
2387
2536
  return self._run_command(
2388
2537
  wallets.regen_hotkey(
2389
2538
  wallet,
@@ -2453,6 +2602,7 @@ class CLIManager:
2453
2602
  ):
2454
2603
  rich.print("[red]Error: Invalid SS58 address or public key![/red]")
2455
2604
  return False
2605
+ # do not logger.debug any creation cmds
2456
2606
  return self._run_command(
2457
2607
  wallets.regen_hotkey_pub(
2458
2608
  wallet, ss58_address, public_key_hex, overwrite, json_output
@@ -2517,6 +2667,7 @@ class CLIManager:
2517
2667
  )
2518
2668
  if not uri:
2519
2669
  n_words = get_n_words(n_words)
2670
+ # do not logger.debug any creation cmds
2520
2671
  return self._run_command(
2521
2672
  wallets.new_hotkey(
2522
2673
  wallet, n_words, use_password, uri, overwrite, json_output
@@ -2583,7 +2734,13 @@ class CLIManager:
2583
2734
  f"hotkey [blue]{wallet_hotkey}[/blue] "
2584
2735
  f"[{COLORS.GENERAL.HK}]({hotkey_ss58})[/{COLORS.GENERAL.HK}]"
2585
2736
  )
2586
-
2737
+ logger.debug(
2738
+ "args:\n"
2739
+ f"network {network}\n"
2740
+ f"hotkey_ss58 {hotkey_ss58}\n"
2741
+ f"hotkey_display {hotkey_display}\n"
2742
+ f"prompt {prompt}\n"
2743
+ )
2587
2744
  return self._run_command(
2588
2745
  wallets.associate_hotkey(
2589
2746
  wallet,
@@ -2731,7 +2888,8 @@ class CLIManager:
2731
2888
 
2732
2889
  if not scheduled_block:
2733
2890
  block_input = Prompt.ask(
2734
- "[blue]Enter the block number[/blue] where the swap was scheduled [dim](optional, press enter to skip)[/dim]",
2891
+ "[blue]Enter the block number[/blue] where the swap was scheduled "
2892
+ "[dim](optional, press enter to skip)[/dim]",
2735
2893
  default="",
2736
2894
  )
2737
2895
  if block_input:
@@ -2740,7 +2898,12 @@ class CLIManager:
2740
2898
  except ValueError:
2741
2899
  print_error("Invalid block number")
2742
2900
  raise typer.Exit()
2743
-
2901
+ logger.debug(
2902
+ "args:\n"
2903
+ f"scheduled_block {scheduled_block}\n"
2904
+ f"ss58_address {ss58_address}\n"
2905
+ f"network {network}\n"
2906
+ )
2744
2907
  return self._run_command(
2745
2908
  wallets.check_swap_status(self.subtensor, ss58_address, scheduled_block)
2746
2909
  )
@@ -2797,6 +2960,7 @@ class CLIManager:
2797
2960
  )
2798
2961
  if not uri:
2799
2962
  n_words = get_n_words(n_words)
2963
+ # do not logger.debug any creation commands
2800
2964
  return self._run_command(
2801
2965
  wallets.wallet_create(
2802
2966
  wallet, n_words, use_password, uri, overwrite, json_output
@@ -2905,6 +3069,12 @@ class CLIManager:
2905
3069
  ask_for=ask_for,
2906
3070
  validate=validate,
2907
3071
  )
3072
+ logger.debug(
3073
+ "args:\n"
3074
+ f"all_balances {all_balances}\n"
3075
+ f"ss58_addresses {ss58_addresses}\n"
3076
+ f"network {network}"
3077
+ )
2908
3078
  subtensor = self.initialize_chain(network)
2909
3079
  return self._run_command(
2910
3080
  wallets.wallet_balance(
@@ -3055,6 +3225,7 @@ class CLIManager:
3055
3225
  additional,
3056
3226
  github_repo,
3057
3227
  )
3228
+ logger.debug(f"args:\nidentity {identity}\nnetwork {network}\n")
3058
3229
 
3059
3230
  return self._run_command(
3060
3231
  wallets.set_id(
@@ -3316,7 +3487,12 @@ class CLIManager:
3316
3487
  f"[dark_sea_green3]{new_wallet}[/dark_sea_green3]\n"
3317
3488
  )
3318
3489
  new_wallet_coldkey_ss58 = new_wallet.coldkeypub.ss58_address
3319
-
3490
+ logger.debug(
3491
+ "args:\n"
3492
+ f"network {network}\n"
3493
+ f"new_coldkey_ss58 {new_wallet_coldkey_ss58}\n"
3494
+ f"force_swap {force_swap}"
3495
+ )
3320
3496
  return self._run_command(
3321
3497
  wallets.schedule_coldkey_swap(
3322
3498
  wallet=wallet,
@@ -3388,7 +3564,13 @@ class CLIManager:
3388
3564
  wallet = self.wallet_ask(
3389
3565
  wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
3390
3566
  )
3391
-
3567
+ logger.debug(
3568
+ "args:\n"
3569
+ f"coldkey_ss58 {coldkey_ss58}\n"
3570
+ f"network {network}\n"
3571
+ f"live: {live}\n"
3572
+ f"no_prompt: {no_prompt}\n"
3573
+ )
3392
3574
  return self._run_command(
3393
3575
  list_stake.stake_list(
3394
3576
  wallet,
@@ -3637,6 +3819,7 @@ class CLIManager:
3637
3819
  ),
3638
3820
  exit_early=False,
3639
3821
  )
3822
+ logger.debug(f"Free balance: {free_balance}")
3640
3823
  if free_balance == Balance.from_tao(0):
3641
3824
  print_error("You dont have any balance to stake.")
3642
3825
  return
@@ -3657,7 +3840,21 @@ class CLIManager:
3657
3840
  f"You dont have enough balance to stake. Current free Balance: {free_balance}."
3658
3841
  )
3659
3842
  raise typer.Exit()
3660
-
3843
+ logger.debug(
3844
+ "args:\n"
3845
+ f"network: {network}\n"
3846
+ f"netuids: {netuids}\n"
3847
+ f"stake_all: {stake_all}\n"
3848
+ f"amount: {amount}\n"
3849
+ f"prompt: {prompt}\n"
3850
+ f"all_hotkeys: {all_hotkeys}\n"
3851
+ f"include_hotkeys: {include_hotkeys}\n"
3852
+ f"exclude_hotkeys: {exclude_hotkeys}\n"
3853
+ f"safe_staking: {safe_staking}\n"
3854
+ f"rate_tolerance: {rate_tolerance}\n"
3855
+ f"allow_partial_stake: {allow_partial_stake}\n"
3856
+ f"period: {period}\n"
3857
+ )
3661
3858
  return self._run_command(
3662
3859
  add_stake.stake_add(
3663
3860
  wallet,
@@ -3782,11 +3979,11 @@ class CLIManager:
3782
3979
  "Interactive mode cannot be used with hotkey selection options like "
3783
3980
  "--include-hotkeys, --exclude-hotkeys, --all-hotkeys, or --hotkey."
3784
3981
  )
3785
- raise typer.Exit()
3982
+ return False
3786
3983
 
3787
3984
  if unstake_all and unstake_all_alpha:
3788
3985
  print_error("Cannot specify both unstake-all and unstake-all-alpha.")
3789
- raise typer.Exit()
3986
+ return False
3790
3987
 
3791
3988
  if not interactive and not unstake_all and not unstake_all_alpha:
3792
3989
  netuid = get_optional_netuid(netuid, all_netuids)
@@ -3795,23 +3992,39 @@ class CLIManager:
3795
3992
  "You have specified hotkeys to include and also the `--all-hotkeys` flag. The flag"
3796
3993
  " should only be used standalone (to use all hotkeys) or with `--exclude-hotkeys`."
3797
3994
  )
3798
- raise typer.Exit()
3995
+ return False
3799
3996
 
3800
3997
  if include_hotkeys and exclude_hotkeys:
3801
3998
  print_error(
3802
3999
  "You have specified both including and excluding hotkeys options. Select one or the other."
3803
4000
  )
3804
- raise typer.Exit()
4001
+ return False
3805
4002
 
3806
4003
  if unstake_all and amount:
3807
4004
  print_error(
3808
4005
  "Cannot specify both a specific amount and 'unstake-all'. Choose one or the other."
3809
4006
  )
3810
- raise typer.Exit()
4007
+ return False
3811
4008
 
3812
4009
  if amount and amount <= 0:
3813
4010
  print_error(f"You entered an incorrect unstake amount: {amount}")
3814
- raise typer.Exit()
4011
+ return False
4012
+
4013
+ if include_hotkeys:
4014
+ include_hotkeys = parse_to_list(
4015
+ include_hotkeys,
4016
+ str,
4017
+ "Hotkeys must be a comma-separated list of ss58s or names, e.g., `--include-hotkeys hk1,hk2`.",
4018
+ is_ss58=False,
4019
+ )
4020
+
4021
+ if exclude_hotkeys:
4022
+ exclude_hotkeys = parse_to_list(
4023
+ exclude_hotkeys,
4024
+ str,
4025
+ "Hotkeys must be a comma-separated list of ss58s or names, e.g., `--exclude-hotkeys hk3,hk4`.",
4026
+ is_ss58=False,
4027
+ )
3815
4028
 
3816
4029
  if (
3817
4030
  not wallet_hotkey
@@ -3828,7 +4041,8 @@ class CLIManager:
3828
4041
  default=self.config.get("wallet_name") or defaults.wallet.name,
3829
4042
  )
3830
4043
  hotkey_or_ss58 = Prompt.ask(
3831
- "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake from [dim](or Press Enter to view existing staked hotkeys)[/dim]",
4044
+ "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake from [dim]"
4045
+ "(or Press Enter to view existing staked hotkeys)[/dim]",
3832
4046
  )
3833
4047
  if hotkey_or_ss58 == "":
3834
4048
  wallet = self.wallet_ask(
@@ -3859,12 +4073,12 @@ class CLIManager:
3859
4073
  if include_hotkeys:
3860
4074
  if len(include_hotkeys) > 1:
3861
4075
  print_error("Cannot unstake_all from multiple hotkeys at once.")
3862
- raise typer.Exit()
4076
+ return False
3863
4077
  elif is_valid_ss58_address(include_hotkeys[0]):
3864
4078
  hotkey_ss58_address = include_hotkeys[0]
3865
4079
  else:
3866
4080
  print_error("Invalid hotkey ss58 address.")
3867
- raise typer.Exit()
4081
+ return False
3868
4082
  elif all_hotkeys:
3869
4083
  wallet = self.wallet_ask(
3870
4084
  wallet_name,
@@ -3875,7 +4089,8 @@ class CLIManager:
3875
4089
  else:
3876
4090
  if not hotkey_ss58_address and not wallet_hotkey:
3877
4091
  hotkey_or_ss58 = Prompt.ask(
3878
- "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake all from [dim](or enter 'all' to unstake from all hotkeys)[/dim]",
4092
+ "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake all from [dim]"
4093
+ "(or enter 'all' to unstake from all hotkeys)[/dim]",
3879
4094
  default=self.config.get("wallet_hotkey")
3880
4095
  or defaults.wallet.hotkey,
3881
4096
  )
@@ -3907,6 +4122,17 @@ class CLIManager:
3907
4122
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3908
4123
  validate=WV.WALLET_AND_HOTKEY,
3909
4124
  )
4125
+ logger.debug(
4126
+ "args:\n"
4127
+ f"network: {network}\n"
4128
+ f"hotkey_ss58_address: {hotkey_ss58_address}\n"
4129
+ f"unstake_all: {unstake_all}\n"
4130
+ f"unstake_all_alpha: {unstake_all_alpha}\n"
4131
+ f"all_hotkeys: {all_hotkeys}\n"
4132
+ f"include_hotkeys: {include_hotkeys}\n"
4133
+ f"exclude_hotkeys: {exclude_hotkeys}\n"
4134
+ f"era: {period}"
4135
+ )
3910
4136
  return self._run_command(
3911
4137
  remove_stake.unstake_all(
3912
4138
  wallet=wallet,
@@ -3941,22 +4167,38 @@ class CLIManager:
3941
4167
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3942
4168
  validate=WV.WALLET_AND_HOTKEY,
3943
4169
  )
3944
-
3945
- if include_hotkeys:
3946
- include_hotkeys = parse_to_list(
3947
- include_hotkeys,
3948
- str,
3949
- "Hotkeys must be a comma-separated list of ss58s or names, e.g., `--include-hotkeys hk1,hk2`.",
3950
- is_ss58=False,
4170
+ if not amount and not prompt:
4171
+ print_error(
4172
+ f"Ambiguous request! Specify {arg__('--amount')}, {arg__('--all')}, or {arg__('--all-alpha')} "
4173
+ f"to use {arg__('--no-prompt')}"
3951
4174
  )
4175
+ return False
3952
4176
 
3953
- if exclude_hotkeys:
3954
- exclude_hotkeys = parse_to_list(
3955
- exclude_hotkeys,
3956
- str,
3957
- "Hotkeys must be a comma-separated list of ss58s or names, e.g., `--exclude-hotkeys hk3,hk4`.",
3958
- is_ss58=False,
4177
+ if not amount and json_output:
4178
+ json_console.print_json(
4179
+ data={
4180
+ "success": False,
4181
+ "err_msg": "Ambiguous request! Specify '--amount', '--all', "
4182
+ "or '--all-alpha' to use '--json-output'",
4183
+ }
3959
4184
  )
4185
+ return False
4186
+ logger.debug(
4187
+ "args:\n"
4188
+ f"network: {network}\n"
4189
+ f"hotkey_ss58_address: {hotkey_ss58_address}\n"
4190
+ f"all_hotkeys: {all_hotkeys}\n"
4191
+ f"include_hotkeys: {include_hotkeys}\n"
4192
+ f"exclude_hotkeys: {exclude_hotkeys}\n"
4193
+ f"amount: {amount}\n"
4194
+ f"prompt: {prompt}\n"
4195
+ f"interactive: {interactive}\n"
4196
+ f"netuid: {netuid}\n"
4197
+ f"safe_staking: {safe_staking}\n"
4198
+ f"rate_tolerance: {rate_tolerance}\n"
4199
+ f"allow_partial_stake: {allow_partial_stake}\n"
4200
+ f"era: {period}"
4201
+ )
3960
4202
 
3961
4203
  return self._run_command(
3962
4204
  remove_stake.unstake(
@@ -4121,7 +4363,19 @@ class CLIManager:
4121
4363
  destination_netuid = IntPrompt.ask(
4122
4364
  "Enter the [blue]destination subnet[/blue] (netuid) to move stake to"
4123
4365
  )
4124
-
4366
+ logger.debug(
4367
+ "args:\n"
4368
+ f"network: {network}\n"
4369
+ f"origin_netuid: {origin_netuid}\n"
4370
+ f"origin_hotkey: {origin_hotkey}\n"
4371
+ f"destination_hotkey: {destination_hotkey}\n"
4372
+ f"destination_netuid: {destination_netuid}\n"
4373
+ f"amount: {amount}\n"
4374
+ f"stake_all: {stake_all}\n"
4375
+ f"era: {period}\n"
4376
+ f"interactive_selection: {interactive_selection}\n"
4377
+ f"prompt: {prompt}\n"
4378
+ )
4125
4379
  result = self._run_command(
4126
4380
  move_stake.move_stake(
4127
4381
  subtensor=self.initialize_chain(network),
@@ -4286,7 +4540,18 @@ class CLIManager:
4286
4540
  dest_netuid = IntPrompt.ask(
4287
4541
  "Enter the [blue]destination subnet[/blue] (netuid)"
4288
4542
  )
4289
-
4543
+ logger.debug(
4544
+ "args:\n"
4545
+ f"network: {network}\n"
4546
+ f"origin_hotkey: {origin_hotkey}\n"
4547
+ f"origin_netuid: {origin_netuid}\n"
4548
+ f"dest_netuid: {dest_netuid}\n"
4549
+ f"dest_hotkey: {origin_hotkey}\n"
4550
+ f"dest_coldkey_ss58: {dest_ss58}\n"
4551
+ f"amount: {amount}\n"
4552
+ f"era: {period}\n"
4553
+ f"stake_all: {stake_all}"
4554
+ )
4290
4555
  result = self._run_command(
4291
4556
  move_stake.transfer_stake(
4292
4557
  wallet=wallet,
@@ -4394,7 +4659,19 @@ class CLIManager:
4394
4659
  )
4395
4660
  if not amount and not swap_all:
4396
4661
  amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to swap")
4397
-
4662
+ logger.debug(
4663
+ "args:\n"
4664
+ f"network: {network}\n"
4665
+ f"origin_netuid: {origin_netuid}\n"
4666
+ f"dest_netuid: {dest_netuid}\n"
4667
+ f"amount: {amount}\n"
4668
+ f"swap_all: {swap_all}\n"
4669
+ f"era: {period}\n"
4670
+ f"interactive_selection: {interactive_selection}\n"
4671
+ f"prompt: {prompt}\n"
4672
+ f"wait_for_inclusion: {wait_for_inclusion}\n"
4673
+ f"wait_for_finalization: {wait_for_finalization}\n"
4674
+ )
4398
4675
  result = self._run_command(
4399
4676
  move_stake.swap_stake(
4400
4677
  wallet=wallet,
@@ -4510,7 +4787,7 @@ class CLIManager:
4510
4787
 
4511
4788
  EXAMPLE
4512
4789
 
4513
- [green]$[/green] btcli stake child set -c 5FCL3gmjtQV4xxxxuEPEFQVhyyyyqYgNwX7drFLw7MSdBnxP -c 5Hp5dxxxxtGg7pu8dN2btyyyyVA1vELmM9dy8KQv3LxV8PA7 --hotkey default --netuid 1 -p 0.3 -p 0.7
4790
+ [green]$[/green] btcli stake child set -c 5FCL3gmjtQV4xxxxuEPEFQVhyyyyqYgNwX7drFLw7MSdBnxP -c 5Hp5dxxxxtGg7pu8dN2btyyyyVA1vELmM9dy8KQv3LxV8PA7 --hotkey default --netuid 1 --prop 0.3 --prop 0.7
4514
4791
  """
4515
4792
  self.verbosity_handler(quiet, verbose, json_output)
4516
4793
  netuid = get_optional_netuid(netuid, all_netuids)
@@ -4543,6 +4820,15 @@ class CLIManager:
4543
4820
  ask_for=[WO.NAME, WO.HOTKEY],
4544
4821
  validate=WV.WALLET_AND_HOTKEY,
4545
4822
  )
4823
+ logger.debug(
4824
+ "args:\n"
4825
+ f"network: {network}\n"
4826
+ f"netuid: {netuid}\n"
4827
+ f"children: {children}\n"
4828
+ f"proportions: {proportions}\n"
4829
+ f"wait_for_inclusion: {wait_for_inclusion}\n"
4830
+ f"wait_for_finalization: {wait_for_finalization}\n"
4831
+ )
4546
4832
  return self._run_command(
4547
4833
  children_hotkeys.set_children(
4548
4834
  wallet=wallet,
@@ -4608,6 +4894,13 @@ class CLIManager:
4608
4894
  netuid = IntPrompt.ask(
4609
4895
  "Enter netuid (leave blank for all)", default=None, show_default=True
4610
4896
  )
4897
+ logger.debug(
4898
+ "args:\n"
4899
+ f"network: {network}\n"
4900
+ f"netuid: {netuid}\n"
4901
+ f"wait_for_inclusion: {wait_for_inclusion}\n"
4902
+ f"wait_for_finalization: {wait_for_finalization}\n"
4903
+ )
4611
4904
  return self._run_command(
4612
4905
  children_hotkeys.revoke_children(
4613
4906
  wallet,
@@ -4686,6 +4979,14 @@ class CLIManager:
4686
4979
  netuid = IntPrompt.ask(
4687
4980
  "Enter netuid (leave blank for all)", default=None, show_default=True
4688
4981
  )
4982
+ logger.debug(
4983
+ "args:\n"
4984
+ f"network: {network}\n"
4985
+ f"netuid: {netuid}\n"
4986
+ f"take: {take}\n"
4987
+ f"wait_for_inclusion: {wait_for_inclusion}\n"
4988
+ f"wait_for_finalization: {wait_for_finalization}\n"
4989
+ )
4689
4990
  results: list[tuple[Optional[int], bool]] = self._run_command(
4690
4991
  children_hotkeys.childkey_take(
4691
4992
  wallet=wallet,
@@ -4771,12 +5072,8 @@ class CLIManager:
4771
5072
  )
4772
5073
  return False
4773
5074
  param_name = "alpha_values"
4774
- low_val = FloatPrompt.ask(
4775
- f"Enter the new value for [{COLORS.G.ARG}]alpha_low[/{COLORS.G.ARG}]"
4776
- )
4777
- high_val = FloatPrompt.ask(
4778
- f"Enter the new value for [{COLORS.G.ARG}]alpha_high[/{COLORS.G.ARG}]"
4779
- )
5075
+ low_val = FloatPrompt.ask(f"Enter the new value for {arg__('alpha_low')}")
5076
+ high_val = FloatPrompt.ask(f"Enter the new value for {arg__('alpha_high')}")
4780
5077
  param_value = f"{low_val},{high_val}"
4781
5078
  if param_name == "yuma_version":
4782
5079
  if not prompt:
@@ -4800,7 +5097,7 @@ class CLIManager:
4800
5097
  if param_name == "subnet_is_active":
4801
5098
  err_console.print(
4802
5099
  f"[{COLORS.SU.HYPERPARAM}]subnet_is_active[/{COLORS.SU.HYPERPARAM}] "
4803
- f"is set by using [{COLORS.G.ARG}]`btcli subnets start`[/{COLORS.G.ARG}] command."
5100
+ f"is set by using {arg__('btcli subnets start')} command."
4804
5101
  )
4805
5102
  return False
4806
5103
 
@@ -4821,6 +5118,13 @@ class CLIManager:
4821
5118
  wallet = self.wallet_ask(
4822
5119
  wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
4823
5120
  )
5121
+ logger.debug(
5122
+ "args:\n"
5123
+ f"network: {network}\n"
5124
+ f"netuid: {netuid}\n"
5125
+ f"param_name: {param_name}\n"
5126
+ f"param_value: {param_value}"
5127
+ )
4824
5128
  result, err_msg = self._run_command(
4825
5129
  sudo.sudo_set_hyperparameter(
4826
5130
  wallet,
@@ -4941,6 +5245,7 @@ class CLIManager:
4941
5245
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
4942
5246
  validate=WV.WALLET_AND_HOTKEY,
4943
5247
  )
5248
+ logger.debug(f"args:\nnetwork: {network}\nproposal: {proposal}\nvote: {vote}\n")
4944
5249
  return self._run_command(
4945
5250
  sudo.senate_vote(
4946
5251
  wallet, self.initialize_chain(network), proposal, vote, prompt
@@ -4993,7 +5298,7 @@ class CLIManager:
4993
5298
  f"Take value must be between {min_value} and {max_value}. Provided value: {take}"
4994
5299
  )
4995
5300
  raise typer.Exit()
4996
-
5301
+ logger.debug(f"args:\nnetwork: {network}\ntake: {take}")
4997
5302
  result = self._run_command(
4998
5303
  sudo.set_take(wallet, self.initialize_chain(network), take)
4999
5304
  )
@@ -5142,14 +5447,12 @@ class CLIManager:
5142
5447
  """
5143
5448
  if json_output and html_output:
5144
5449
  print_error(
5145
- f"Cannot specify both [{COLORS.G.ARG}]--json-output[/{COLORS.G.ARG}] "
5146
- f"and [{COLORS.G.ARG}]--html[/{COLORS.G.ARG}]"
5450
+ f"Cannot specify both {arg__('--json-output')} and {arg__('--html')}"
5147
5451
  )
5148
5452
  return
5149
5453
  if current_only and html_output:
5150
5454
  print_error(
5151
- f"Cannot specify both [{COLORS.G.ARG}]--current[/{COLORS.G.ARG}] "
5152
- f"and [{COLORS.G.ARG}]--html[/{COLORS.G.ARG}]"
5455
+ f"Cannot specify both {arg__('--current')} and {arg__('--html')}"
5153
5456
  )
5154
5457
  return
5155
5458
  self.verbosity_handler(quiet=quiet, verbose=verbose, json_output=json_output)
@@ -5160,9 +5463,8 @@ class CLIManager:
5160
5463
  Constants.network_map[x] for x in non_archives
5161
5464
  ]:
5162
5465
  err_console.print(
5163
- f"[red]Error[/red] Running this command without [{COLORS.G.ARG}]--current[/{COLORS.G.ARG}] requires "
5164
- "use of an archive node. "
5165
- f"Try running again with the [{COLORS.G.ARG}]--network archive[/{COLORS.G.ARG}] flag."
5466
+ f"[red]Error[/red] Running this command without {arg__('--current')} requires use of an archive node. "
5467
+ f"Try running again with the {arg__('--network archive')} flag."
5166
5468
  )
5167
5469
  return False
5168
5470
 
@@ -5225,7 +5527,7 @@ class CLIManager:
5225
5527
 
5226
5528
  EXAMPLE
5227
5529
 
5228
- [green]$[/green] btcli subnets list
5530
+ [green]$[/green] btcli subnets show
5229
5531
  """
5230
5532
  self.verbosity_handler(quiet, verbose, json_output)
5231
5533
  subtensor = self.initialize_chain(network)
@@ -5337,6 +5639,7 @@ class CLIManager:
5337
5639
  logo_url=logo_url,
5338
5640
  additional=additional_info,
5339
5641
  )
5642
+ logger.debug(f"args:\nnetwork: {network}\nidentity: {identity}\n")
5340
5643
  self._run_command(
5341
5644
  subnets.create(
5342
5645
  wallet, self.initialize_chain(network), identity, json_output, prompt
@@ -5398,6 +5701,7 @@ class CLIManager:
5398
5701
  ],
5399
5702
  validate=WV.WALLET,
5400
5703
  )
5704
+ logger.debug(f"args:\nnetwork: {network}\nnetuid: {netuid}\n")
5401
5705
  return self._run_command(
5402
5706
  subnets.start_subnet(
5403
5707
  wallet,
@@ -5513,7 +5817,9 @@ class CLIManager:
5513
5817
  logo_url=logo_url,
5514
5818
  additional=additional_info,
5515
5819
  )
5516
-
5820
+ logger.debug(
5821
+ f"args:\nnetwork: {network}\nnetuid: {netuid}\nidentity: {identity}"
5822
+ )
5517
5823
  success = self._run_command(
5518
5824
  subnets.set_identity(
5519
5825
  wallet, self.initialize_chain(network), netuid, identity, prompt
@@ -5651,6 +5957,7 @@ class CLIManager:
5651
5957
  ask_for=[WO.NAME, WO.HOTKEY],
5652
5958
  validate=WV.WALLET_AND_HOTKEY,
5653
5959
  )
5960
+ logger.debug(f"args:\nnetwork: {network}\nnetuid: {netuid}\nperiod: {period}\n")
5654
5961
  return self._run_command(
5655
5962
  subnets.register(
5656
5963
  wallet,
@@ -5763,6 +6070,55 @@ class CLIManager:
5763
6070
  )
5764
6071
  )
5765
6072
 
6073
+ def subnets_set_symbol(
6074
+ self,
6075
+ wallet_name: str = Options.wallet_name,
6076
+ wallet_path: str = Options.wallet_path,
6077
+ wallet_hotkey: str = Options.wallet_hotkey,
6078
+ network: Optional[list[str]] = Options.network,
6079
+ netuid: int = Options.netuid,
6080
+ json_output: bool = Options.json_output,
6081
+ prompt: bool = Options.prompt,
6082
+ quiet: bool = Options.quiet,
6083
+ verbose: bool = Options.verbose,
6084
+ symbol: str = typer.Argument(help="The symbol to set for your subnet."),
6085
+ ):
6086
+ """
6087
+ Allows the user to update their subnet symbol to a different available symbol. The full list of available symbols can be found here:
6088
+ [#8CB9E9]https://github.com/opentensor/subtensor/blob/main/pallets/subtensor/src/subnets/symbols.rs#L8[/#8CB9E9]
6089
+
6090
+
6091
+ EXAMPLE
6092
+
6093
+ [green]$[/green] btcli subnets set-symbol [dark_orange]--netuid 1 シ[/dark_orange]
6094
+
6095
+
6096
+ JSON OUTPUT:
6097
+ If --json-output is used, the output will be in the following schema:
6098
+ [#AFEFFF]{success: [dark_orange]bool[/dark_orange], message: [dark_orange]str[/dark_orange]}[/#AFEFFF]
6099
+ """
6100
+ self.verbosity_handler(quiet, verbose, json_output)
6101
+ if len(symbol) > 1:
6102
+ err_console.print("Your symbol must be a single character.")
6103
+ return False
6104
+ wallet = self.wallet_ask(
6105
+ wallet_name,
6106
+ wallet_path,
6107
+ wallet_hotkey,
6108
+ ask_for=[WO.NAME, WO.HOTKEY],
6109
+ validate=WV.WALLET_AND_HOTKEY,
6110
+ )
6111
+ return self._run_command(
6112
+ subnets.set_symbol(
6113
+ wallet=wallet,
6114
+ subtensor=self.initialize_chain(network),
6115
+ netuid=netuid,
6116
+ symbol=symbol,
6117
+ prompt=prompt,
6118
+ json_output=json_output,
6119
+ )
6120
+ )
6121
+
5766
6122
  def weights_reveal(
5767
6123
  self,
5768
6124
  network: Optional[list[str]] = Options.network,
@@ -5848,7 +6204,6 @@ class CLIManager:
5848
6204
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
5849
6205
  validate=WV.WALLET_AND_HOTKEY,
5850
6206
  )
5851
-
5852
6207
  return self._run_command(
5853
6208
  weights_cmds.reveal_weights(
5854
6209
  self.initialize_chain(network),
@@ -6094,7 +6449,14 @@ class CLIManager:
6094
6449
  if price_low >= price_high:
6095
6450
  err_console.print("The low price must be lower than the high price.")
6096
6451
  return False
6097
-
6452
+ logger.debug(
6453
+ f"args:\n"
6454
+ f"hotkey: {hotkey}\n"
6455
+ f"netuid: {netuid}\n"
6456
+ f"liquidity: {liquidity_}\n"
6457
+ f"price_low: {price_low}\n"
6458
+ f"price_high: {price_high}\n"
6459
+ )
6098
6460
  return self._run_command(
6099
6461
  liquidity.add_liquidity(
6100
6462
  subtensor=self.initialize_chain(network),
@@ -6195,6 +6557,14 @@ class CLIManager:
6195
6557
  validate=WV.WALLET,
6196
6558
  return_wallet_and_hotkey=True,
6197
6559
  )
6560
+ logger.debug(
6561
+ f"args:\n"
6562
+ f"network: {network}\n"
6563
+ f"hotkey: {hotkey}\n"
6564
+ f"netuid: {netuid}\n"
6565
+ f"position_id: {position_id}\n"
6566
+ f"all_liquidity_ids: {all_liquidity_ids}\n"
6567
+ )
6198
6568
  return self._run_command(
6199
6569
  liquidity.remove_liquidity(
6200
6570
  subtensor=self.initialize_chain(network),
@@ -6259,6 +6629,14 @@ class CLIManager:
6259
6629
  f"[blue]{position_id}[/blue] (can be positive or negative)",
6260
6630
  negative_allowed=True,
6261
6631
  )
6632
+ logger.debug(
6633
+ f"args:\n"
6634
+ f"network: {network}\n"
6635
+ f"hotkey: {hotkey}\n"
6636
+ f"netuid: {netuid}\n"
6637
+ f"position_id: {position_id}\n"
6638
+ f"liquidity_delta: {liquidity_delta}"
6639
+ )
6262
6640
 
6263
6641
  return self._run_command(
6264
6642
  liquidity.modify_liquidity(
@@ -6274,7 +6652,6 @@ class CLIManager:
6274
6652
  )
6275
6653
 
6276
6654
  @staticmethod
6277
- @utils_app.command("convert")
6278
6655
  def convert(
6279
6656
  from_rao: Optional[str] = typer.Option(
6280
6657
  None, "--rao", help="Convert amount from Rao"
@@ -6304,6 +6681,66 @@ class CLIManager:
6304
6681
  f"{Balance.from_tao(tao).rao}{Balance.rao_unit}",
6305
6682
  )
6306
6683
 
6684
+ def best_connection(
6685
+ self,
6686
+ additional_networks: Optional[list[str]] = typer.Option(
6687
+ None,
6688
+ "--network",
6689
+ help="Network(s) to test for the best connection",
6690
+ ),
6691
+ ):
6692
+ """
6693
+ This command will give you the latency of all finney-like network in additional to any additional networks you specify via the '--network' flag
6694
+
6695
+ The results are three-fold. One column is the overall time to initialise a connection, send the requests, and wait for the results. The second column measures single ping-pong speed once connected. The third makes a real world call to fetch the chain head.
6696
+
6697
+ EXAMPLE
6698
+
6699
+ [green]$[/green] btcli utils latency --network ws://189.234.12.45 --network wss://mysubtensor.duckdns.org
6700
+
6701
+ """
6702
+ additional_networks = additional_networks or []
6703
+ if any(not x.startswith("ws") for x in additional_networks):
6704
+ err_console.print(
6705
+ "Invalid network endpoint. Ensure you are specifying a valid websocket endpoint"
6706
+ f" (starting with [{COLORS.G.LINKS}]ws://[/{COLORS.G.LINKS}] or "
6707
+ f"[{COLORS.G.LINKS}]wss://[/{COLORS.G.LINKS}]).",
6708
+ )
6709
+ return False
6710
+ results: dict[str, list[float]] = self._run_command(
6711
+ best_connection(Constants.lite_nodes + additional_networks)
6712
+ )
6713
+ sorted_results = {
6714
+ k: v for k, v in sorted(results.items(), key=lambda item: item[1][0])
6715
+ }
6716
+ table = Table(
6717
+ Column("Network"),
6718
+ Column("End to End Latency", style="cyan"),
6719
+ Column("Single Request Ping", style="cyan"),
6720
+ Column("Chain Head Request Latency", style="cyan"),
6721
+ title="Connection Latencies (seconds)",
6722
+ caption="lower value is faster",
6723
+ )
6724
+ for n_name, (
6725
+ overall_latency,
6726
+ single_request,
6727
+ chain_head,
6728
+ ) in sorted_results.items():
6729
+ table.add_row(
6730
+ n_name, str(overall_latency), str(single_request), str(chain_head)
6731
+ )
6732
+ console.print(table)
6733
+ fastest = next(iter(sorted_results.keys()))
6734
+ if conf_net := self.config.get("network", ""):
6735
+ if not conf_net.startswith("ws") and conf_net in Constants.networks:
6736
+ conf_net = Constants.network_map[conf_net]
6737
+ if conf_net != fastest:
6738
+ console.print(
6739
+ f"The fastest network is {fastest}. You currently have {conf_net} selected as your default network."
6740
+ f"\nYou can update this with {arg__(f'btcli config set --network {fastest}')}"
6741
+ )
6742
+ return True
6743
+
6307
6744
  def run(self):
6308
6745
  self.app()
6309
6746