bittensor-cli 8.4.3__py3-none-any.whl → 9.0.0rc2__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 (30) hide show
  1. bittensor_cli/__init__.py +2 -2
  2. bittensor_cli/cli.py +1508 -1385
  3. bittensor_cli/src/__init__.py +627 -197
  4. bittensor_cli/src/bittensor/balances.py +41 -8
  5. bittensor_cli/src/bittensor/chain_data.py +557 -428
  6. bittensor_cli/src/bittensor/extrinsics/registration.py +161 -47
  7. bittensor_cli/src/bittensor/extrinsics/root.py +14 -8
  8. bittensor_cli/src/bittensor/extrinsics/transfer.py +14 -21
  9. bittensor_cli/src/bittensor/minigraph.py +46 -8
  10. bittensor_cli/src/bittensor/subtensor_interface.py +572 -253
  11. bittensor_cli/src/bittensor/utils.py +326 -75
  12. bittensor_cli/src/commands/stake/__init__.py +154 -0
  13. bittensor_cli/src/commands/stake/children_hotkeys.py +121 -87
  14. bittensor_cli/src/commands/stake/move.py +1000 -0
  15. bittensor_cli/src/commands/stake/stake.py +1637 -1264
  16. bittensor_cli/src/commands/subnets/__init__.py +0 -0
  17. bittensor_cli/src/commands/subnets/price.py +867 -0
  18. bittensor_cli/src/commands/subnets/subnets.py +2055 -0
  19. bittensor_cli/src/commands/sudo.py +529 -26
  20. bittensor_cli/src/commands/wallets.py +234 -544
  21. bittensor_cli/src/commands/weights.py +15 -11
  22. {bittensor_cli-8.4.3.dist-info → bittensor_cli-9.0.0rc2.dist-info}/METADATA +7 -4
  23. bittensor_cli-9.0.0rc2.dist-info/RECORD +32 -0
  24. bittensor_cli/src/bittensor/async_substrate_interface.py +0 -2748
  25. bittensor_cli/src/commands/root.py +0 -1752
  26. bittensor_cli/src/commands/subnets.py +0 -897
  27. bittensor_cli-8.4.3.dist-info/RECORD +0 -31
  28. {bittensor_cli-8.4.3.dist-info → bittensor_cli-9.0.0rc2.dist-info}/WHEEL +0 -0
  29. {bittensor_cli-8.4.3.dist-info → bittensor_cli-9.0.0rc2.dist-info}/entry_points.txt +0 -0
  30. {bittensor_cli-8.4.3.dist-info → bittensor_cli-9.0.0rc2.dist-info}/top_level.txt +0 -0
bittensor_cli/cli.py CHANGED
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  import asyncio
3
- import binascii
4
3
  import curses
5
- from functools import partial
6
4
  import os.path
7
5
  import re
8
6
  import ssl
@@ -19,21 +17,22 @@ from bittensor_wallet import Wallet
19
17
  from rich import box
20
18
  from rich.prompt import Confirm, FloatPrompt, Prompt, IntPrompt
21
19
  from rich.table import Column, Table
20
+ from rich.tree import Tree
22
21
  from bittensor_cli.src import (
23
22
  defaults,
24
23
  HELP_PANELS,
25
24
  WalletOptions as WO,
26
25
  WalletValidationTypes as WV,
27
26
  Constants,
27
+ COLOR_PALETTE,
28
28
  )
29
29
  from bittensor_cli.src.bittensor import utils
30
30
  from bittensor_cli.src.bittensor.balances import Balance
31
- from bittensor_cli.src.bittensor.async_substrate_interface import (
32
- SubstrateRequestException,
33
- )
34
- from bittensor_cli.src.commands import root, subnets, sudo, wallets
31
+ from async_substrate_interface.errors import SubstrateRequestException
32
+ from bittensor_cli.src.commands import sudo, wallets
35
33
  from bittensor_cli.src.commands import weights as weights_cmds
36
- from bittensor_cli.src.commands.stake import children_hotkeys, stake
34
+ from bittensor_cli.src.commands.subnets import price, subnets
35
+ from bittensor_cli.src.commands.stake import children_hotkeys, stake, move
37
36
  from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
38
37
  from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters
39
38
  from bittensor_cli.src.bittensor.utils import (
@@ -43,22 +42,30 @@ from bittensor_cli.src.bittensor.utils import (
43
42
  is_valid_ss58_address,
44
43
  print_error,
45
44
  validate_chain_endpoint,
46
- retry_prompt,
45
+ validate_netuid,
46
+ is_rao_network,
47
+ get_effective_network,
48
+ prompt_for_identity,
49
+ validate_uri,
50
+ prompt_for_subnet_identity,
51
+ print_linux_dependency_message,
52
+ is_linux,
47
53
  )
48
54
  from typing_extensions import Annotated
49
55
  from textwrap import dedent
50
- from websockets import ConnectionClosed
56
+ from websockets import ConnectionClosed, InvalidHandshake
51
57
  from yaml import safe_dump, safe_load
52
58
 
53
59
  try:
54
60
  from git import Repo, GitError
55
61
  except ImportError:
62
+ Repo = None
56
63
 
57
64
  class GitError(Exception):
58
65
  pass
59
66
 
60
67
 
61
- __version__ = "8.4.3"
68
+ __version__ = "9.0.0rc2"
62
69
 
63
70
 
64
71
  _core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0)
@@ -127,15 +134,22 @@ class Options:
127
134
  use_password = typer.Option(
128
135
  True,
129
136
  help="Set this to `True` to protect the generated Bittensor key with a password.",
137
+ is_flag=True,
138
+ flag_value=False,
130
139
  )
131
140
  public_hex_key = typer.Option(None, help="The public key in hex format.")
132
141
  ss58_address = typer.Option(
133
142
  None, "--ss58", "--ss58-address", help="The SS58 address of the coldkey."
134
143
  )
135
- overwrite = typer.Option(
144
+ overwrite_coldkey = typer.Option(
145
+ False,
146
+ help="Overwrite the old coldkey with the newly generated coldkey.",
147
+ prompt=True,
148
+ )
149
+ overwrite_hotkey = typer.Option(
136
150
  False,
137
- "--overwrite/--no-overwrite",
138
- help="Overwrite the existing wallet file with the new one.",
151
+ help="Overwrite the old hotkey with the newly generated hotkey.",
152
+ prompt=True,
139
153
  )
140
154
  network = typer.Option(
141
155
  None,
@@ -157,6 +171,17 @@ class Options:
157
171
  None,
158
172
  help="The netuid of the subnet in the root network, (e.g. 1).",
159
173
  prompt=True,
174
+ callback=validate_netuid,
175
+ )
176
+ netuid_not_req = typer.Option(
177
+ None,
178
+ help="The netuid of the subnet in the root network, (e.g. 1).",
179
+ prompt=False,
180
+ )
181
+ all_netuids = typer.Option(
182
+ False,
183
+ help="Use all netuids",
184
+ prompt=False,
160
185
  )
161
186
  weights = typer.Option(
162
187
  None,
@@ -202,6 +227,17 @@ class Options:
202
227
  "--quiet",
203
228
  help="Display only critical information on the console.",
204
229
  )
230
+ live = typer.Option(
231
+ False,
232
+ "--live",
233
+ help="Display live view of the table",
234
+ )
235
+ uri = typer.Option(
236
+ None,
237
+ "--uri",
238
+ help="Create wallet from uri (e.g. 'Alice', 'Bob', 'Charlie', 'Dave', 'Eve')",
239
+ callback=validate_uri,
240
+ )
205
241
 
206
242
 
207
243
  def list_prompt(init_var: list, list_type: type, help_text: str) -> list:
@@ -244,7 +280,8 @@ def parse_to_list(
244
280
  def verbosity_console_handler(verbosity_level: int = 1) -> None:
245
281
  """
246
282
  Sets verbosity level of console output
247
- :param verbosity_level: int corresponding to verbosity level of console output (0 is quiet, 1 is normal, 2 is verbose)
283
+ :param verbosity_level: int corresponding to verbosity level of console output (0 is quiet, 1 is normal, 2 is
284
+ verbose)
248
285
  """
249
286
  if verbosity_level not in range(3):
250
287
  raise ValueError(
@@ -264,6 +301,32 @@ def verbosity_console_handler(verbosity_level: int = 1) -> None:
264
301
  verbose_console.quiet = False
265
302
 
266
303
 
304
+ def get_optional_netuid(netuid: Optional[int], all_netuids: bool) -> Optional[int]:
305
+ """
306
+ Parses options to determine if the user wants to use a specific netuid or all netuids (None)
307
+
308
+ Returns:
309
+ None if using all netuids, otherwise int for the netuid to use
310
+ """
311
+ if netuid is None and all_netuids is True:
312
+ return None
313
+ elif netuid is None and all_netuids is False:
314
+ answer = Prompt.ask(
315
+ f"Enter the [{COLOR_PALETTE['GENERAL']['SUBHEADING_MAIN']}]netuid"
316
+ f"[/{COLOR_PALETTE['GENERAL']['SUBHEADING_MAIN']}] to use. Leave blank for all netuids",
317
+ default=None,
318
+ show_default=False,
319
+ )
320
+ if answer is None:
321
+ return None
322
+ if answer.lower() == "all":
323
+ return None
324
+ else:
325
+ return int(answer)
326
+ else:
327
+ return netuid
328
+
329
+
267
330
  def get_n_words(n_words: Optional[int]) -> int:
268
331
  """
269
332
  Prompts the user to select the number of words used in the mnemonic if not supplied or not within the
@@ -413,12 +476,21 @@ def version_callback(value: bool):
413
476
  raise typer.Exit()
414
477
 
415
478
 
479
+ def commands_callback(value: bool):
480
+ """
481
+ Prints a tree of commands for the app
482
+ """
483
+ if value:
484
+ cli = CLIManager()
485
+ console.print(cli.generate_command_tree())
486
+ raise typer.Exit()
487
+
488
+
416
489
  class CLIManager:
417
490
  """
418
491
  :var app: the main CLI Typer app
419
492
  :var config_app: the Typer app as it relates to config commands
420
493
  :var wallet_app: the Typer app as it relates to wallet commands
421
- :var root_app: the Typer app as it relates to root commands
422
494
  :var stake_app: the Typer app as it relates to stake commands
423
495
  :var sudo_app: the Typer app as it relates to sudo commands
424
496
  :var subnets_app: the Typer app as it relates to subnets commands
@@ -429,7 +501,6 @@ class CLIManager:
429
501
  app: typer.Typer
430
502
  config_app: typer.Typer
431
503
  wallet_app: typer.Typer
432
- root_app: typer.Typer
433
504
  subnets_app: typer.Typer
434
505
  weights_app: typer.Typer
435
506
  utils_app = typer.Typer(epilog=_epilog)
@@ -443,7 +514,9 @@ class CLIManager:
443
514
  "use_cache": True,
444
515
  "metagraph_cols": {
445
516
  "UID": True,
446
- "STAKE": True,
517
+ "GLOBAL_STAKE": True,
518
+ "LOCAL_STAKE": True,
519
+ "STAKE_WEIGHT": True,
447
520
  "RANK": True,
448
521
  "TRUST": True,
449
522
  "CONSENSUS": True,
@@ -471,7 +544,6 @@ class CLIManager:
471
544
  )
472
545
  self.config_app = typer.Typer(epilog=_epilog)
473
546
  self.wallet_app = typer.Typer(epilog=_epilog)
474
- self.root_app = typer.Typer(epilog=_epilog)
475
547
  self.stake_app = typer.Typer(epilog=_epilog)
476
548
  self.sudo_app = typer.Typer(epilog=_epilog)
477
549
  self.subnets_app = typer.Typer(epilog=_epilog)
@@ -501,15 +573,6 @@ class CLIManager:
501
573
  self.wallet_app, name="wallets", hidden=True, no_args_is_help=True
502
574
  )
503
575
 
504
- # root aliases
505
- self.app.add_typer(
506
- self.root_app,
507
- name="root",
508
- short_help="Root commands, alias: `r`",
509
- no_args_is_help=True,
510
- )
511
- self.app.add_typer(self.root_app, name="r", hidden=True, no_args_is_help=True)
512
-
513
576
  # stake aliases
514
577
  self.app.add_typer(
515
578
  self.stake_app,
@@ -558,7 +621,9 @@ class CLIManager:
558
621
  )
559
622
 
560
623
  # utils app
561
- self.app.add_typer(self.utils_app, name="utils", no_args_is_help=True)
624
+ self.app.add_typer(
625
+ self.utils_app, name="utils", no_args_is_help=True, hidden=True
626
+ )
562
627
 
563
628
  # config commands
564
629
  self.config_app.command("set")(self.set_config)
@@ -595,16 +660,21 @@ class CLIManager:
595
660
  "balance", rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"]
596
661
  )(self.wallet_balance)
597
662
  self.wallet_app.command(
598
- "history", rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"]
663
+ "history",
664
+ rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"],
665
+ hidden=True,
599
666
  )(self.wallet_history)
600
667
  self.wallet_app.command(
601
- "overview", rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"]
668
+ "overview",
669
+ rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"],
602
670
  )(self.wallet_overview)
603
671
  self.wallet_app.command(
604
672
  "transfer", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
605
673
  )(self.wallet_transfer)
606
674
  self.wallet_app.command(
607
- "inspect", rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"]
675
+ "inspect",
676
+ rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"],
677
+ hidden=True,
608
678
  )(self.wallet_inspect)
609
679
  self.wallet_app.command(
610
680
  "faucet", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
@@ -619,59 +689,25 @@ class CLIManager:
619
689
  "sign", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
620
690
  )(self.wallet_sign)
621
691
 
622
- # root commands
623
- self.root_app.command("list")(self.root_list)
624
- self.root_app.command(
625
- "set-weights", rich_help_panel=HELP_PANELS["ROOT"]["WEIGHT_MGMT"]
626
- )(self.root_set_weights)
627
- self.root_app.command(
628
- "get-weights", rich_help_panel=HELP_PANELS["ROOT"]["WEIGHT_MGMT"]
629
- )(self.root_get_weights)
630
- self.root_app.command(
631
- "boost", rich_help_panel=HELP_PANELS["ROOT"]["WEIGHT_MGMT"]
632
- )(self.root_boost)
633
- self.root_app.command(
634
- "slash", rich_help_panel=HELP_PANELS["ROOT"]["WEIGHT_MGMT"]
635
- )(self.root_slash)
636
- self.root_app.command(
637
- "senate", rich_help_panel=HELP_PANELS["ROOT"]["GOVERNANCE"]
638
- )(self.root_senate)
639
- self.root_app.command(
640
- "senate-vote", rich_help_panel=HELP_PANELS["ROOT"]["GOVERNANCE"]
641
- )(self.root_senate_vote)
642
- self.root_app.command("register")(self.root_register)
643
- self.root_app.command(
644
- "proposals", rich_help_panel=HELP_PANELS["ROOT"]["GOVERNANCE"]
645
- )(self.root_proposals)
646
- self.root_app.command(
647
- "set-take", rich_help_panel=HELP_PANELS["ROOT"]["DELEGATION"]
648
- )(self.root_set_take)
649
- self.root_app.command(
650
- "delegate-stake", rich_help_panel=HELP_PANELS["ROOT"]["DELEGATION"]
651
- )(self.root_delegate_stake)
652
- self.root_app.command(
653
- "undelegate-stake", rich_help_panel=HELP_PANELS["ROOT"]["DELEGATION"]
654
- )(self.root_undelegate_stake)
655
- self.root_app.command(
656
- "my-delegates", rich_help_panel=HELP_PANELS["ROOT"]["DELEGATION"]
657
- )(self.root_my_delegates)
658
- self.root_app.command(
659
- "list-delegates", rich_help_panel=HELP_PANELS["ROOT"]["DELEGATION"]
660
- )(self.root_list_delegates)
661
- self.root_app.command(
662
- "nominate", rich_help_panel=HELP_PANELS["ROOT"]["GOVERNANCE"]
663
- )(self.root_nominate)
664
-
665
692
  # stake commands
666
- self.stake_app.command(
667
- "show", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
668
- )(self.stake_show)
669
693
  self.stake_app.command(
670
694
  "add", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
671
695
  )(self.stake_add)
672
696
  self.stake_app.command(
673
697
  "remove", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
674
698
  )(self.stake_remove)
699
+ self.stake_app.command(
700
+ "list", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
701
+ )(self.stake_list)
702
+ self.stake_app.command(
703
+ "move", rich_help_panel=HELP_PANELS["STAKE"]["MOVEMENT"]
704
+ )(self.stake_move)
705
+ self.stake_app.command(
706
+ "transfer", rich_help_panel=HELP_PANELS["STAKE"]["MOVEMENT"]
707
+ )(self.stake_transfer)
708
+ self.stake_app.command(
709
+ "swap", rich_help_panel=HELP_PANELS["STAKE"]["MOVEMENT"]
710
+ )(self.stake_swap)
675
711
 
676
712
  # stake-children commands
677
713
  children_app = typer.Typer()
@@ -697,6 +733,21 @@ class CLIManager:
697
733
  self.sudo_app.command("get", rich_help_panel=HELP_PANELS["SUDO"]["CONFIG"])(
698
734
  self.sudo_get
699
735
  )
736
+ self.sudo_app.command(
737
+ "senate", rich_help_panel=HELP_PANELS["SUDO"]["GOVERNANCE"]
738
+ )(self.sudo_senate)
739
+ self.sudo_app.command(
740
+ "proposals", rich_help_panel=HELP_PANELS["SUDO"]["GOVERNANCE"]
741
+ )(self.sudo_proposals)
742
+ self.sudo_app.command(
743
+ "senate-vote", rich_help_panel=HELP_PANELS["SUDO"]["GOVERNANCE"]
744
+ )(self.sudo_senate_vote)
745
+ self.sudo_app.command("set-take", rich_help_panel=HELP_PANELS["SUDO"]["TAKE"])(
746
+ self.sudo_set_take
747
+ )
748
+ self.sudo_app.command("get-take", rich_help_panel=HELP_PANELS["SUDO"]["TAKE"])(
749
+ self.sudo_get_take
750
+ )
700
751
 
701
752
  # subnets commands
702
753
  self.subnets_app.command(
@@ -706,8 +757,8 @@ class CLIManager:
706
757
  "list", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
707
758
  )(self.subnets_list)
708
759
  self.subnets_app.command(
709
- "lock-cost", rich_help_panel=HELP_PANELS["SUBNETS"]["CREATION"]
710
- )(self.subnets_lock_cost)
760
+ "burn-cost", rich_help_panel=HELP_PANELS["SUBNETS"]["CREATION"]
761
+ )(self.subnets_burn_cost)
711
762
  self.subnets_app.command(
712
763
  "create", rich_help_panel=HELP_PANELS["SUBNETS"]["CREATION"]
713
764
  )(self.subnets_create)
@@ -718,8 +769,14 @@ class CLIManager:
718
769
  "register", rich_help_panel=HELP_PANELS["SUBNETS"]["REGISTER"]
719
770
  )(self.subnets_register)
720
771
  self.subnets_app.command(
721
- "metagraph", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
722
- )(self.subnets_metagraph)
772
+ "metagraph", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"], hidden=True
773
+ )(self.subnets_show) # Aliased to `s show` for now
774
+ self.subnets_app.command(
775
+ "show", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
776
+ )(self.subnets_show)
777
+ self.subnets_app.command(
778
+ "price", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
779
+ )(self.subnets_price)
723
780
 
724
781
  # weights commands
725
782
  self.weights_app.command(
@@ -764,22 +821,49 @@ class CLIManager:
764
821
  hidden=True,
765
822
  )(self.wallet_get_id)
766
823
 
767
- # Root
768
- self.root_app.command("set_weights", hidden=True)(self.root_set_weights)
769
- self.root_app.command("get_weights", hidden=True)(self.root_get_weights)
770
- self.root_app.command("senate_vote", hidden=True)(self.root_senate_vote)
771
- self.root_app.command("set_take", hidden=True)(self.root_set_take)
772
- self.root_app.command("delegate_stake", hidden=True)(self.root_delegate_stake)
773
- self.root_app.command("undelegate_stake", hidden=True)(
774
- self.root_undelegate_stake
775
- )
776
- self.root_app.command("my_delegates", hidden=True)(self.root_my_delegates)
777
- self.root_app.command("list_delegates", hidden=True)(self.root_list_delegates)
778
-
779
824
  # Subnets
780
- self.subnets_app.command("lock_cost", hidden=True)(self.subnets_lock_cost)
825
+ self.subnets_app.command("burn_cost", hidden=True)(self.subnets_burn_cost)
781
826
  self.subnets_app.command("pow_register", hidden=True)(self.subnets_pow_register)
782
827
 
828
+ # Sudo
829
+ self.sudo_app.command("senate_vote", hidden=True)(self.sudo_senate_vote)
830
+ self.sudo_app.command("get_take", hidden=True)(self.sudo_get_take)
831
+ self.sudo_app.command("set_take", hidden=True)(self.sudo_set_take)
832
+
833
+ def generate_command_tree(self) -> Tree:
834
+ """
835
+ Generates a rich.Tree of the commands, subcommands, and groups of this app
836
+ """
837
+
838
+ def build_rich_tree(data: dict, parent: Tree):
839
+ for group, content in data.get("groups", {}).items():
840
+ group_node = parent.add(
841
+ f"[bold cyan]{group}[/]"
842
+ ) # Add group to the tree
843
+ for command in content.get("commands", []):
844
+ group_node.add(f"[green]{command}[/]") # Add commands to the group
845
+ build_rich_tree(content, group_node) # Recurse for subgroups
846
+
847
+ def traverse_group(group: typer.Typer) -> dict:
848
+ tree = {}
849
+ if commands := [
850
+ cmd.name for cmd in group.registered_commands if not cmd.hidden
851
+ ]:
852
+ tree["commands"] = commands
853
+ for group in group.registered_groups:
854
+ if "groups" not in tree:
855
+ tree["groups"] = {}
856
+ if not group.hidden:
857
+ if group_transversal := traverse_group(group.typer_instance):
858
+ tree["groups"][group.name] = group_transversal
859
+
860
+ return tree
861
+
862
+ groups_and_commands = traverse_group(self.app)
863
+ root = Tree("[bold magenta]BTCLI Commands[/]") # Root node
864
+ build_rich_tree(groups_and_commands, root)
865
+ return root
866
+
783
867
  def initialize_chain(
784
868
  self,
785
869
  network: Optional[list[str]] = None,
@@ -810,13 +894,13 @@ class CLIManager:
810
894
  elif self.config["network"]:
811
895
  self.subtensor = SubtensorInterface(self.config["network"])
812
896
  console.print(
813
- f"Using the specified network [dark_orange]{self.config['network']}[/dark_orange] from config"
897
+ f"Using the specified network [{COLOR_PALETTE['GENERAL']['LINKS']}]{self.config['network']}[/{COLOR_PALETTE['GENERAL']['LINKS']}] from config"
814
898
  )
815
899
  else:
816
900
  self.subtensor = SubtensorInterface(defaults.subtensor.network)
817
901
  return self.subtensor
818
902
 
819
- def _run_command(self, cmd: Coroutine):
903
+ def _run_command(self, cmd: Coroutine, exit_early: bool = True):
820
904
  """
821
905
  Runs the supplied coroutine with `asyncio.run`
822
906
  """
@@ -832,7 +916,7 @@ class CLIManager:
832
916
  initiated = True
833
917
  result = await cmd
834
918
  return result
835
- except (ConnectionRefusedError, ssl.SSLError):
919
+ except (ConnectionRefusedError, ssl.SSLError, InvalidHandshake):
836
920
  err_console.print(f"Unable to connect to the chain: {self.subtensor}")
837
921
  verbose_console.print(traceback.format_exc())
838
922
  except (
@@ -849,7 +933,12 @@ class CLIManager:
849
933
  finally:
850
934
  if initiated is False:
851
935
  asyncio.create_task(cmd).cancel()
852
- raise typer.Exit()
936
+ if exit_early is True:
937
+ try:
938
+ raise typer.Exit()
939
+ except Exception as e: # ensures we always exit cleanly
940
+ if not isinstance(e, (typer.Exit, RuntimeError)): # temporarily to handle multiple run commands in one session
941
+ err_console.print(f"An unknown error has occurred: {e}")
853
942
 
854
943
  if sys.version_info < (3, 10):
855
944
  # For Python 3.9 or lower
@@ -861,11 +950,21 @@ class CLIManager:
861
950
  def main_callback(
862
951
  self,
863
952
  version: Annotated[
864
- Optional[bool], typer.Option("--version", callback=version_callback)
953
+ Optional[bool],
954
+ typer.Option(
955
+ "--version", callback=version_callback, help="Show BTCLI version"
956
+ ),
957
+ ] = None,
958
+ commands: Annotated[
959
+ Optional[bool],
960
+ typer.Option(
961
+ "--commands", callback=commands_callback, help="Show BTCLI commands"
962
+ ),
865
963
  ] = None,
866
964
  ):
867
965
  """
868
- Command line interface (CLI) for Bittensor. Uses the values in the configuration file. These values can be overriden by passing them explicitly in the command line.
966
+ Command line interface (CLI) for Bittensor. Uses the values in the configuration file. These values can be
967
+ overriden by passing them explicitly in the command line.
869
968
  """
870
969
  # Load or create the config file
871
970
  if os.path.exists(self.config_path):
@@ -1173,39 +1272,16 @@ class CLIManager:
1173
1272
  :return: created Wallet object
1174
1273
  """
1175
1274
  # Prompt for missing attributes specified in ask_for
1176
-
1177
- if wallet_path:
1178
- if wallet_path == "default":
1179
- wallet_path = defaults.wallet.path
1180
-
1181
- elif self.config.get("wallet_path"):
1182
- wallet_path = self.config.get("wallet_path")
1183
- console.print(
1184
- f"Using the wallet path from config:[bold magenta] {wallet_path}"
1185
- )
1186
-
1187
- if WO.PATH in ask_for and not wallet_path:
1188
- wallet_path = Prompt.ask(
1189
- "Enter the [blue]wallet path[/blue]"
1190
- + " [dark_sea_green3 italic](Hint: You can set this with `btcli config set --wallet-path`)[/dark_sea_green3 italic]",
1191
- default=defaults.wallet.path,
1192
- )
1193
- if wallet_path:
1194
- wallet_path = os.path.expanduser(wallet_path)
1195
- else:
1196
- wallet_path = os.path.expanduser(defaults.wallet.path)
1197
- console.print(f"Using default wallet path: ({defaults.wallet.path})")
1198
-
1199
1275
  if WO.NAME in ask_for and not wallet_name:
1200
1276
  if self.config.get("wallet_name"):
1201
1277
  wallet_name = self.config.get("wallet_name")
1202
1278
  console.print(
1203
- f"Using the wallet name from config:[bold cyan] {wallet_name}"
1279
+ f"Using the [blue]wallet name[/blue] from config:[bold cyan] {wallet_name}"
1204
1280
  )
1205
1281
  else:
1206
1282
  wallet_name = Prompt.ask(
1207
1283
  "Enter the [blue]wallet name[/blue]"
1208
- + " [dark_sea_green3 italic](Hint: You can set this with `btcli config set --wallet-name`)[/dark_sea_green3 italic]",
1284
+ + f" [{COLOR_PALETTE['GENERAL']['HINT']} italic](Hint: You can set this with `btcli config set --wallet-name`)",
1209
1285
  default=defaults.wallet.name,
1210
1286
  )
1211
1287
 
@@ -1213,7 +1289,7 @@ class CLIManager:
1213
1289
  if self.config.get("wallet_hotkey"):
1214
1290
  wallet_hotkey = self.config.get("wallet_hotkey")
1215
1291
  console.print(
1216
- f"Using the wallet hotkey from config:[bold cyan] {wallet_hotkey}"
1292
+ f"Using the [blue]wallet hotkey[/blue] from config:[bold cyan] {wallet_hotkey}"
1217
1293
  )
1218
1294
  else:
1219
1295
  wallet_hotkey = Prompt.ask(
@@ -1221,8 +1297,27 @@ class CLIManager:
1221
1297
  + " [dark_sea_green3 italic](Hint: You can set this with `btcli config set --wallet-hotkey`)[/dark_sea_green3 italic]",
1222
1298
  default=defaults.wallet.hotkey,
1223
1299
  )
1300
+ if wallet_path:
1301
+ if wallet_path == "default":
1302
+ wallet_path = defaults.wallet.path
1303
+
1304
+ elif self.config.get("wallet_path"):
1305
+ wallet_path = self.config.get("wallet_path")
1306
+ console.print(
1307
+ f"Using the [blue]wallet path[/blue] from config:[bold magenta] {wallet_path}"
1308
+ )
1309
+ else:
1310
+ wallet_path = defaults.wallet.path
1224
1311
 
1312
+ if WO.PATH in ask_for and not wallet_path:
1313
+ wallet_path = Prompt.ask(
1314
+ "Enter the [blue]wallet path[/blue]"
1315
+ + " [dark_sea_green3 italic](Hint: You can set this with `btcli config set --wallet-path`)[/dark_sea_green3 italic]",
1316
+ default=defaults.wallet.path,
1317
+ )
1225
1318
  # Create the Wallet object
1319
+ if wallet_path:
1320
+ wallet_path = os.path.expanduser(wallet_path)
1226
1321
  wallet = Wallet(name=wallet_name, path=wallet_path, hotkey=wallet_hotkey)
1227
1322
 
1228
1323
  # Validate the wallet if required
@@ -1378,7 +1473,7 @@ class CLIManager:
1378
1473
  "Netuids must be a comma-separated list of ints, e.g., `--netuids 1,2,3,4`.",
1379
1474
  )
1380
1475
 
1381
- ask_for = [WO.NAME] if not all_wallets else []
1476
+ ask_for = [WO.NAME, WO.PATH] if not all_wallets else [WO.PATH]
1382
1477
  validate = WV.WALLET if not all_wallets else WV.NONE
1383
1478
  wallet = self.wallet_ask(
1384
1479
  wallet_name, wallet_path, wallet_hotkey, ask_for=ask_for, validate=validate
@@ -1408,6 +1503,7 @@ class CLIManager:
1408
1503
  include_hotkeys,
1409
1504
  exclude_hotkeys,
1410
1505
  netuids_filter=netuids,
1506
+ verbose=verbose,
1411
1507
  )
1412
1508
  )
1413
1509
 
@@ -1425,12 +1521,9 @@ class CLIManager:
1425
1521
  None,
1426
1522
  "--amount",
1427
1523
  "-a",
1428
- prompt=False,
1524
+ prompt=True,
1429
1525
  help="Amount (in TAO) to transfer.",
1430
1526
  ),
1431
- transfer_all: bool = typer.Option(
1432
- False, "--all", prompt=False, help="Transfer all available balance."
1433
- ),
1434
1527
  wallet_name: str = Options.wallet_name,
1435
1528
  wallet_path: str = Options.wallet_path,
1436
1529
  wallet_hotkey: str = Options.wallet_hotkey,
@@ -1466,25 +1559,20 @@ class CLIManager:
1466
1559
  wallet_name,
1467
1560
  wallet_path,
1468
1561
  wallet_hotkey,
1469
- ask_for=[WO.NAME],
1562
+ ask_for=[WO.NAME, WO.PATH],
1470
1563
  validate=WV.WALLET,
1471
1564
  )
1565
+
1566
+ # For Rao games - temporarily commented out
1567
+ # effective_network = get_effective_network(self.config, network)
1568
+ # if is_rao_network(effective_network):
1569
+ # print_error("This command is disabled on the 'rao' network.")
1570
+ # raise typer.Exit()
1571
+
1472
1572
  subtensor = self.initialize_chain(network)
1473
- if transfer_all and amount:
1474
- print_error("Cannot specify an amount and '--all' flag.")
1475
- raise typer.Exit()
1476
- elif transfer_all:
1477
- amount = 0
1478
- elif not amount:
1479
- amount = FloatPrompt.ask("Enter amount (in TAO) to transfer.")
1480
1573
  return self._run_command(
1481
1574
  wallets.transfer(
1482
- wallet,
1483
- subtensor,
1484
- destination_ss58_address,
1485
- amount,
1486
- transfer_all,
1487
- prompt,
1575
+ wallet, subtensor, destination_ss58_address, amount, prompt
1488
1576
  )
1489
1577
  )
1490
1578
 
@@ -1523,7 +1611,7 @@ class CLIManager:
1523
1611
  wallet_name,
1524
1612
  wallet_path,
1525
1613
  wallet_hotkey,
1526
- ask_for=[WO.NAME, WO.HOTKEY],
1614
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
1527
1615
  validate=WV.WALLET_AND_HOTKEY,
1528
1616
  )
1529
1617
  if not destination_hotkey_name:
@@ -1535,7 +1623,7 @@ class CLIManager:
1535
1623
  wallet_name,
1536
1624
  wallet_path,
1537
1625
  destination_hotkey_name,
1538
- ask_for=[WO.NAME, WO.HOTKEY],
1626
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
1539
1627
  validate=WV.WALLET_AND_HOTKEY,
1540
1628
  )
1541
1629
  self.initialize_chain(network)
@@ -1591,6 +1679,8 @@ class CLIManager:
1591
1679
 
1592
1680
  [bold]Note[/bold]: The `inspect` command is for displaying information only and does not perform any transactions or state changes on the blockchain. It is intended to be used with Bittensor CLI and not as a standalone function in user code.
1593
1681
  """
1682
+ print_error("This command is disabled on the 'rao' network.")
1683
+ raise typer.Exit()
1594
1684
  self.verbosity_handler(quiet, verbose)
1595
1685
 
1596
1686
  if netuids:
@@ -1601,11 +1691,12 @@ class CLIManager:
1601
1691
  )
1602
1692
 
1603
1693
  # if all-wallets is entered, ask for path
1604
- ask_for = [WO.NAME] if not all_wallets else []
1694
+ ask_for = [WO.NAME, WO.PATH] if not all_wallets else [WO.PATH]
1605
1695
  validate = WV.WALLET if not all_wallets else WV.NONE
1606
1696
  wallet = self.wallet_ask(
1607
1697
  wallet_name, wallet_path, wallet_hotkey, ask_for=ask_for, validate=validate
1608
1698
  )
1699
+
1609
1700
  self.initialize_chain(network)
1610
1701
  return self._run_command(
1611
1702
  wallets.inspect(
@@ -1667,7 +1758,6 @@ class CLIManager:
1667
1758
  "--max-successes",
1668
1759
  help="Set the maximum number of times to successfully run the faucet for this command.",
1669
1760
  ),
1670
- prompt: bool = Options.prompt,
1671
1761
  ):
1672
1762
  """
1673
1763
  Obtain test TAO tokens by performing Proof of Work (PoW).
@@ -1691,7 +1781,7 @@ class CLIManager:
1691
1781
  wallet_name,
1692
1782
  wallet_path,
1693
1783
  wallet_hotkey,
1694
- ask_for=[WO.NAME],
1784
+ ask_for=[WO.NAME, WO.PATH],
1695
1785
  validate=WV.WALLET,
1696
1786
  )
1697
1787
  return self._run_command(
@@ -1706,7 +1796,6 @@ class CLIManager:
1706
1796
  output_in_place,
1707
1797
  verbose,
1708
1798
  max_successes,
1709
- prompt,
1710
1799
  )
1711
1800
  )
1712
1801
 
@@ -1720,7 +1809,6 @@ class CLIManager:
1720
1809
  json: Optional[str] = Options.json,
1721
1810
  json_password: Optional[str] = Options.json_password,
1722
1811
  use_password: Optional[bool] = Options.use_password,
1723
- overwrite: bool = Options.overwrite,
1724
1812
  quiet: bool = Options.quiet,
1725
1813
  verbose: bool = Options.verbose,
1726
1814
  ):
@@ -1750,7 +1838,8 @@ class CLIManager:
1750
1838
 
1751
1839
  if not wallet_name:
1752
1840
  wallet_name = Prompt.ask(
1753
- "Enter the name of the new wallet", default=defaults.wallet.name
1841
+ f"Enter the name of the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]new wallet (coldkey)",
1842
+ default=defaults.wallet.name,
1754
1843
  )
1755
1844
 
1756
1845
  wallet = Wallet(wallet_name, wallet_hotkey, wallet_path)
@@ -1766,7 +1855,6 @@ class CLIManager:
1766
1855
  json,
1767
1856
  json_password,
1768
1857
  use_password,
1769
- overwrite,
1770
1858
  )
1771
1859
  )
1772
1860
 
@@ -1777,7 +1865,6 @@ class CLIManager:
1777
1865
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
1778
1866
  public_key_hex: Optional[str] = Options.public_hex_key,
1779
1867
  ss58_address: Optional[str] = Options.ss58_address,
1780
- overwrite: bool = Options.overwrite,
1781
1868
  quiet: bool = Options.quiet,
1782
1869
  verbose: bool = Options.verbose,
1783
1870
  ):
@@ -1806,7 +1893,8 @@ class CLIManager:
1806
1893
 
1807
1894
  if not wallet_name:
1808
1895
  wallet_name = Prompt.ask(
1809
- "Enter the name of the new wallet", default=defaults.wallet.name
1896
+ f"Enter the name of the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]new wallet (coldkey)",
1897
+ default=defaults.wallet.name,
1810
1898
  )
1811
1899
  wallet = Wallet(wallet_name, wallet_hotkey, wallet_path)
1812
1900
 
@@ -1824,7 +1912,7 @@ class CLIManager:
1824
1912
  rich.print("[red]Error: Invalid SS58 address or public key![/red]")
1825
1913
  raise typer.Exit()
1826
1914
  return self._run_command(
1827
- wallets.regen_coldkey_pub(wallet, ss58_address, public_key_hex, overwrite)
1915
+ wallets.regen_coldkey_pub(wallet, ss58_address, public_key_hex)
1828
1916
  )
1829
1917
 
1830
1918
  def wallet_regen_hotkey(
@@ -1839,8 +1927,9 @@ class CLIManager:
1839
1927
  use_password: bool = typer.Option(
1840
1928
  False, # Overriden to False
1841
1929
  help="Set to 'True' to protect the generated Bittensor key with a password.",
1930
+ is_flag=True,
1931
+ flag_value=True,
1842
1932
  ),
1843
- overwrite: bool = Options.overwrite,
1844
1933
  quiet: bool = Options.quiet,
1845
1934
  verbose: bool = Options.verbose,
1846
1935
  ):
@@ -1879,7 +1968,6 @@ class CLIManager:
1879
1968
  json,
1880
1969
  json_password,
1881
1970
  use_password,
1882
- overwrite,
1883
1971
  )
1884
1972
  )
1885
1973
 
@@ -1897,8 +1985,10 @@ class CLIManager:
1897
1985
  use_password: bool = typer.Option(
1898
1986
  False, # Overriden to False
1899
1987
  help="Set to 'True' to protect the generated Bittensor key with a password.",
1988
+ is_flag=True,
1989
+ flag_value=True,
1900
1990
  ),
1901
- overwrite: bool = Options.overwrite,
1991
+ uri: Optional[str] = Options.uri,
1902
1992
  quiet: bool = Options.quiet,
1903
1993
  verbose: bool = Options.verbose,
1904
1994
  ):
@@ -1920,12 +2010,14 @@ class CLIManager:
1920
2010
 
1921
2011
  if not wallet_name:
1922
2012
  wallet_name = Prompt.ask(
1923
- "Enter the wallet name", default=defaults.wallet.name
2013
+ f"Enter the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]wallet name",
2014
+ default=defaults.wallet.name,
1924
2015
  )
1925
2016
 
1926
2017
  if not wallet_hotkey:
1927
2018
  wallet_hotkey = Prompt.ask(
1928
- "Enter the name of the new hotkey", default=defaults.wallet.hotkey
2019
+ f"Enter the name of the [{COLOR_PALETTE['GENERAL']['HOTKEY']}]new hotkey",
2020
+ default=defaults.wallet.hotkey,
1929
2021
  )
1930
2022
 
1931
2023
  wallet = self.wallet_ask(
@@ -1935,10 +2027,9 @@ class CLIManager:
1935
2027
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
1936
2028
  validate=WV.WALLET,
1937
2029
  )
1938
- n_words = get_n_words(n_words)
1939
- return self._run_command(
1940
- wallets.new_hotkey(wallet, n_words, use_password, overwrite)
1941
- )
2030
+ if not uri:
2031
+ n_words = get_n_words(n_words)
2032
+ return self._run_command(wallets.new_hotkey(wallet, n_words, use_password, uri))
1942
2033
 
1943
2034
  def wallet_new_coldkey(
1944
2035
  self,
@@ -1952,7 +2043,7 @@ class CLIManager:
1952
2043
  help="The number of words used in the mnemonic. Options: [12, 15, 18, 21, 24]",
1953
2044
  ),
1954
2045
  use_password: Optional[bool] = Options.use_password,
1955
- overwrite: bool = Options.overwrite,
2046
+ uri: Optional[str] = Options.uri,
1956
2047
  quiet: bool = Options.quiet,
1957
2048
  verbose: bool = Options.verbose,
1958
2049
  ):
@@ -1978,7 +2069,8 @@ class CLIManager:
1978
2069
 
1979
2070
  if not wallet_name:
1980
2071
  wallet_name = Prompt.ask(
1981
- "Enter the name of the new wallet", default=defaults.wallet.name
2072
+ f"Enter the name of the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]new wallet (coldkey)",
2073
+ default=defaults.wallet.name,
1982
2074
  )
1983
2075
 
1984
2076
  wallet = self.wallet_ask(
@@ -1988,9 +2080,10 @@ class CLIManager:
1988
2080
  ask_for=[WO.NAME, WO.PATH],
1989
2081
  validate=WV.NONE,
1990
2082
  )
1991
- n_words = get_n_words(n_words)
2083
+ if not uri:
2084
+ n_words = get_n_words(n_words)
1992
2085
  return self._run_command(
1993
- wallets.new_coldkey(wallet, n_words, use_password, overwrite)
2086
+ wallets.new_coldkey(wallet, n_words, use_password, uri)
1994
2087
  )
1995
2088
 
1996
2089
  def wallet_check_ck_swap(
@@ -2025,7 +2118,7 @@ class CLIManager:
2025
2118
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2026
2119
  n_words: Optional[int] = None,
2027
2120
  use_password: bool = Options.use_password,
2028
- overwrite: bool = Options.overwrite,
2121
+ uri: Optional[str] = Options.uri,
2029
2122
  quiet: bool = Options.quiet,
2030
2123
  verbose: bool = Options.verbose,
2031
2124
  ):
@@ -2049,12 +2142,13 @@ class CLIManager:
2049
2142
 
2050
2143
  if not wallet_name:
2051
2144
  wallet_name = Prompt.ask(
2052
- "Enter the name of the new wallet (coldkey)",
2145
+ f"Enter the name of the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]new wallet (coldkey)",
2053
2146
  default=defaults.wallet.name,
2054
2147
  )
2055
2148
  if not wallet_hotkey:
2056
2149
  wallet_hotkey = Prompt.ask(
2057
- "Enter the the name of the new hotkey", default=defaults.wallet.hotkey
2150
+ f"Enter the the name of the [{COLOR_PALETTE['GENERAL']['HOTKEY']}]new hotkey",
2151
+ default=defaults.wallet.hotkey,
2058
2152
  )
2059
2153
 
2060
2154
  self.verbosity_handler(quiet, verbose)
@@ -2065,13 +2159,14 @@ class CLIManager:
2065
2159
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
2066
2160
  validate=WV.NONE,
2067
2161
  )
2068
- n_words = get_n_words(n_words)
2162
+ if not uri:
2163
+ n_words = get_n_words(n_words)
2069
2164
  return self._run_command(
2070
2165
  wallets.wallet_create(
2071
2166
  wallet,
2072
2167
  n_words,
2073
2168
  use_password,
2074
- overwrite,
2169
+ uri,
2075
2170
  )
2076
2171
  )
2077
2172
 
@@ -2116,8 +2211,18 @@ class CLIManager:
2116
2211
 
2117
2212
  """
2118
2213
  self.verbosity_handler(quiet, verbose)
2119
-
2120
- if ss58_addresses:
2214
+ wallet = None
2215
+ if all_balances:
2216
+ ask_for = [WO.PATH]
2217
+ validate = WV.NONE
2218
+ wallet = self.wallet_ask(
2219
+ wallet_name,
2220
+ wallet_path,
2221
+ wallet_hotkey,
2222
+ ask_for=ask_for,
2223
+ validate=validate,
2224
+ )
2225
+ elif ss58_addresses:
2121
2226
  valid_ss58s = [
2122
2227
  ss58 for ss58 in set(ss58_addresses) if is_valid_ss58_address(ss58)
2123
2228
  ]
@@ -2127,20 +2232,45 @@ class CLIManager:
2127
2232
  print_error(f"Incorrect ss58 address: {invalid_ss58}. Skipping.")
2128
2233
 
2129
2234
  if valid_ss58s:
2130
- wallet = None
2131
2235
  ss58_addresses = valid_ss58s
2132
2236
  else:
2133
2237
  raise typer.Exit()
2134
2238
  else:
2135
- ask_for = [] if all_balances else [WO.NAME]
2136
- validate = WV.NONE if all_balances else WV.WALLET
2137
- wallet = self.wallet_ask(
2138
- wallet_name,
2139
- wallet_path,
2140
- wallet_hotkey,
2141
- ask_for=ask_for,
2142
- validate=validate,
2143
- )
2239
+ if wallet_name:
2240
+ coldkey_or_ss58 = wallet_name
2241
+ else:
2242
+ coldkey_or_ss58 = Prompt.ask(
2243
+ "Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 addresses[/blue] (comma-separated)",
2244
+ default=self.config.get("wallet_name") or defaults.wallet.name,
2245
+ )
2246
+
2247
+ # Split and validate ss58 addresses
2248
+ coldkey_or_ss58_list = [x.strip() for x in coldkey_or_ss58.split(",")]
2249
+ if any(is_valid_ss58_address(x) for x in coldkey_or_ss58_list):
2250
+ valid_ss58s = [
2251
+ ss58 for ss58 in coldkey_or_ss58_list if is_valid_ss58_address(ss58)
2252
+ ]
2253
+ invalid_ss58s = set(coldkey_or_ss58_list) - set(valid_ss58s)
2254
+ for invalid_ss58 in invalid_ss58s:
2255
+ print_error(f"Incorrect ss58 address: {invalid_ss58}. Skipping.")
2256
+
2257
+ if valid_ss58s:
2258
+ ss58_addresses = valid_ss58s
2259
+ else:
2260
+ raise typer.Exit()
2261
+ else:
2262
+ wallet_name = (
2263
+ coldkey_or_ss58_list[0] if coldkey_or_ss58_list else wallet_name
2264
+ )
2265
+ ask_for = [WO.NAME, WO.PATH]
2266
+ validate = WV.WALLET
2267
+ wallet = self.wallet_ask(
2268
+ wallet_name,
2269
+ wallet_path,
2270
+ wallet_hotkey,
2271
+ ask_for=ask_for,
2272
+ validate=validate,
2273
+ )
2144
2274
  subtensor = self.initialize_chain(network)
2145
2275
  return self._run_command(
2146
2276
  wallets.wallet_balance(wallet, subtensor, all_balances, ss58_addresses)
@@ -2166,19 +2296,23 @@ class CLIManager:
2166
2296
  [green]$[/green] btcli wallet history
2167
2297
 
2168
2298
  """
2299
+ # TODO: Fetch effective network and redirect users accordingly - this only works on finney
2300
+ # no_use_config_str = "Using the network [dark_orange]finney[/dark_orange] and ignoring network/chain configs"
2169
2301
 
2170
- no_use_config_str = "Using the network [dark_orange]finney[/dark_orange] and ignoring network/chain configs"
2302
+ # if self.config.get("network"):
2303
+ # if self.config.get("network") != "finney":
2304
+ # console.print(no_use_config_str)
2171
2305
 
2172
- if self.config.get("network"):
2173
- if self.config.get("network") != "finney":
2174
- console.print(no_use_config_str)
2306
+ # For Rao games
2307
+ print_error("This command is disabled on the 'rao' network.")
2308
+ raise typer.Exit()
2175
2309
 
2176
2310
  self.verbosity_handler(quiet, verbose)
2177
2311
  wallet = self.wallet_ask(
2178
2312
  wallet_name,
2179
2313
  wallet_path,
2180
2314
  wallet_hotkey,
2181
- ask_for=[WO.NAME],
2315
+ ask_for=[WO.NAME, WO.PATH],
2182
2316
  validate=WV.WALLET,
2183
2317
  )
2184
2318
  return self._run_command(wallets.wallet_history(wallet))
@@ -2189,69 +2323,42 @@ class CLIManager:
2189
2323
  wallet_path: Optional[str] = Options.wallet_path,
2190
2324
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2191
2325
  network: Optional[list[str]] = Options.network,
2192
- display_name: str = typer.Option(
2326
+ name: str = typer.Option(
2193
2327
  "",
2194
- "--display-name",
2195
- "--display",
2328
+ "--name",
2196
2329
  help="The display name for the identity.",
2197
2330
  ),
2198
- legal_name: str = typer.Option(
2199
- "",
2200
- "--legal-name",
2201
- "--legal",
2202
- help="The legal name for the identity.",
2203
- ),
2204
2331
  web_url: str = typer.Option(
2205
2332
  "",
2206
2333
  "--web-url",
2207
2334
  "--web",
2208
2335
  help="The web URL for the identity.",
2209
2336
  ),
2210
- riot_handle: str = typer.Option(
2211
- "",
2212
- "--riot-handle",
2213
- "--riot",
2214
- help="The Riot handle for the identity.",
2215
- ),
2216
- email: str = typer.Option(
2217
- "",
2218
- help="The email address for the identity.",
2219
- ),
2220
- pgp_fingerprint: str = typer.Option(
2221
- "",
2222
- "--pgp-fingerprint",
2223
- "--pgp",
2224
- help="The PGP fingerprint for the identity.",
2225
- ),
2226
2337
  image_url: str = typer.Option(
2227
2338
  "",
2228
2339
  "--image-url",
2229
2340
  "--image",
2230
2341
  help="The image URL for the identity.",
2231
2342
  ),
2232
- info_: str = typer.Option(
2343
+ discord: str = typer.Option(
2233
2344
  "",
2234
- "--info",
2235
- "-i",
2236
- help="The info for the identity.",
2345
+ "--discord",
2346
+ help="The Discord handle for the identity.",
2237
2347
  ),
2238
- twitter_url: str = typer.Option(
2348
+ description: str = typer.Option(
2239
2349
  "",
2240
- "-x",
2241
- "-𝕏",
2242
- "--twitter-url",
2243
- "--twitter",
2244
- help="The 𝕏 (Twitter) URL for the identity.",
2350
+ "--description",
2351
+ help="The description for the identity.",
2245
2352
  ),
2246
- validator_id: Optional[bool] = typer.Option(
2247
- None,
2248
- "--validator/--not-validator",
2249
- help="Are you updating a validator hotkey identity?",
2353
+ additional: str = typer.Option(
2354
+ "",
2355
+ "--additional",
2356
+ help="Additional details for the identity.",
2250
2357
  ),
2251
- subnet_netuid: Optional[int] = typer.Option(
2252
- None,
2253
- "--netuid",
2254
- help="Netuid if you are updating identity of a subnet owner",
2358
+ github_repo: str = typer.Option(
2359
+ "",
2360
+ "--github",
2361
+ help="The GitHub repository for the identity.",
2255
2362
  ),
2256
2363
  quiet: bool = Options.quiet,
2257
2364
  verbose: bool = Options.verbose,
@@ -2279,94 +2386,67 @@ class CLIManager:
2279
2386
  wallet_name,
2280
2387
  wallet_path,
2281
2388
  wallet_hotkey,
2282
- ask_for=[WO.HOTKEY, WO.NAME],
2283
- validate=WV.WALLET_AND_HOTKEY,
2389
+ ask_for=[WO.NAME],
2390
+ validate=WV.WALLET,
2284
2391
  )
2285
2392
 
2286
- if not any(
2287
- [
2288
- display_name,
2289
- legal_name,
2290
- web_url,
2291
- riot_handle,
2292
- email,
2293
- pgp_fingerprint,
2294
- image_url,
2295
- info_,
2296
- twitter_url,
2297
- ]
2298
- ):
2299
- console.print(
2300
- "[yellow]All fields are optional. Press Enter to skip a field.[/yellow]"
2301
- )
2302
- text_rejection = partial(
2303
- retry_prompt,
2304
- rejection=lambda x: sys.getsizeof(x) > 113,
2305
- rejection_text="[red]Error:[/red] Identity field must be <= 64 raw bytes.",
2306
- )
2393
+ current_identity = self._run_command(
2394
+ wallets.get_id(
2395
+ self.initialize_chain(network),
2396
+ wallet.coldkeypub.ss58_address,
2397
+ "Current on-chain identity",
2398
+ ),
2399
+ exit_early=False,
2400
+ )
2307
2401
 
2308
- def pgp_check(s: str):
2309
- try:
2310
- if s.startswith("0x"):
2311
- s = s[2:] # Strip '0x'
2312
- pgp_fingerprint_encoded = binascii.unhexlify(s.replace(" ", ""))
2313
- except Exception:
2314
- return True
2315
- return True if len(pgp_fingerprint_encoded) != 20 else False
2316
-
2317
- display_name = display_name or text_rejection("Display name")
2318
- legal_name = legal_name or text_rejection("Legal name")
2319
- web_url = web_url or text_rejection("Web URL")
2320
- riot_handle = riot_handle or text_rejection("Riot handle")
2321
- email = email or text_rejection("Email address")
2322
- pgp_fingerprint = pgp_fingerprint or retry_prompt(
2323
- "PGP fingerprint (Eg: A1B2 C3D4 E5F6 7890 1234 5678 9ABC DEF0 1234 5678)",
2324
- lambda s: False if not s else pgp_check(s),
2325
- "[red]Error:[/red] PGP Fingerprint must be exactly 20 bytes.",
2326
- )
2327
- image_url = image_url or text_rejection("Image URL")
2328
- info_ = info_ or text_rejection("Enter info")
2329
- twitter_url = twitter_url or text_rejection("𝕏 (Twitter) URL")
2330
-
2331
- validator_id = validator_id or Confirm.ask(
2332
- "Are you updating a [bold blue]validator hotkey[/bold blue] identity or a [bold blue]subnet "
2333
- "owner[/bold blue] identity?\n"
2334
- "Enter [bold green]Y[/bold green] for [bold]validator hotkey[/bold] or [bold red]N[/bold red] for "
2335
- "[bold]subnet owner[/bold]",
2336
- show_choices=True,
2337
- )
2402
+ if prompt:
2403
+ if not Confirm.ask(
2404
+ "Cost to register an [blue]Identity[/blue] is [blue]0.1 TAO[/blue],"
2405
+ " are you sure you wish to continue?"
2406
+ ):
2407
+ console.print(":cross_mark: Aborted!")
2408
+ raise typer.Exit()
2338
2409
 
2339
- if validator_id is False:
2340
- subnet_netuid = IntPrompt.ask("Enter the netuid of the subnet you own")
2410
+ identity = prompt_for_identity(
2411
+ current_identity,
2412
+ name,
2413
+ web_url,
2414
+ image_url,
2415
+ discord,
2416
+ description,
2417
+ additional,
2418
+ github_repo,
2419
+ )
2341
2420
 
2342
2421
  return self._run_command(
2343
2422
  wallets.set_id(
2344
2423
  wallet,
2345
2424
  self.initialize_chain(network),
2346
- display_name,
2347
- legal_name,
2348
- web_url,
2349
- pgp_fingerprint,
2350
- riot_handle,
2351
- email,
2352
- image_url,
2353
- twitter_url,
2354
- info_,
2355
- validator_id,
2356
- subnet_netuid,
2425
+ identity["name"],
2426
+ identity["url"],
2427
+ identity["image"],
2428
+ identity["discord"],
2429
+ identity["description"],
2430
+ identity["additional"],
2431
+ identity["github_repo"],
2357
2432
  prompt,
2358
2433
  )
2359
2434
  )
2360
2435
 
2361
2436
  def wallet_get_id(
2362
2437
  self,
2363
- target_ss58_address: str = typer.Option(
2438
+ wallet_name: Optional[str] = Options.wallet_name,
2439
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2440
+ wallet_path: Optional[str] = Options.wallet_path,
2441
+ coldkey_ss58=typer.Option(
2364
2442
  None,
2443
+ "--ss58",
2444
+ "--coldkey_ss58",
2445
+ "--coldkey.ss58_address",
2446
+ "--coldkey.ss58",
2365
2447
  "--key",
2366
2448
  "-k",
2367
- "--ss58",
2368
- help="The coldkey or hotkey ss58 address to query.",
2369
- prompt=True,
2449
+ help="Coldkey address of the wallet",
2370
2450
  ),
2371
2451
  network: Optional[list[str]] = Options.network,
2372
2452
  quiet: bool = Options.quiet,
@@ -2389,13 +2469,28 @@ class CLIManager:
2389
2469
 
2390
2470
  [bold]Note[/bold]: This command is primarily used for informational purposes and has no side effects on the blockchain network state.
2391
2471
  """
2392
- if not is_valid_ss58_address(target_ss58_address):
2393
- print_error("You have entered an incorrect ss58 address. Please try again")
2394
- raise typer.Exit()
2472
+ wallet = None
2473
+ if coldkey_ss58:
2474
+ if not is_valid_ss58_address(coldkey_ss58):
2475
+ print_error("You entered an invalid ss58 address")
2476
+ raise typer.Exit()
2477
+ else:
2478
+ coldkey_or_ss58 = Prompt.ask(
2479
+ "Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 address[/blue]",
2480
+ default=self.config.get("wallet_name") or defaults.wallet.name,
2481
+ )
2482
+ if is_valid_ss58_address(coldkey_or_ss58):
2483
+ coldkey_ss58 = coldkey_or_ss58
2484
+ else:
2485
+ wallet_name = coldkey_or_ss58 if coldkey_or_ss58 else wallet_name
2486
+ wallet = self.wallet_ask(
2487
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
2488
+ )
2489
+ coldkey_ss58 = wallet.coldkeypub.ss58_address
2395
2490
 
2396
2491
  self.verbosity_handler(quiet, verbose)
2397
2492
  return self._run_command(
2398
- wallets.get_id(self.initialize_chain(network), target_ss58_address)
2493
+ wallets.get_id(self.initialize_chain(network), coldkey_ss58)
2399
2494
  )
2400
2495
 
2401
2496
  def wallet_sign(
@@ -2429,872 +2524,117 @@ class CLIManager:
2429
2524
  self.verbosity_handler(quiet, verbose)
2430
2525
  if use_hotkey is None:
2431
2526
  use_hotkey = Confirm.ask(
2432
- "Would you like to sign the transaction using your [red]hotkey[/red]?"
2433
- "\n[Type [red]y[/red] for [red]hotkey[/red] and [blue]n[/blue] for [blue]coldkey[/blue]] "
2434
- "(default is [blue]coldkey[/blue])",
2527
+ f"Would you like to sign the transaction using your [{COLOR_PALETTE['GENERAL']['HOTKEY']}]hotkey[/{COLOR_PALETTE['GENERAL']['HOTKEY']}]?"
2528
+ f"\n[Type [{COLOR_PALETTE['GENERAL']['HOTKEY']}]y[/{COLOR_PALETTE['GENERAL']['HOTKEY']}] for [{COLOR_PALETTE['GENERAL']['HOTKEY']}]hotkey[/{COLOR_PALETTE['GENERAL']['HOTKEY']}]"
2529
+ f" and [{COLOR_PALETTE['GENERAL']['COLDKEY']}]n[/{COLOR_PALETTE['GENERAL']['COLDKEY']}] for [{COLOR_PALETTE['GENERAL']['COLDKEY']}]coldkey[/{COLOR_PALETTE['GENERAL']['COLDKEY']}]] (default is [{COLOR_PALETTE['GENERAL']['COLDKEY']}]coldkey[/{COLOR_PALETTE['GENERAL']['COLDKEY']}])",
2435
2530
  default=False,
2436
2531
  )
2437
2532
 
2438
- ask_for = [WO.HOTKEY, WO.NAME] if use_hotkey else [WO.NAME]
2533
+ ask_for = [WO.HOTKEY, WO.PATH, WO.NAME] if use_hotkey else [WO.NAME, WO.PATH]
2439
2534
  validate = WV.WALLET_AND_HOTKEY if use_hotkey else WV.WALLET
2440
2535
 
2441
2536
  wallet = self.wallet_ask(
2442
2537
  wallet_name, wallet_path, wallet_hotkey, ask_for=ask_for, validate=validate
2443
2538
  )
2444
2539
  if not message:
2445
- message = typer.prompt("Enter the message to encode and sign")
2540
+ message = Prompt.ask("Enter the [blue]message[/blue] to encode and sign")
2446
2541
 
2447
2542
  return self._run_command(wallets.sign(wallet, message, use_hotkey))
2448
2543
 
2449
- def root_list(
2544
+ def stake_list(
2450
2545
  self,
2451
2546
  network: Optional[list[str]] = Options.network,
2547
+ wallet_name: Optional[str] = Options.wallet_name,
2548
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2549
+ wallet_path: Optional[str] = Options.wallet_path,
2550
+ coldkey_ss58=typer.Option(
2551
+ None,
2552
+ "--ss58",
2553
+ "--coldkey_ss58",
2554
+ "--coldkey.ss58_address",
2555
+ "--coldkey.ss58",
2556
+ help="Coldkey address of the wallet",
2557
+ ),
2558
+ live: bool = Options.live,
2452
2559
  quiet: bool = Options.quiet,
2453
2560
  verbose: bool = Options.verbose,
2561
+ no_prompt: bool = Options.prompt,
2562
+ # TODO add: all-wallets, reuse_last, html_output
2454
2563
  ):
2455
- """
2456
- Show the neurons (root network validators) in the root network (netuid = 0).
2457
-
2458
- USAGE
2459
-
2460
- The command fetches and lists the neurons (root network validators) in the root network, showing their unique identifiers (UIDs), names, addresses, stakes, and whether they are part of the senate (network governance body).
2461
-
2462
- This command is useful for understanding the composition and governance structure of the Bittensor network's root network. It provides insights into which neurons hold significant influence and responsibility within the Bittensor network.
2564
+ """List all stake accounts for wallet."""
2565
+ self.verbosity_handler(quiet, verbose)
2463
2566
 
2464
- EXAMPLE
2567
+ wallet = None
2568
+ if coldkey_ss58:
2569
+ if not is_valid_ss58_address(coldkey_ss58):
2570
+ print_error("You entered an invalid ss58 address")
2571
+ raise typer.Exit()
2572
+ else:
2573
+ if wallet_name:
2574
+ coldkey_or_ss58 = wallet_name
2575
+ else:
2576
+ coldkey_or_ss58 = Prompt.ask(
2577
+ "Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 address[/blue]",
2578
+ default=self.config.get("wallet_name") or defaults.wallet.name,
2579
+ )
2580
+ if is_valid_ss58_address(coldkey_or_ss58):
2581
+ coldkey_ss58 = coldkey_or_ss58
2582
+ else:
2583
+ wallet_name = coldkey_or_ss58 if coldkey_or_ss58 else wallet_name
2584
+ wallet = self.wallet_ask(
2585
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
2586
+ )
2465
2587
 
2466
- [green]$[/green] btcli root list
2467
- """
2468
- self.verbosity_handler(quiet, verbose)
2469
2588
  return self._run_command(
2470
- root.root_list(subtensor=self.initialize_chain(network))
2589
+ stake.stake_list(
2590
+ wallet,
2591
+ coldkey_ss58,
2592
+ self.initialize_chain(network),
2593
+ live,
2594
+ verbose,
2595
+ no_prompt,
2596
+ )
2471
2597
  )
2472
2598
 
2473
- def root_set_weights(
2599
+ def stake_add(
2474
2600
  self,
2475
- network: Optional[list[str]] = Options.network,
2476
- wallet_name: str = Options.wallet_name,
2477
- wallet_path: str = Options.wallet_path,
2478
- wallet_hotkey: str = Options.wallet_hotkey,
2479
- netuids=typer.Option(
2480
- None,
2481
- "--netuids",
2482
- "--netuid",
2483
- "-n",
2484
- help="Set the netuid(s) to set weights to. Separate multiple netuids with a comma, for example: `-n 0,1,2`.",
2485
- ),
2486
- weights: str = Options.weights,
2487
- prompt: bool = Options.prompt,
2488
- quiet: bool = Options.quiet,
2489
- verbose: bool = Options.verbose,
2490
- ):
2491
- """
2492
- Set the weights for different subnets, by setting them in the root network.
2493
-
2494
- To use this command, you should specify the netuids and corresponding weights you wish to assign. This command is used by validators registered to the root subnet to influence the distribution of subnet rewards and responsibilities.
2495
-
2496
- You must have a comprehensive understanding of the dynamics of the subnets to use this command. It is a powerful tool that directly impacts the subnet's operational mechanics and reward distribution.
2497
-
2498
- EXAMPLE
2499
-
2500
- With no spaces between the passed values:
2501
-
2502
- [green]$[/green] btcli root set-weights --netuids 1,2 --weights 0.2,0.3
2503
-
2504
- or
2505
-
2506
- Include double quotes to include spaces between the passed values:
2507
-
2508
- [green]$[/green] btcli root set-weights --netuids "1, 2" --weights "0.2, 0.3"
2509
- """
2510
- self.verbosity_handler(quiet, verbose)
2511
-
2512
- if netuids:
2513
- netuids = parse_to_list(
2514
- netuids,
2515
- int,
2516
- "Netuids must be a comma-separated list of ints, e.g., `--netuid 1,2,3,4`.",
2517
- )
2518
- else:
2519
- netuids = list_prompt(netuids, int, "Enter netuids (e.g: 1, 4, 6)")
2520
-
2521
- if weights:
2522
- weights = parse_to_list(
2523
- weights,
2524
- float,
2525
- "Weights must be a comma-separated list of floats, e.g., `--weights 0.3,0.4,0.3`.",
2526
- )
2527
- else:
2528
- weights = list_prompt(
2529
- weights, float, "Enter weights (e.g. 0.02, 0.03, 0.01)"
2530
- )
2531
-
2532
- if len(netuids) != len(weights):
2533
- raise typer.BadParameter(
2534
- "The number of netuids and weights must be the same."
2535
- )
2536
-
2537
- wallet = self.wallet_ask(
2538
- wallet_name,
2539
- wallet_path,
2540
- wallet_hotkey,
2541
- ask_for=[WO.HOTKEY, WO.NAME],
2542
- validate=WV.WALLET_AND_HOTKEY,
2543
- )
2544
- self._run_command(
2545
- root.set_weights(
2546
- wallet, self.initialize_chain(network), netuids, weights, prompt
2547
- )
2548
- )
2549
-
2550
- def root_get_weights(
2551
- self,
2552
- network: Optional[list[str]] = Options.network,
2553
- limit_min_col: Optional[int] = typer.Option(
2554
- None,
2555
- "--limit-min-col",
2556
- "--min",
2557
- help="Limit the left display of the table to this column.",
2558
- ),
2559
- limit_max_col: Optional[int] = typer.Option(
2560
- None,
2561
- "--limit-max-col",
2562
- "--max",
2563
- help="Limit the right display of the table to this column.",
2564
- ),
2565
- reuse_last: bool = Options.reuse_last,
2566
- html_output: bool = Options.html_output,
2567
- quiet: bool = Options.quiet,
2568
- verbose: bool = Options.verbose,
2569
- ):
2570
- """
2571
- Shows a table listing the weights assigned to each subnet in the root network.
2572
-
2573
- This command provides visibility into how network responsibilities and rewards are distributed among various subnets. This information is crucial for understanding the current influence and reward distribution across different subnets. Use this command if you are interested in the governance and operational dynamics of the Bittensor network.
2574
-
2575
- EXAMPLE
2576
-
2577
- [green]$[/green] btcli root get_weights
2578
- """
2579
- self.verbosity_handler(quiet, verbose)
2580
- if (reuse_last or html_output) and self.config.get("use_cache") is False:
2581
- err_console.print(
2582
- "Unable to use `--reuse-last` or `--html` when config 'no-cache' is set to 'True'."
2583
- "Change it to 'False' using `btcli config set`."
2584
- )
2585
- raise typer.Exit()
2586
- if not reuse_last:
2587
- subtensor = self.initialize_chain(network)
2588
- else:
2589
- subtensor = None
2590
- return self._run_command(
2591
- root.get_weights(
2592
- subtensor,
2593
- limit_min_col,
2594
- limit_max_col,
2595
- reuse_last,
2596
- html_output,
2597
- not self.config.get("use_cache", True),
2598
- )
2599
- )
2600
-
2601
- def root_boost(
2602
- self,
2603
- network: Optional[list[str]] = Options.network,
2604
- wallet_name: str = Options.wallet_name,
2605
- wallet_path: Optional[str] = Options.wallet_path,
2606
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2607
- netuid: int = Options.netuid,
2608
- amount: float = typer.Option(
2609
- None,
2610
- "--amount",
2611
- "--increase",
2612
- "-a",
2613
- prompt="Enter the boost amount (added to existing weight)",
2614
- help="Amount (float) to boost (added to the existing weight), (e.g. 0.01)",
2615
- ),
2616
- prompt: bool = Options.prompt,
2617
- quiet: bool = Options.quiet,
2618
- verbose: bool = Options.verbose,
2619
- ):
2620
- """
2621
- Increase (boost) the weights for a specific subnet in the root network. Any amount provided will be added to the subnet's existing weight.
2622
-
2623
- EXAMPLE
2624
-
2625
- [green]$[/green] btcli root boost --netuid 1 --increase 0.01
2626
- """
2627
- self.verbosity_handler(quiet, verbose)
2628
- wallet = self.wallet_ask(
2629
- wallet_name,
2630
- wallet_path,
2631
- wallet_hotkey,
2632
- ask_for=[WO.NAME, WO.HOTKEY],
2633
- validate=WV.WALLET_AND_HOTKEY,
2634
- )
2635
- return self._run_command(
2636
- root.set_boost(
2637
- wallet, self.initialize_chain(network), netuid, amount, prompt
2638
- )
2639
- )
2640
-
2641
- def root_slash(
2642
- self,
2643
- network: Optional[list[str]] = Options.network,
2644
- wallet_name: str = Options.wallet_name,
2645
- wallet_path: Optional[str] = Options.wallet_path,
2646
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2647
- netuid: int = Options.netuid,
2648
- amount: float = typer.Option(
2649
- None,
2650
- "--amount",
2651
- "--decrease",
2652
- "-a",
2653
- prompt="Enter the slash amount (subtracted from the existing weight)",
2654
- help="Amount (float) to slash (subtract from the existing weight), (e.g. 0.01)",
2655
- ),
2656
- prompt: bool = Options.prompt,
2657
- quiet: bool = Options.quiet,
2658
- verbose: bool = Options.verbose,
2659
- ):
2660
- """
2661
- Decrease (slash) the weights for a specific subnet in the root network. Any amount provided will be subtracted from the subnet's existing weight.
2662
-
2663
- EXAMPLE
2664
-
2665
- [green]$[/green] btcli root slash --netuid 1 --decrease 0.01
2666
-
2667
- """
2668
- self.verbosity_handler(quiet, verbose)
2669
- wallet = self.wallet_ask(
2670
- wallet_name,
2671
- wallet_path,
2672
- wallet_hotkey,
2673
- ask_for=[WO.NAME, WO.HOTKEY],
2674
- validate=WV.WALLET_AND_HOTKEY,
2675
- )
2676
- return self._run_command(
2677
- root.set_slash(
2678
- wallet, self.initialize_chain(network), netuid, amount, prompt
2679
- )
2680
- )
2681
-
2682
- def root_senate_vote(
2683
- self,
2684
- network: Optional[list[str]] = Options.network,
2685
- wallet_name: Optional[str] = Options.wallet_name,
2686
- wallet_path: Optional[str] = Options.wallet_path,
2687
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2688
- proposal: str = typer.Option(
2689
- None,
2690
- "--proposal",
2691
- "--proposal-hash",
2692
- prompt="Enter the proposal hash",
2693
- help="The hash of the proposal to vote on.",
2694
- ),
2695
- prompt: bool = Options.prompt,
2696
- quiet: bool = Options.quiet,
2697
- verbose: bool = Options.verbose,
2698
- vote: bool = typer.Option(
2699
- None,
2700
- "--vote-aye/--vote-nay",
2701
- prompt="Enter y to vote Aye, or enter n to vote Nay",
2702
- help="The vote casted on the proposal",
2703
- ),
2704
- ):
2705
- """
2706
- Cast a vote on an active proposal in Bittensor's governance protocol.
2707
-
2708
- This command is used by Senate members to vote on various proposals that shape the network's future. Use `btcli root proposals` to see the active proposals and their hashes.
2709
-
2710
- USAGE
2711
-
2712
- The user must specify the hash of the proposal they want to vote on. The command then allows the Senate member to cast a 'Yes' or 'No' vote, contributing to the decision-making process on the proposal. This command is crucial for Senate members to exercise their voting rights on key proposals. It plays a vital role in the governance and evolution of the Bittensor network.
2713
-
2714
- EXAMPLE
2715
-
2716
- [green]$[/green] btcli root senate_vote --proposal <proposal_hash>
2717
- """
2718
- self.verbosity_handler(quiet, verbose)
2719
- wallet = self.wallet_ask(
2720
- wallet_name,
2721
- wallet_path,
2722
- wallet_hotkey,
2723
- ask_for=[WO.NAME, WO.HOTKEY],
2724
- validate=WV.WALLET_AND_HOTKEY,
2725
- )
2726
- return self._run_command(
2727
- root.senate_vote(
2728
- wallet, self.initialize_chain(network), proposal, vote, prompt
2729
- )
2730
- )
2731
-
2732
- def root_senate(
2733
- self,
2734
- network: Optional[list[str]] = Options.network,
2735
- quiet: bool = Options.quiet,
2736
- verbose: bool = Options.verbose,
2737
- ):
2738
- """
2739
- Shows the Senate members of the Bittensor's governance protocol.
2740
-
2741
- This command lists the delegates involved in the decision-making process of the Bittensor network, showing their names and wallet addresses. This information is crucial for understanding who holds governance roles within the network.
2742
-
2743
- EXAMPLE
2744
-
2745
- [green]$[/green] btcli root senate
2746
- """
2747
- self.verbosity_handler(quiet, verbose)
2748
- return self._run_command(root.get_senate(self.initialize_chain(network)))
2749
-
2750
- def root_register(
2751
- self,
2752
- network: Optional[list[str]] = Options.network,
2753
- wallet_name: Optional[str] = Options.wallet_name,
2754
- wallet_path: Optional[str] = Options.wallet_path,
2755
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2756
- prompt: bool = Options.prompt,
2757
- quiet: bool = Options.quiet,
2758
- verbose: bool = Options.verbose,
2759
- ):
2760
- """
2761
- Register a neuron to the root subnet by recycling some TAO to cover for the registration cost.
2762
-
2763
- This command adds a new neuron as a validator on the root network. This will allow the neuron owner to set subnet weights.
2764
-
2765
- # Usage:
2766
-
2767
- Before registering, the command checks if the specified subnet exists and whether the TAO balance in the user's wallet is sufficient to cover the registration cost. The registration cost is determined by the current recycle amount for the specified subnet. If the balance is insufficient or the subnet does not exist, the command will exit with an appropriate error message.
2768
-
2769
- # Example usage:
2770
-
2771
- [green]$[/green] btcli subnets register --netuid 1
2772
- """
2773
- self.verbosity_handler(quiet, verbose)
2774
- wallet = self.wallet_ask(
2775
- wallet_name,
2776
- wallet_path,
2777
- wallet_hotkey,
2778
- ask_for=[WO.NAME, WO.HOTKEY],
2779
- validate=WV.WALLET_AND_HOTKEY,
2780
- )
2781
- return self._run_command(
2782
- root.register(wallet, self.initialize_chain(network), prompt)
2783
- )
2784
-
2785
- def root_proposals(
2786
- self,
2787
- network: Optional[list[str]] = Options.network,
2788
- quiet: bool = Options.quiet,
2789
- verbose: bool = Options.verbose,
2790
- ):
2791
- """
2792
- View active proposals for the senate in the Bittensor's governance protocol.
2793
-
2794
- This command displays the details of ongoing proposals, including proposal hashes, votes, thresholds, and proposal data.
2795
-
2796
- EXAMPLE
2797
-
2798
- [green]$[/green] btcli root proposals
2799
- """
2800
- self.verbosity_handler(quiet, verbose)
2801
- return self._run_command(root.proposals(self.initialize_chain(network)))
2802
-
2803
- def root_set_take(
2804
- self,
2805
- network: Optional[list[str]] = Options.network,
2806
- wallet_name: Optional[str] = Options.wallet_name,
2807
- wallet_path: Optional[str] = Options.wallet_path,
2808
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2809
- take: float = typer.Option(None, help="The new take value."),
2810
- quiet: bool = Options.quiet,
2811
- verbose: bool = Options.verbose,
2812
- ):
2813
- """
2814
- Allows users to change their delegate take percentage.
2815
-
2816
- This command can be used to update the delegate takes individually for every subnet. To run the command, the user must have a configured wallet with both hotkey and coldkey. The command performs the below checks:
2817
-
2818
- 1. The provided hotkey is already a delegate.
2819
- 2. The new take value is within 0-18% range.
2820
-
2821
- EXAMPLE
2822
-
2823
- [green]$[/green] btcli root set_take --wallet-name my_wallet --wallet-hotkey my_hotkey
2824
- """
2825
- max_value = 0.18
2826
- min_value = 0.00
2827
- self.verbosity_handler(quiet, verbose)
2828
-
2829
- if not take:
2830
- max_value_style = typer.style(f"Max: {max_value}", fg="magenta")
2831
- min_value_style = typer.style(f"Min: {min_value}", fg="magenta")
2832
- prompt_text = typer.style(
2833
- "Enter take value (0.18 for 18%)", fg="bright_cyan", bold=True
2834
- )
2835
- take = FloatPrompt.ask(f"{prompt_text} {min_value_style} {max_value_style}")
2836
-
2837
- if not (min_value <= take <= max_value):
2838
- print_error(
2839
- f"Take value must be between {min_value} and {max_value}. Provided value: {take}"
2840
- )
2841
- raise typer.Exit()
2842
-
2843
- wallet = self.wallet_ask(
2844
- wallet_name,
2845
- wallet_path,
2846
- wallet_hotkey,
2847
- ask_for=[WO.NAME, WO.HOTKEY],
2848
- validate=WV.WALLET_AND_HOTKEY,
2849
- )
2850
-
2851
- return self._run_command(
2852
- root.set_take(wallet, self.initialize_chain(network), take)
2853
- )
2854
-
2855
- def root_delegate_stake(
2856
- self,
2857
- delegate_ss58key: str = typer.Option(
2858
- None,
2859
- help="The ss58 address of the delegate hotkey to stake TAO to.",
2860
- prompt="Enter the hotkey ss58 address you want to delegate TAO to.",
2861
- ),
2862
- amount: Optional[float] = typer.Option(
2863
- None, help="The amount of TAO to stake. Do no specify if using `--all`"
2864
- ),
2865
- stake_all: Optional[bool] = typer.Option(
2866
- False,
2867
- "--all",
2868
- "-a",
2869
- help="If specified, the command stakes all available TAO. Do not specify if using"
2870
- " `--amount`",
2871
- ),
2872
- wallet_name: Optional[str] = Options.wallet_name,
2873
- wallet_path: Optional[str] = Options.wallet_path,
2874
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2875
- network: Optional[list[str]] = Options.network,
2876
- prompt: bool = Options.prompt,
2877
- quiet: bool = Options.quiet,
2878
- verbose: bool = Options.verbose,
2879
- ):
2880
- """
2881
- Stakes TAO to a specified delegate hotkey.
2882
-
2883
- This command allocates the user's TAO to the specified hotkey of a delegate, potentially earning staking rewards in return. If the
2884
- `--all` flag is used, it delegates the entire TAO balance available in the user's wallet.
2885
-
2886
- This command should be run by a TAO holder. Compare this command with "btcli stake add" that is typically run by a subnet validator to add stake to their own delegate hotkey.
2887
-
2888
- EXAMPLE
2889
-
2890
- [green]$[/green] btcli root delegate-stake --delegate_ss58key <SS58_ADDRESS> --amount <AMOUNT>
2891
-
2892
- [green]$[/green] btcli root delegate-stake --delegate_ss58key <SS58_ADDRESS> --all
2893
-
2894
- [blue bold]Note[/blue bold]: This command modifies the blockchain state and may incur transaction fees. The user should ensure the delegate's address and the amount to be staked are correct before executing the command.
2895
- """
2896
- self.verbosity_handler(quiet, verbose)
2897
- if amount and stake_all:
2898
- err_console.print(
2899
- "Both `--amount` and `--all` are specified. Choose one or the other."
2900
- )
2901
- if not stake_all and not amount:
2902
- while True:
2903
- amount = FloatPrompt.ask(
2904
- "Amount to [blue]stake (TAO τ)[/blue]", console=console
2905
- )
2906
- confirmation = FloatPrompt.ask(
2907
- "Confirm the amount to stake [blue](TAO τ)[/blue]",
2908
- console=console,
2909
- )
2910
- if amount == confirmation:
2911
- break
2912
- else:
2913
- err_console.print(
2914
- "[red]The amounts do not match. Please try again.[/red]"
2915
- )
2916
-
2917
- wallet = self.wallet_ask(
2918
- wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
2919
- )
2920
- return self._run_command(
2921
- root.delegate_stake(
2922
- wallet,
2923
- self.initialize_chain(network),
2924
- amount,
2925
- delegate_ss58key,
2926
- prompt,
2927
- )
2928
- )
2929
-
2930
- def root_undelegate_stake(
2931
- self,
2932
- delegate_ss58key: str = typer.Option(
2933
- None,
2934
- help="The ss58 address of the delegate to undelegate from.",
2935
- prompt="Enter the hotkey ss58 address you want to undelegate from",
2936
- ),
2937
- amount: Optional[float] = typer.Option(
2938
- None, help="The amount of TAO to unstake. Do no specify if using `--all`"
2939
- ),
2940
- unstake_all: Optional[bool] = typer.Option(
2941
- False,
2942
- "--all",
2943
- "-a",
2944
- help="If specified, the command undelegates all staked TAO from the delegate. Do not specify if using"
2945
- " `--amount`",
2946
- ),
2947
- wallet_name: Optional[str] = Options.wallet_name,
2948
- wallet_path: Optional[str] = Options.wallet_path,
2949
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2950
- network: Optional[list[str]] = Options.network,
2951
- prompt: bool = Options.prompt,
2952
- quiet: bool = Options.quiet,
2953
- verbose: bool = Options.verbose,
2954
- ):
2955
- """
2956
- Allows users to withdraw their staked TAO from a delegate.
2957
-
2958
- The user must provide the delegate hotkey's ss58 address and the amount of TAO to undelegate. The function will then send a transaction to the blockchain to process the undelegation. This command can result in a change to the blockchain state and may incur transaction fees.
2959
-
2960
- EXAMPLE
2961
-
2962
- [green]$[/green] btcli undelegate --delegate_ss58key <SS58_ADDRESS> --amount <AMOUNT>
2963
-
2964
- [green]$[/green] btcli undelegate --delegate_ss58key <SS58_ADDRESS> --all
2965
- """
2966
- self.verbosity_handler(quiet, verbose)
2967
- if amount and unstake_all:
2968
- err_console.print(
2969
- "Both `--amount` and `--all` are specified. Choose one or the other."
2970
- )
2971
- if not unstake_all and not amount:
2972
- while True:
2973
- amount = FloatPrompt.ask(
2974
- "Amount to [blue]unstake (TAO τ)[/blue]", console=console
2975
- )
2976
- confirmation = FloatPrompt.ask(
2977
- "Confirm the amount to unstake [blue](TAO τ)[/blue]",
2978
- console=console,
2979
- )
2980
- if amount == confirmation:
2981
- break
2982
- else:
2983
- err_console.print(
2984
- "[red]The amounts do not match. Please try again.[/red]"
2985
- )
2986
-
2987
- wallet = self.wallet_ask(
2988
- wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
2989
- )
2990
- self._run_command(
2991
- root.delegate_unstake(
2992
- wallet,
2993
- self.initialize_chain(network),
2994
- amount,
2995
- delegate_ss58key,
2996
- prompt,
2997
- )
2998
- )
2999
-
3000
- def root_my_delegates(
3001
- self,
3002
- network: Optional[list[str]] = Options.network,
3003
- wallet_name: Optional[str] = Options.wallet_name,
3004
- wallet_path: Optional[str] = Options.wallet_path,
3005
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3006
- all_wallets: bool = typer.Option(
3007
- False,
3008
- "--all-wallets",
3009
- "--all",
3010
- "-a",
3011
- help="If specified, the command aggregates information across all the wallets.",
3012
- ),
3013
- quiet: bool = Options.quiet,
3014
- verbose: bool = Options.verbose,
3015
- ):
3016
- """
3017
- Shows a table with the details on the user's delegates.
3018
-
3019
- The table output includes the following columns:
3020
-
3021
- - Wallet: The name of the user's wallet (coldkey).
3022
-
3023
- - OWNER: The name of the delegate who owns the hotkey.
3024
-
3025
- - SS58: The truncated SS58 address of the delegate's hotkey.
3026
-
3027
- - Delegation: The amount of TAO staked by the user to the delegate.
3028
-
3029
- - τ/24h: The earnings from the delegate to the user over the past 24 hours.
3030
-
3031
- - NOMS: The number of nominators for the delegate.
3032
-
3033
- - OWNER STAKE(τ): The stake amount owned by the delegate.
3034
-
3035
- - TOTAL STAKE(τ): The total stake amount held by the delegate.
3036
-
3037
- - SUBNETS: The list of subnets the delegate is a part of.
3038
-
3039
- - VPERMIT: Validator permits held by the delegate for various subnets.
3040
-
3041
- - 24h/kτ: Earnings per 1000 TAO staked over the last 24 hours.
3042
-
3043
- - Desc: A description of the delegate.
3044
-
3045
- The command also sums and prints the total amount of TAO delegated across all wallets.
3046
-
3047
- EXAMPLE
3048
-
3049
- [green]$[/green] btcli root my-delegates
3050
- [green]$[/green] btcli root my-delegates --all
3051
- [green]$[/green] btcli root my-delegates --wallet-name my_wallet
3052
-
3053
- [blue bold]Note[/blue bold]: This command is not intended to be used directly in user code.
3054
- """
3055
- self.verbosity_handler(quiet, verbose)
3056
- wallet = self.wallet_ask(
3057
- wallet_name,
3058
- wallet_path,
3059
- wallet_hotkey,
3060
- ask_for=([WO.NAME] if not all_wallets else []),
3061
- validate=WV.WALLET if not all_wallets else WV.NONE,
3062
- )
3063
- self._run_command(
3064
- root.my_delegates(wallet, self.initialize_chain(network), all_wallets)
3065
- )
3066
-
3067
- def root_list_delegates(
3068
- self,
3069
- network: Optional[list[str]] = Options.network,
3070
- quiet: bool = Options.quiet,
3071
- verbose: bool = Options.verbose,
3072
- ):
3073
- """
3074
- Displays a table of Bittensor network-wide delegates, providing a comprehensive overview of delegate statistics and information.
3075
-
3076
- This table helps users make informed decisions on which delegates to allocate their TAO stake.
3077
-
3078
- The table columns include:
3079
-
3080
- - INDEX: The delegate's index in the sorted list.
3081
-
3082
- - DELEGATE: The name of the delegate.
3083
-
3084
- - SS58: The delegate's unique ss58 address (truncated for display).
3085
-
3086
- - NOMINATORS: The count of nominators backing the delegate.
3087
-
3088
- - OWN STAKE(τ): The amount of delegate's own stake (not the TAO delegated from any nominators).
3089
-
3090
- - TOTAL STAKE(τ): The delegate's total stake, i.e., the sum of delegate's own stake and nominators' stakes.
3091
-
3092
- - CHANGE/(4h): The percentage change in the delegate's stake over the last four hours.
3093
-
3094
- - SUBNETS: The subnets in which the delegate is registered.
3095
-
3096
- - VPERMIT: Indicates the subnets in which the delegate has validator permits.
3097
-
3098
- - NOMINATOR/(24h)/kτ: The earnings per 1000 τ staked by nominators in the last 24 hours.
3099
-
3100
- - DELEGATE/(24h): The total earnings of the delegate in the last 24 hours.
3101
-
3102
- - DESCRIPTION: A brief description of the delegate's purpose and operations.
3103
-
3104
- [blue bold]NOTES:[/blue bold]
3105
-
3106
- - Sorting is done based on the `TOTAL STAKE` column in descending order.
3107
- - Changes in stake are shown as: increases in green and decreases in red.
3108
- - Entries with no previous data are marked with `NA`.
3109
- - Each delegate's name is a hyperlink to more information, if available.
3110
-
3111
- EXAMPLE
3112
-
3113
- [green]$[/green] btcli root list_delegates
3114
-
3115
- [green]$[/green] btcli root list_delegates --subtensor.network finney # can also be `test` or `local`
3116
-
3117
- [blue bold]NOTE[/blue bold]: This command is intended for use within a
3118
- console application. It prints directly to the console and does not return any value.
3119
- """
3120
- self.verbosity_handler(quiet, verbose)
3121
-
3122
- if network:
3123
- if "finney" in network:
3124
- network = ["wss://archive.chain.opentensor.ai:443"]
3125
- elif (conf_net := self.config.get("network")) == "finney":
3126
- network = ["wss://archive.chain.opentensor.ai:443"]
3127
- elif conf_net:
3128
- network = [conf_net]
3129
- else:
3130
- network = ["wss://archive.chain.opentensor.ai:443"]
3131
-
3132
- sub = self.initialize_chain(network)
3133
- return self._run_command(root.list_delegates(sub))
3134
-
3135
- # TODO: Confirm if we need a command for this - currently registering to root auto makes u delegate
3136
- def root_nominate(
3137
- self,
3138
- wallet_name: Optional[str] = Options.wallet_name,
3139
- wallet_path: Optional[str] = Options.wallet_path,
3140
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3141
- network: Optional[list[str]] = Options.network,
3142
- prompt: bool = Options.prompt,
3143
- quiet: bool = Options.quiet,
3144
- verbose: bool = Options.verbose,
3145
- ):
3146
- """
3147
- Enables a wallet's hotkey to become a delegate.
3148
-
3149
- This command handles the nomination process, including wallet unlocking and verification of the hotkey's current delegate status.
3150
-
3151
- The command performs several checks:
3152
-
3153
- - Verifies that the hotkey is not already a delegate to prevent redundant nominations.
3154
-
3155
- - Tries to nominate the wallet and reports success or failure.
3156
-
3157
- Upon success, the wallet's hotkey is registered as a delegate on the network.
3158
-
3159
- To run the command, the user must have a configured wallet with both hotkey and coldkey. If the wallet is not already nominated, this command will initiate the process.
3160
-
3161
- EXAMPLE
3162
-
3163
- [green]$[/green] btcli root nominate
3164
-
3165
- [green]$[/green] btcli root nominate --wallet-name my_wallet --wallet-hotkey my_hotkey
3166
-
3167
- [blue bold]Note[/blue bold]: This command prints the output directly to the console. It should not be called programmatically in user code due to its interactive nature and side effects on the network state.
3168
- """
3169
- self.verbosity_handler(quiet, verbose)
3170
- wallet = self.wallet_ask(
3171
- wallet_name,
3172
- wallet_path,
3173
- wallet_hotkey,
3174
- ask_for=[WO.NAME, WO.HOTKEY],
3175
- validate=WV.WALLET_AND_HOTKEY,
3176
- )
3177
- return self._run_command(
3178
- root.nominate(wallet, self.initialize_chain(network), prompt)
3179
- )
3180
-
3181
- def stake_show(
3182
- self,
3183
- all_wallets: bool = typer.Option(
3184
- False,
3185
- "--all",
3186
- "--all-wallets",
3187
- "-a",
3188
- help="When set, the command checks all the coldkey wallets of the user instead of just the specified wallet.",
3189
- ),
3190
- network: Optional[list[str]] = Options.network,
3191
- wallet_name: Optional[str] = Options.wallet_name,
3192
- wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3193
- wallet_path: Optional[str] = Options.wallet_path,
3194
- reuse_last: bool = Options.reuse_last,
3195
- html_output: bool = Options.html_output,
3196
- quiet: bool = Options.quiet,
3197
- verbose: bool = Options.verbose,
3198
- ):
3199
- """
3200
- Lists all the stake accounts associated with a user's wallet.
3201
-
3202
- This command provides a comprehensive view of the stakes associated with the user's coldkeys. It shows both the user's own hotkeys and also the hotkeys of the delegates to which this user has staked.
3203
-
3204
- The command lists all the stake accounts for a specified wallet or all wallets in the user's configuration directory. It displays the coldkey, balance, hotkey details (own hotkey and delegate hotkey), stake amount, and the rate of return.
3205
-
3206
- The command shows a table with the below columns:
3207
-
3208
- - Coldkey: The coldkey associated with the wallet.
3209
-
3210
- - Balance: The balance of the coldkey.
3211
-
3212
- - Hotkey: The names of the coldkey's own hotkeys and the delegate hotkeys to which this coldkey has staked.
3213
-
3214
- - Stake: The amount of TAO staked to all the hotkeys.
3215
-
3216
- - Rate: The rate of return on the stake, shown in TAO per day.
3217
-
3218
- EXAMPLE
3219
-
3220
- [green]$[/green] btcli stake show --all
3221
- """
3222
- self.verbosity_handler(quiet, verbose)
3223
- if (reuse_last or html_output) and self.config.get("use_cache") is False:
3224
- err_console.print(
3225
- "Unable to use `--reuse-last` or `--html` when config 'no-cache' is set to 'True'. "
3226
- "Please change the config to 'False' using `btcli config set`"
3227
- )
3228
- raise typer.Exit()
3229
- if not reuse_last:
3230
- subtensor = self.initialize_chain(network)
3231
- else:
3232
- subtensor = None
3233
-
3234
- if all_wallets:
3235
- wallet = self.wallet_ask(
3236
- wallet_name,
3237
- wallet_path,
3238
- wallet_hotkey,
3239
- ask_for=[],
3240
- validate=WV.NONE,
3241
- )
3242
- else:
3243
- wallet = self.wallet_ask(
3244
- wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
3245
- )
3246
-
3247
- return self._run_command(
3248
- stake.show(
3249
- wallet,
3250
- subtensor,
3251
- all_wallets,
3252
- reuse_last,
3253
- html_output,
3254
- not self.config.get("use_cache", True),
3255
- )
3256
- )
3257
-
3258
- def stake_add(
3259
- self,
3260
- stake_all: bool = typer.Option(
3261
- False,
3262
- "--all-tokens",
3263
- "--all",
3264
- "-a",
3265
- help="When set, the command stakes all the available TAO from the coldkey.",
3266
- ),
3267
- amount: float = typer.Option(
3268
- 0.0, "--amount", help="The amount of TAO to stake"
3269
- ),
3270
- max_stake: float = typer.Option(
3271
- 0.0,
3272
- "--max-stake",
3273
- "-m",
3274
- help="Stake is sent to a hotkey only until the hotkey's total stake is less than or equal to this maximum staked TAO. If a hotkey already has stake greater than this amount, then stake is not added to this hotkey.",
3275
- ),
3276
- hotkey_ss58_address: str = typer.Option(
3277
- "",
3278
- help="The ss58 address of the hotkey to stake to.",
3279
- ),
3280
- include_hotkeys: str = typer.Option(
3281
- "",
3282
- "--include-hotkeys",
3283
- "-in",
3284
- help="Specifies hotkeys by name or ss58 address to stake to. For example, `-in hk1,hk2`",
3285
- ),
3286
- exclude_hotkeys: str = typer.Option(
3287
- "",
3288
- "--exclude-hotkeys",
3289
- "-ex",
3290
- help="Specifies hotkeys by name or ss58 address to not to stake to (use this option only with `--all-hotkeys`)"
3291
- " i.e. `--all-hotkeys -ex hk3,hk4`",
3292
- ),
3293
- all_hotkeys: bool = typer.Option(
3294
- False,
3295
- help="When set, this command stakes to all hotkeys associated with the wallet. Do not use if specifying "
3296
- "hotkeys in `--include-hotkeys`.",
3297
- ),
2601
+ stake_all: bool = typer.Option(
2602
+ False,
2603
+ "--all-tokens",
2604
+ "--all",
2605
+ "-a",
2606
+ help="When set, the command stakes all the available TAO from the coldkey.",
2607
+ ),
2608
+ amount: float = typer.Option(
2609
+ 0.0, "--amount", help="The amount of TAO to stake"
2610
+ ),
2611
+ max_stake: float = typer.Option(
2612
+ 0.0,
2613
+ "--max-stake",
2614
+ "-m",
2615
+ help="Stake is sent to a hotkey only until the hotkey's total stake is less than or equal to this maximum staked TAO. If a hotkey already has stake greater than this amount, then stake is not added to this hotkey.",
2616
+ ),
2617
+ include_hotkeys: str = typer.Option(
2618
+ "",
2619
+ "--include-hotkeys",
2620
+ "-in",
2621
+ "--hotkey-ss58-address",
2622
+ help="Specifies hotkeys by name or ss58 address to stake to. For example, `-in hk1,hk2`",
2623
+ ),
2624
+ exclude_hotkeys: str = typer.Option(
2625
+ "",
2626
+ "--exclude-hotkeys",
2627
+ "-ex",
2628
+ help="Specifies hotkeys by name or ss58 address to not to stake to (use this option only with `--all-hotkeys`)"
2629
+ " i.e. `--all-hotkeys -ex hk3,hk4`",
2630
+ ),
2631
+ all_hotkeys: bool = typer.Option(
2632
+ False,
2633
+ help="When set, this command stakes to all hotkeys associated with the wallet. Do not use if specifying "
2634
+ "hotkeys in `--include-hotkeys`.",
2635
+ ),
2636
+ netuid: Optional[int] = Options.netuid_not_req,
2637
+ all_netuids: bool = Options.all_netuids,
3298
2638
  wallet_name: str = Options.wallet_name,
3299
2639
  wallet_path: str = Options.wallet_path,
3300
2640
  wallet_hotkey: str = Options.wallet_hotkey,
@@ -3315,16 +2655,14 @@ class CLIManager:
3315
2655
  [green]$[/green] btcli stake add --amount 100 --wallet-name <my_wallet> --wallet-hotkey <my_hotkey>
3316
2656
  """
3317
2657
  self.verbosity_handler(quiet, verbose)
2658
+ netuid = get_optional_netuid(netuid, all_netuids)
3318
2659
 
3319
2660
  if stake_all and amount:
3320
- print_error(
2661
+ err_console.print(
3321
2662
  "Cannot specify an amount and 'stake-all'. Choose one or the other."
3322
2663
  )
3323
2664
  raise typer.Exit()
3324
2665
 
3325
- if not stake_all and not amount and not max_stake:
3326
- amount = FloatPrompt.ask("Amount to [blue]stake (TAO τ)[/blue]")
3327
-
3328
2666
  if stake_all and not amount:
3329
2667
  if not Confirm.ask("Stake all the available TAO tokens?", default=False):
3330
2668
  raise typer.Exit()
@@ -3342,40 +2680,66 @@ class CLIManager:
3342
2680
  )
3343
2681
  raise typer.Exit()
3344
2682
 
3345
- if (
3346
- not wallet_hotkey
3347
- and not all_hotkeys
3348
- and not include_hotkeys
3349
- and not hotkey_ss58_address
3350
- ):
3351
- hotkey_or_ss58 = Prompt.ask(
3352
- "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to stake to",
3353
- )
3354
- if is_valid_ss58_address(hotkey_or_ss58):
3355
- hotkey_ss58_address = hotkey_or_ss58
2683
+ if not wallet_hotkey and not all_hotkeys and not include_hotkeys:
2684
+ if not wallet_name:
2685
+ wallet_name = Prompt.ask(
2686
+ "Enter the [blue]wallet name[/blue]",
2687
+ default=self.config.get("wallet_name") or defaults.wallet.name,
2688
+ )
2689
+ if netuid is not None:
2690
+ hotkey_or_ss58 = Prompt.ask(
2691
+ "Enter the [blue]wallet hotkey[/blue] name or [blue]ss58 address[/blue] to stake to [dim](or Press Enter to view delegates)[/dim]",
2692
+ )
2693
+ else:
2694
+ hotkey_or_ss58 = Prompt.ask(
2695
+ "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to stake to",
2696
+ default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
2697
+ )
2698
+
2699
+ if hotkey_or_ss58 == "":
3356
2700
  wallet = self.wallet_ask(
3357
- wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
2701
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
2702
+ )
2703
+ selected_hotkey = self._run_command(
2704
+ subnets.show(
2705
+ subtensor=self.initialize_chain(network),
2706
+ netuid=netuid,
2707
+ max_rows=12,
2708
+ prompt=False,
2709
+ delegate_selection=True,
2710
+ ),
2711
+ exit_early=False,
2712
+ )
2713
+ if selected_hotkey is None:
2714
+ print_error("No delegate selected. Exiting.")
2715
+ raise typer.Exit()
2716
+ include_hotkeys = selected_hotkey
2717
+ elif is_valid_ss58_address(hotkey_or_ss58):
2718
+ wallet = self.wallet_ask(
2719
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
3358
2720
  )
2721
+ include_hotkeys = hotkey_or_ss58
3359
2722
  else:
3360
2723
  wallet_hotkey = hotkey_or_ss58
3361
2724
  wallet = self.wallet_ask(
3362
2725
  wallet_name,
3363
2726
  wallet_path,
3364
2727
  wallet_hotkey,
3365
- ask_for=[WO.NAME, WO.HOTKEY],
2728
+ ask_for=[WO.NAME, WO.HOTKEY, WO.PATH],
3366
2729
  validate=WV.WALLET_AND_HOTKEY,
3367
2730
  )
2731
+ include_hotkeys = wallet.hotkey.ss58_address
3368
2732
 
3369
- elif all_hotkeys or include_hotkeys or exclude_hotkeys or hotkey_ss58_address:
2733
+ elif all_hotkeys or include_hotkeys or exclude_hotkeys:
3370
2734
  wallet = self.wallet_ask(
3371
- wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
2735
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
3372
2736
  )
3373
2737
  else:
3374
2738
  wallet = self.wallet_ask(
3375
2739
  wallet_name,
3376
2740
  wallet_path,
3377
2741
  wallet_hotkey,
3378
- ask_for=[WO.NAME, WO.HOTKEY],
2742
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3379
2743
  validate=WV.WALLET_AND_HOTKEY,
3380
2744
  )
3381
2745
 
@@ -3383,8 +2747,8 @@ class CLIManager:
3383
2747
  included_hotkeys = parse_to_list(
3384
2748
  include_hotkeys,
3385
2749
  str,
3386
- "Hotkeys must be a comma-separated list of ss58s or hotkey names, e.g., "
3387
- "`--include-hotkeys 5Grw....,5Grw....`.",
2750
+ "Hotkeys must be a comma-separated list of ss58s, e.g., `--include-hotkeys 5Grw....,5Grw....`.",
2751
+ is_ss58=True,
3388
2752
  )
3389
2753
  else:
3390
2754
  included_hotkeys = []
@@ -3393,24 +2757,54 @@ class CLIManager:
3393
2757
  excluded_hotkeys = parse_to_list(
3394
2758
  exclude_hotkeys,
3395
2759
  str,
3396
- "Hotkeys must be a comma-separated list of ss58s or hotkey names, e.g., "
3397
- "`--exclude-hotkeys 5Grw....,5Grw....`.",
2760
+ "Hotkeys must be a comma-separated list of ss58s, e.g., `--exclude-hotkeys 5Grw....,5Grw....`.",
2761
+ is_ss58=True,
3398
2762
  )
3399
2763
  else:
3400
2764
  excluded_hotkeys = []
3401
2765
 
2766
+ # TODO: Ask amount for each subnet explicitly if more than one
2767
+ if not stake_all and not amount and not max_stake:
2768
+ free_balance, staked_balance = self._run_command(
2769
+ wallets.wallet_balance(
2770
+ wallet, self.initialize_chain(network), False, None
2771
+ ),
2772
+ exit_early=False,
2773
+ )
2774
+ if free_balance == Balance.from_tao(0):
2775
+ print_error("You dont have any balance to stake.")
2776
+ raise typer.Exit()
2777
+ if netuid is not None:
2778
+ amount = FloatPrompt.ask(
2779
+ f"Amount to [{COLOR_PALETTE['GENERAL']['SUBHEADING_MAIN']}]stake (TAO τ)"
2780
+ )
2781
+ else:
2782
+ amount = FloatPrompt.ask(
2783
+ f"Amount to [{COLOR_PALETTE['GENERAL']['SUBHEADING_MAIN']}]stake to each netuid (TAO τ)"
2784
+ )
2785
+
2786
+ if amount <= 0:
2787
+ print_error(f"You entered an incorrect stake amount: {amount}")
2788
+ raise typer.Exit()
2789
+ if Balance.from_tao(amount) > free_balance:
2790
+ print_error(
2791
+ f"You dont have enough balance to stake. Current free Balance: {free_balance}."
2792
+ )
2793
+ raise typer.Exit()
2794
+
3402
2795
  return self._run_command(
3403
2796
  stake.stake_add(
3404
2797
  wallet,
3405
2798
  self.initialize_chain(network),
3406
- amount,
2799
+ netuid,
3407
2800
  stake_all,
2801
+ amount,
2802
+ False,
2803
+ prompt,
3408
2804
  max_stake,
2805
+ all_hotkeys,
3409
2806
  included_hotkeys,
3410
2807
  excluded_hotkeys,
3411
- all_hotkeys,
3412
- prompt,
3413
- hotkey_ss58_address,
3414
2808
  )
3415
2809
  )
3416
2810
 
@@ -3420,11 +2814,21 @@ class CLIManager:
3420
2814
  wallet_name: str = Options.wallet_name,
3421
2815
  wallet_path: str = Options.wallet_path,
3422
2816
  wallet_hotkey: str = Options.wallet_hotkey,
2817
+ netuid: Optional[int] = Options.netuid_not_req,
2818
+ all_netuids: bool = Options.all_netuids,
3423
2819
  unstake_all: bool = typer.Option(
3424
2820
  False,
3425
2821
  "--unstake-all",
3426
2822
  "--all",
3427
- help="When set, this commmand unstakes all staked TAO from the specified hotkeys.",
2823
+ hidden=True,
2824
+ help="When set, this command unstakes all staked TAO + Alpha from the all hotkeys.",
2825
+ ),
2826
+ unstake_all_alpha: bool = typer.Option(
2827
+ False,
2828
+ "--unstake-all-alpha",
2829
+ "--all-alpha",
2830
+ hidden=True,
2831
+ help="When set, this command unstakes all staked Alpha from the all hotkeys.",
3428
2832
  ),
3429
2833
  amount: float = typer.Option(
3430
2834
  0.0, "--amount", "-a", help="The amount of TAO to unstake."
@@ -3458,6 +2862,12 @@ class CLIManager:
3458
2862
  "hotkeys in `--include-hotkeys`.",
3459
2863
  ),
3460
2864
  prompt: bool = Options.prompt,
2865
+ interactive: bool = typer.Option(
2866
+ False,
2867
+ "--interactive",
2868
+ "-i",
2869
+ help="Enter interactive mode for unstaking.",
2870
+ ),
3461
2871
  quiet: bool = Options.quiet,
3462
2872
  verbose: bool = Options.verbose,
3463
2873
  ):
@@ -3473,31 +2883,45 @@ class CLIManager:
3473
2883
  [blue bold]Note[/blue bold]: This command is for users who wish to reallocate their stake or withdraw them from the network. It allows for flexible management of TAO stake across different neurons (hotkeys) on the network.
3474
2884
  """
3475
2885
  self.verbosity_handler(quiet, verbose)
2886
+ # TODO: Coldkey related unstakes need to be updated. Patching for now.
2887
+ unstake_all_alpha = False
2888
+ unstake_all = False
3476
2889
 
3477
- if all_hotkeys and include_hotkeys:
2890
+ if interactive and any(
2891
+ [hotkey_ss58_address, include_hotkeys, exclude_hotkeys, all_hotkeys]
2892
+ ):
3478
2893
  err_console.print(
3479
- "You have specified hotkeys to include and also the `--all-hotkeys` flag. The flag"
3480
- "should only be used standalone (to use all hotkeys) or with `--exclude-hotkeys`."
2894
+ "Interactive mode cannot be used with hotkey selection options like --include-hotkeys, --exclude-hotkeys, --all-hotkeys, or --hotkey."
3481
2895
  )
3482
2896
  raise typer.Exit()
3483
2897
 
3484
- if include_hotkeys and exclude_hotkeys:
3485
- err_console.print(
3486
- "You have specified both including and excluding hotkeys options. Select one or the other."
3487
- )
2898
+ if unstake_all and unstake_all_alpha:
2899
+ err_console.print("Cannot specify both unstake-all and unstake-all-alpha.")
3488
2900
  raise typer.Exit()
3489
2901
 
3490
- if unstake_all and amount:
3491
- err_console.print(
3492
- "Cannot specify both a specific amount and 'unstake-all'. Choose one or the other."
3493
- )
3494
- raise typer.Exit()
2902
+ if not interactive and not unstake_all and not unstake_all_alpha:
2903
+ netuid = get_optional_netuid(netuid, all_netuids)
2904
+ if all_hotkeys and include_hotkeys:
2905
+ err_console.print(
2906
+ "You have specified hotkeys to include and also the `--all-hotkeys` flag. The flag"
2907
+ " should only be used standalone (to use all hotkeys) or with `--exclude-hotkeys`."
2908
+ )
2909
+ raise typer.Exit()
2910
+
2911
+ if include_hotkeys and exclude_hotkeys:
2912
+ err_console.print(
2913
+ "You have specified both including and excluding hotkeys options. Select one or the other."
2914
+ )
2915
+ raise typer.Exit()
3495
2916
 
3496
- if not unstake_all and not amount and not keep_stake:
3497
- amount = FloatPrompt.ask("Amount to [blue]unstake (TAO τ)[/blue]")
2917
+ if unstake_all and amount:
2918
+ err_console.print(
2919
+ "Cannot specify both a specific amount and 'unstake-all'. Choose one or the other."
2920
+ )
2921
+ raise typer.Exit()
3498
2922
 
3499
- if unstake_all and not amount and prompt:
3500
- if not Confirm.ask("Unstake all staked TAO tokens?", default=False):
2923
+ if amount and amount <= 0:
2924
+ print_error(f"You entered an incorrect unstake amount: {amount}")
3501
2925
  raise typer.Exit()
3502
2926
 
3503
2927
  if (
@@ -3505,14 +2929,27 @@ class CLIManager:
3505
2929
  and not hotkey_ss58_address
3506
2930
  and not all_hotkeys
3507
2931
  and not include_hotkeys
2932
+ and not interactive
2933
+ and not unstake_all
2934
+ and not unstake_all_alpha
3508
2935
  ):
2936
+ if not wallet_name:
2937
+ wallet_name = Prompt.ask(
2938
+ "Enter the [blue]wallet name[/blue]",
2939
+ default=self.config.get("wallet_name") or defaults.wallet.name,
2940
+ )
3509
2941
  hotkey_or_ss58 = Prompt.ask(
3510
- "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake from"
2942
+ "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake from [dim](or Press Enter to view existing staked hotkeys)[/dim]",
3511
2943
  )
3512
- if is_valid_ss58_address(hotkey_or_ss58):
2944
+ if hotkey_or_ss58 == "":
2945
+ wallet = self.wallet_ask(
2946
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
2947
+ )
2948
+ interactive = True
2949
+ elif is_valid_ss58_address(hotkey_or_ss58):
3513
2950
  hotkey_ss58_address = hotkey_or_ss58
3514
2951
  wallet = self.wallet_ask(
3515
- wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
2952
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
3516
2953
  )
3517
2954
  else:
3518
2955
  wallet_hotkey = hotkey_or_ss58
@@ -3520,56 +2957,436 @@ class CLIManager:
3520
2957
  wallet_name,
3521
2958
  wallet_path,
3522
2959
  wallet_hotkey,
3523
- ask_for=[WO.NAME, WO.HOTKEY],
2960
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3524
2961
  validate=WV.WALLET_AND_HOTKEY,
3525
2962
  )
3526
2963
 
3527
- elif all_hotkeys or include_hotkeys or exclude_hotkeys or hotkey_ss58_address:
2964
+ elif (
2965
+ all_hotkeys
2966
+ or include_hotkeys
2967
+ or exclude_hotkeys
2968
+ or hotkey_ss58_address
2969
+ or interactive
2970
+ or unstake_all
2971
+ or unstake_all_alpha
2972
+ ):
2973
+ wallet = self.wallet_ask(
2974
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
2975
+ )
2976
+ else:
3528
2977
  wallet = self.wallet_ask(
3529
- wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
2978
+ wallet_name,
2979
+ wallet_path,
2980
+ wallet_hotkey,
2981
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
2982
+ validate=WV.WALLET_AND_HOTKEY,
2983
+ )
2984
+
2985
+ if include_hotkeys:
2986
+ included_hotkeys = parse_to_list(
2987
+ include_hotkeys,
2988
+ str,
2989
+ "Hotkeys must be a comma-separated list of ss58s or names, e.g., `--include-hotkeys hk1,hk2`.",
2990
+ is_ss58=False,
2991
+ )
2992
+ else:
2993
+ included_hotkeys = []
2994
+
2995
+ if exclude_hotkeys:
2996
+ excluded_hotkeys = parse_to_list(
2997
+ exclude_hotkeys,
2998
+ str,
2999
+ "Hotkeys must be a comma-separated list of ss58s or names, e.g., `--exclude-hotkeys hk3,hk4`.",
3000
+ is_ss58=False,
3001
+ )
3002
+ else:
3003
+ excluded_hotkeys = []
3004
+
3005
+ return self._run_command(
3006
+ stake.unstake(
3007
+ wallet,
3008
+ self.initialize_chain(network),
3009
+ hotkey_ss58_address,
3010
+ all_hotkeys,
3011
+ included_hotkeys,
3012
+ excluded_hotkeys,
3013
+ amount,
3014
+ keep_stake,
3015
+ unstake_all,
3016
+ prompt,
3017
+ interactive,
3018
+ netuid=netuid,
3019
+ unstake_all_alpha=unstake_all_alpha,
3020
+ )
3021
+ )
3022
+
3023
+ def stake_move(
3024
+ self,
3025
+ network: Optional[list[str]] = Options.network,
3026
+ wallet_name=Options.wallet_name,
3027
+ wallet_path=Options.wallet_path,
3028
+ wallet_hotkey=Options.wallet_hotkey,
3029
+ origin_netuid: Optional[int] = typer.Option(
3030
+ None, "--origin-netuid", help="Origin netuid"
3031
+ ),
3032
+ destination_netuid: Optional[int] = typer.Option(
3033
+ None, "--dest-netuid", help="Destination netuid"
3034
+ ),
3035
+ destination_hotkey: Optional[str] = typer.Option(
3036
+ None, "--dest-ss58", "--dest", help="Destination hotkey", prompt=False
3037
+ ),
3038
+ amount: float = typer.Option(
3039
+ None,
3040
+ "--amount",
3041
+ help="The amount of TAO to stake",
3042
+ prompt=False,
3043
+ ),
3044
+ stake_all: bool = typer.Option(
3045
+ False, "--stake-all", "--all", help="Stake all", prompt=False
3046
+ ),
3047
+ prompt: bool = Options.prompt,
3048
+ ):
3049
+ """
3050
+ Move staked TAO between hotkeys while keeping the same coldkey ownership.
3051
+
3052
+ This command allows you to:
3053
+ - Move stake from one hotkey to another hotkey
3054
+ - Move stake between different subnets
3055
+ - Keep the same coldkey ownership
3056
+
3057
+ You can specify:
3058
+ - The origin subnet (--origin-netuid)
3059
+ - The destination subnet (--dest-netuid)
3060
+ - The destination hotkey (--dest-hotkey)
3061
+ - The amount to move (--amount)
3062
+
3063
+ If no arguments are provided, an interactive selection menu will be shown.
3064
+
3065
+ EXAMPLE
3066
+
3067
+ [green]$[/green] btcli stake move
3068
+ """
3069
+ console.print(
3070
+ "[dim]This command moves stake from one hotkey to another hotkey while keeping the same coldkey.[/dim]"
3071
+ )
3072
+ if not destination_hotkey:
3073
+ dest_wallet_or_ss58 = Prompt.ask(
3074
+ "Enter the [blue]destination wallet[/blue] where destination hotkey is located or [blue]ss58 address[/blue]"
3075
+ )
3076
+ if is_valid_ss58_address(dest_wallet_or_ss58):
3077
+ destination_hotkey = dest_wallet_or_ss58
3078
+ else:
3079
+ dest_wallet = self.wallet_ask(
3080
+ dest_wallet_or_ss58,
3081
+ wallet_path,
3082
+ None,
3083
+ ask_for=[WO.NAME, WO.PATH],
3084
+ validate=WV.WALLET,
3085
+ )
3086
+ destination_hotkey = Prompt.ask(
3087
+ "Enter the [blue]destination hotkey[/blue] name",
3088
+ default=dest_wallet.hotkey_str,
3089
+ )
3090
+ destination_wallet = self.wallet_ask(
3091
+ dest_wallet_or_ss58,
3092
+ wallet_path,
3093
+ destination_hotkey,
3094
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3095
+ validate=WV.WALLET_AND_HOTKEY,
3096
+ )
3097
+ destination_hotkey = destination_wallet.hotkey.ss58_address
3098
+ else:
3099
+ if is_valid_ss58_address(destination_hotkey):
3100
+ destination_hotkey = destination_hotkey
3101
+ else:
3102
+ print_error(
3103
+ "Invalid destination hotkey ss58 address. Please enter a valid ss58 address or wallet name."
3104
+ )
3105
+ raise typer.Exit()
3106
+
3107
+ if not wallet_name:
3108
+ wallet_name = Prompt.ask(
3109
+ "Enter the [blue]origin wallet name[/blue]",
3110
+ default=self.config.get("wallet_name") or defaults.wallet.name,
3111
+ )
3112
+ wallet = self.wallet_ask(
3113
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
3114
+ )
3115
+
3116
+ interactive_selection = False
3117
+ if not wallet_hotkey:
3118
+ origin_hotkey = Prompt.ask(
3119
+ "Enter the [blue]origin hotkey[/blue] name or "
3120
+ "[blue]ss58 address[/blue] where the stake will be moved from "
3121
+ "[dim](or Press Enter to view existing stakes)[/dim]"
3122
+ )
3123
+ if origin_hotkey == "":
3124
+ interactive_selection = True
3125
+
3126
+ elif is_valid_ss58_address(origin_hotkey):
3127
+ origin_hotkey = origin_hotkey
3128
+ else:
3129
+ wallet = self.wallet_ask(
3130
+ wallet_name,
3131
+ wallet_path,
3132
+ origin_hotkey,
3133
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3134
+ validate=WV.WALLET_AND_HOTKEY,
3135
+ )
3136
+ origin_hotkey = wallet.hotkey.ss58_address
3137
+ else:
3138
+ if is_valid_ss58_address(wallet_hotkey):
3139
+ origin_hotkey = wallet_hotkey
3140
+ else:
3141
+ wallet = self.wallet_ask(
3142
+ wallet_name,
3143
+ wallet_path,
3144
+ wallet_hotkey,
3145
+ ask_for=[],
3146
+ validate=WV.WALLET_AND_HOTKEY,
3147
+ )
3148
+ origin_hotkey = wallet.hotkey.ss58_address
3149
+
3150
+ if not interactive_selection:
3151
+ if origin_netuid is None:
3152
+ origin_netuid = IntPrompt.ask(
3153
+ "Enter the [blue]origin subnet[/blue] (netuid) to move stake from"
3154
+ )
3155
+
3156
+ if destination_netuid is None:
3157
+ destination_netuid = IntPrompt.ask(
3158
+ "Enter the [blue]destination subnet[/blue] (netuid) to move stake to"
3159
+ )
3160
+
3161
+ return self._run_command(
3162
+ move.move_stake(
3163
+ subtensor=self.initialize_chain(network),
3164
+ wallet=wallet,
3165
+ origin_netuid=origin_netuid,
3166
+ origin_hotkey=origin_hotkey,
3167
+ destination_netuid=destination_netuid,
3168
+ destination_hotkey=destination_hotkey,
3169
+ amount=amount,
3170
+ stake_all=stake_all,
3171
+ interactive_selection=interactive_selection,
3172
+ prompt=prompt,
3173
+ )
3174
+ )
3175
+
3176
+ def stake_transfer(
3177
+ self,
3178
+ network: Optional[list[str]] = Options.network,
3179
+ wallet_name: Optional[str] = Options.wallet_name,
3180
+ wallet_path: Optional[str] = Options.wallet_path,
3181
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3182
+ origin_netuid: Optional[int] = typer.Option(
3183
+ None,
3184
+ "--origin-netuid",
3185
+ help="The netuid to transfer stake from",
3186
+ ),
3187
+ dest_netuid: Optional[int] = typer.Option(
3188
+ None,
3189
+ "--dest-netuid",
3190
+ help="The netuid to transfer stake to",
3191
+ ),
3192
+ dest_ss58: Optional[str] = typer.Option(
3193
+ None,
3194
+ "--dest-ss58",
3195
+ "--dest",
3196
+ "--dest-coldkey",
3197
+ help="The destination wallet name or SS58 address to transfer stake to",
3198
+ ),
3199
+ amount: float = typer.Option(
3200
+ None,
3201
+ "--amount",
3202
+ "-a",
3203
+ help="Amount of stake to transfer",
3204
+ ),
3205
+ prompt: bool = Options.prompt,
3206
+ quiet: bool = Options.quiet,
3207
+ verbose: bool = Options.verbose,
3208
+ ):
3209
+ """
3210
+ Transfer stake between coldkeys while keeping the same hotkey ownership.
3211
+
3212
+ This command allows you to:
3213
+ - Transfer stake from one coldkey to another coldkey
3214
+ - Keep the same hotkey ownership
3215
+ - Transfer stake between different subnets
3216
+
3217
+ You can specify:
3218
+ - The origin subnet (--origin-netuid)
3219
+ - The destination subnet (--dest-netuid)
3220
+ - The destination wallet/address (--dest)
3221
+ - The amount to transfer (--amount)
3222
+
3223
+ If no arguments are provided, an interactive selection menu will be shown.
3224
+
3225
+ EXAMPLE
3226
+
3227
+ Transfer 100 TAO from subnet 1 to subnet 2:
3228
+ [green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest wallet2 --amount 100
3229
+
3230
+ Using SS58 address:
3231
+ [green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest 5FrLxJsyJ5x9n2rmxFwosFraxFCKcXZDngEP9H7qjkKgHLcK --amount 100
3232
+ """
3233
+ console.print(
3234
+ "[dim]This command transfers stake from one coldkey to another while keeping the same hotkey.[/dim]"
3235
+ )
3236
+ self.verbosity_handler(quiet, verbose)
3237
+
3238
+ wallet = self.wallet_ask(
3239
+ wallet_name,
3240
+ wallet_path,
3241
+ wallet_hotkey,
3242
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3243
+ validate=WV.WALLET_AND_HOTKEY,
3244
+ )
3245
+
3246
+ if not dest_ss58:
3247
+ dest_ss58 = Prompt.ask(
3248
+ "Enter the [blue]destination wallet name[/blue] or [blue]coldkey SS58 address[/blue]"
3530
3249
  )
3531
3250
 
3251
+ if is_valid_ss58_address(dest_ss58):
3252
+ dest_ss58 = dest_ss58
3532
3253
  else:
3533
- wallet = self.wallet_ask(
3534
- wallet_name,
3254
+ dest_wallet = self.wallet_ask(
3255
+ dest_ss58,
3535
3256
  wallet_path,
3536
- wallet_hotkey,
3537
- ask_for=[WO.NAME, WO.HOTKEY],
3538
- validate=WV.WALLET_AND_HOTKEY,
3257
+ None,
3258
+ ask_for=[WO.NAME, WO.PATH],
3259
+ validate=WV.WALLET,
3539
3260
  )
3261
+ dest_ss58 = dest_wallet.coldkeypub.ss58_address
3540
3262
 
3541
- if include_hotkeys:
3542
- included_hotkeys = parse_to_list(
3543
- include_hotkeys,
3544
- str,
3545
- "Hotkeys must be a comma-separated list of ss58s or hotkey names, e.g., "
3546
- "`--include-hotkeys 5Grw....,5Grw....`.",
3547
- )
3263
+ interactive_selection = False
3264
+ if origin_netuid is None and dest_netuid is None and not amount:
3265
+ interactive_selection = True
3548
3266
  else:
3549
- included_hotkeys = []
3267
+ if origin_netuid is None:
3268
+ origin_netuid = IntPrompt.ask(
3269
+ "Enter the [blue]origin subnet[/blue] (netuid)"
3270
+ )
3271
+ if not amount:
3272
+ amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to transfer")
3550
3273
 
3551
- if exclude_hotkeys:
3552
- excluded_hotkeys = parse_to_list(
3553
- exclude_hotkeys,
3554
- str,
3555
- "Hotkeys must be a comma-separated list of ss58s or hotkey names, e.g., "
3556
- "`--exclude-hotkeys 5Grw....,5Grw....`.",
3274
+ if dest_netuid is None:
3275
+ dest_netuid = IntPrompt.ask(
3276
+ "Enter the [blue]destination subnet[/blue] (netuid)"
3277
+ )
3278
+
3279
+ return self._run_command(
3280
+ move.transfer_stake(
3281
+ wallet=wallet,
3282
+ subtensor=self.initialize_chain(network),
3283
+ origin_netuid=origin_netuid,
3284
+ dest_netuid=dest_netuid,
3285
+ dest_coldkey_ss58=dest_ss58,
3286
+ amount=amount,
3287
+ interactive_selection=interactive_selection,
3288
+ prompt=prompt,
3557
3289
  )
3290
+ )
3291
+
3292
+ def stake_swap(
3293
+ self,
3294
+ network: Optional[list[str]] = Options.network,
3295
+ wallet_name: Optional[str] = Options.wallet_name,
3296
+ wallet_path: Optional[str] = Options.wallet_path,
3297
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3298
+ origin_netuid: Optional[int] = typer.Option(
3299
+ None,
3300
+ "--origin-netuid",
3301
+ "-o",
3302
+ "--origin",
3303
+ help="The netuid to swap stake from",
3304
+ ),
3305
+ dest_netuid: Optional[int] = typer.Option(
3306
+ None,
3307
+ "--dest-netuid",
3308
+ "-d",
3309
+ "--dest",
3310
+ help="The netuid to swap stake to",
3311
+ ),
3312
+ amount: float = typer.Option(
3313
+ None,
3314
+ "--amount",
3315
+ "-a",
3316
+ help="Amount of stake to swap",
3317
+ ),
3318
+ swap_all: bool = typer.Option(
3319
+ False,
3320
+ "--swap-all",
3321
+ "--all",
3322
+ help="Swap all available stake",
3323
+ ),
3324
+ prompt: bool = Options.prompt,
3325
+ wait_for_inclusion: bool = Options.wait_for_inclusion,
3326
+ wait_for_finalization: bool = Options.wait_for_finalization,
3327
+ quiet: bool = Options.quiet,
3328
+ verbose: bool = Options.verbose,
3329
+ ):
3330
+ """
3331
+ Swap stake between different subnets while keeping the same coldkey-hotkey pair ownership.
3332
+
3333
+ This command allows you to:
3334
+ - Move stake from one subnet to another subnet
3335
+ - Keep the same coldkey ownership
3336
+ - Keep the same hotkey ownership
3337
+
3338
+ You can specify:
3339
+ - The origin subnet (--origin-netuid)
3340
+ - The destination subnet (--dest-netuid)
3341
+ - The amount to swap (--amount)
3342
+
3343
+ If no arguments are provided, an interactive selection menu will be shown.
3344
+
3345
+ EXAMPLE
3346
+
3347
+ Swap 100 TAO from subnet 1 to subnet 2:
3348
+ [green]$[/green] btcli stake swap --wallet-name default --wallet-hotkey default --origin-netuid 1 --dest-netuid 2 --amount 100
3349
+ """
3350
+ console.print(
3351
+ "[dim]This command moves stake from one subnet to another subnet while keeping the same coldkey-hotkey pair.[/dim]"
3352
+ )
3353
+ self.verbosity_handler(quiet, verbose)
3354
+
3355
+ wallet = self.wallet_ask(
3356
+ wallet_name,
3357
+ wallet_path,
3358
+ wallet_hotkey,
3359
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3360
+ validate=WV.WALLET_AND_HOTKEY,
3361
+ )
3362
+
3363
+ interactive_selection = False
3364
+ if origin_netuid is None and dest_netuid is None and not amount:
3365
+ interactive_selection = True
3558
3366
  else:
3559
- excluded_hotkeys = []
3367
+ if origin_netuid is None:
3368
+ origin_netuid = IntPrompt.ask(
3369
+ "Enter the [blue]origin subnet[/blue] (netuid)"
3370
+ )
3371
+ if dest_netuid is None:
3372
+ dest_netuid = IntPrompt.ask(
3373
+ "Enter the [blue]destination subnet[/blue] (netuid)"
3374
+ )
3375
+ if not amount and not swap_all:
3376
+ amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to swap")
3560
3377
 
3561
3378
  return self._run_command(
3562
- stake.unstake(
3563
- wallet,
3564
- self.initialize_chain(network),
3565
- hotkey_ss58_address,
3566
- all_hotkeys,
3567
- included_hotkeys,
3568
- excluded_hotkeys,
3569
- amount,
3570
- keep_stake,
3571
- unstake_all,
3572
- prompt,
3379
+ move.swap_stake(
3380
+ wallet=wallet,
3381
+ subtensor=self.initialize_chain(network),
3382
+ origin_netuid=origin_netuid,
3383
+ destination_netuid=dest_netuid,
3384
+ amount=amount,
3385
+ swap_all=swap_all,
3386
+ interactive_selection=interactive_selection,
3387
+ prompt=prompt,
3388
+ wait_for_inclusion=wait_for_inclusion,
3389
+ wait_for_finalization=wait_for_finalization,
3573
3390
  )
3574
3391
  )
3575
3392
 
@@ -3609,7 +3426,7 @@ class CLIManager:
3609
3426
  wallet_name,
3610
3427
  wallet_path,
3611
3428
  wallet_hotkey,
3612
- ask_for=[WO.NAME, WO.HOTKEY],
3429
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3613
3430
  validate=WV.WALLET_AND_HOTKEY,
3614
3431
  )
3615
3432
 
@@ -3640,18 +3457,8 @@ class CLIManager:
3640
3457
  wallet_hotkey: str = Options.wallet_hotkey,
3641
3458
  wallet_path: str = Options.wallet_path,
3642
3459
  network: Optional[list[str]] = Options.network,
3643
- netuid: Optional[int] = typer.Option(
3644
- None,
3645
- help="The netuid of the subnet, (e.g. 4)",
3646
- prompt=False,
3647
- ),
3648
- all_netuids: bool = typer.Option(
3649
- False,
3650
- "--all-netuids",
3651
- "--all",
3652
- "--allnetuids",
3653
- help="When this flag is used it sets child hotkeys on all subnets.",
3654
- ),
3460
+ netuid: Optional[int] = Options.netuid_not_req,
3461
+ all_netuids: bool = Options.all_netuids,
3655
3462
  proportions: list[float] = typer.Option(
3656
3463
  [],
3657
3464
  "--proportions",
@@ -3664,10 +3471,9 @@ class CLIManager:
3664
3471
  wait_for_finalization: bool = Options.wait_for_finalization,
3665
3472
  quiet: bool = Options.quiet,
3666
3473
  verbose: bool = Options.verbose,
3667
- prompt: bool = Options.prompt,
3668
3474
  ):
3669
3475
  """
3670
- Set child hotkeys on a specified subnet (or all). Overrides currently set children.
3476
+ Set child hotkeys on specified subnets.
3671
3477
 
3672
3478
  Users can specify the 'proportion' to delegate to child hotkeys (ss58 address). The sum of proportions cannot be greater than 1.
3673
3479
 
@@ -3678,15 +3484,8 @@ class CLIManager:
3678
3484
  [green]$[/green] btcli stake child set -c 5FCL3gmjtQV4xxxxuEPEFQVhyyyyqYgNwX7drFLw7MSdBnxP -c 5Hp5dxxxxtGg7pu8dN2btyyyyVA1vELmM9dy8KQv3LxV8PA7 --hotkey default --netuid 1 -p 0.3 -p 0.7
3679
3485
  """
3680
3486
  self.verbosity_handler(quiet, verbose)
3681
- if all_netuids and netuid:
3682
- err_console.print("Specify either a netuid or `--all`, not both.")
3683
- raise typer.Exit()
3684
- if all_netuids:
3685
- netuid = None
3686
- elif not netuid:
3687
- netuid = IntPrompt.ask(
3688
- "Enter a netuid (leave blank for all)", default=None, show_default=True
3689
- )
3487
+ netuid = get_optional_netuid(netuid, all_netuids)
3488
+
3690
3489
  children = list_prompt(
3691
3490
  children,
3692
3491
  str,
@@ -3711,7 +3510,7 @@ class CLIManager:
3711
3510
  wallet_name,
3712
3511
  wallet_path,
3713
3512
  wallet_hotkey,
3714
- ask_for=[WO.NAME, WO.HOTKEY],
3513
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3715
3514
  validate=WV.WALLET_AND_HOTKEY,
3716
3515
  )
3717
3516
  return self._run_command(
@@ -3723,7 +3522,6 @@ class CLIManager:
3723
3522
  proportions=proportions,
3724
3523
  wait_for_finalization=wait_for_finalization,
3725
3524
  wait_for_inclusion=wait_for_inclusion,
3726
- prompt=prompt,
3727
3525
  )
3728
3526
  )
3729
3527
 
@@ -3749,10 +3547,9 @@ class CLIManager:
3749
3547
  wait_for_finalization: bool = Options.wait_for_finalization,
3750
3548
  quiet: bool = Options.quiet,
3751
3549
  verbose: bool = Options.verbose,
3752
- prompt: bool = Options.prompt,
3753
3550
  ):
3754
3551
  """
3755
- Remove all children hotkeys on a specified subnet (or all).
3552
+ Remove all children hotkeys on a specified subnet.
3756
3553
 
3757
3554
  This command is used to remove delegated authority from all child hotkeys, removing their position and influence on the subnet.
3758
3555
 
@@ -3765,7 +3562,7 @@ class CLIManager:
3765
3562
  wallet_name,
3766
3563
  wallet_path,
3767
3564
  wallet_hotkey,
3768
- ask_for=[WO.NAME, WO.HOTKEY],
3565
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3769
3566
  validate=WV.WALLET_AND_HOTKEY,
3770
3567
  )
3771
3568
  if all_netuids and netuid:
@@ -3784,7 +3581,6 @@ class CLIManager:
3784
3581
  netuid,
3785
3582
  wait_for_inclusion,
3786
3583
  wait_for_finalization,
3787
- prompt=prompt,
3788
3584
  )
3789
3585
  )
3790
3586
 
@@ -3821,153 +3617,308 @@ class CLIManager:
3821
3617
  verbose: bool = Options.verbose,
3822
3618
  ):
3823
3619
  """
3824
- Get and set your child hotkey take on a specified subnet.
3620
+ Get and set your child hotkey take on a specified subnet.
3621
+
3622
+ The child hotkey take must be between 0 - 18%.
3623
+
3624
+ EXAMPLE
3625
+
3626
+ To get the current take value, do not use the '--take' option:
3627
+
3628
+ [green]$[/green] btcli stake child take --hotkey <child_hotkey> --netuid 1
3629
+
3630
+ To set a new take value, use the '--take' option:
3631
+
3632
+ [green]$[/green] btcli stake child take --hotkey <child_hotkey> --take 0.12 --netuid 1
3633
+ """
3634
+ self.verbosity_handler(quiet, verbose)
3635
+ wallet = self.wallet_ask(
3636
+ wallet_name,
3637
+ wallet_path,
3638
+ wallet_hotkey,
3639
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3640
+ validate=WV.WALLET_AND_HOTKEY,
3641
+ )
3642
+ if all_netuids and netuid:
3643
+ err_console.print("Specify either a netuid or '--all', not both.")
3644
+ raise typer.Exit()
3645
+ if all_netuids:
3646
+ netuid = None
3647
+ elif not netuid:
3648
+ netuid = IntPrompt.ask(
3649
+ "Enter netuid (leave blank for all)", default=None, show_default=True
3650
+ )
3651
+ return self._run_command(
3652
+ children_hotkeys.childkey_take(
3653
+ wallet=wallet,
3654
+ subtensor=self.initialize_chain(network),
3655
+ netuid=netuid,
3656
+ take=take,
3657
+ hotkey=hotkey,
3658
+ wait_for_inclusion=wait_for_inclusion,
3659
+ wait_for_finalization=wait_for_finalization,
3660
+ prompt=prompt,
3661
+ )
3662
+ )
3663
+
3664
+ def sudo_set(
3665
+ self,
3666
+ network: Optional[list[str]] = Options.network,
3667
+ wallet_name: str = Options.wallet_name,
3668
+ wallet_path: str = Options.wallet_path,
3669
+ wallet_hotkey: str = Options.wallet_hotkey,
3670
+ netuid: int = Options.netuid,
3671
+ param_name: str = typer.Option(
3672
+ "", "--param", "--parameter", help="The subnet hyperparameter to set"
3673
+ ),
3674
+ param_value: str = typer.Option(
3675
+ "", "--value", help="Value to set the hyperparameter to."
3676
+ ),
3677
+ quiet: bool = Options.quiet,
3678
+ verbose: bool = Options.verbose,
3679
+ ):
3680
+ """
3681
+ Used to set hyperparameters for a specific subnet.
3682
+
3683
+ This command allows subnet owners to modify hyperparameters such as its tempo, emission rates, and other hyperparameters.
3684
+
3685
+ EXAMPLE
3686
+
3687
+ [green]$[/green] btcli sudo set --netuid 1 --param tempo --value 400
3688
+ """
3689
+ self.verbosity_handler(quiet, verbose)
3690
+
3691
+ hyperparams = self._run_command(
3692
+ sudo.get_hyperparameters(self.initialize_chain(network), netuid),
3693
+ exit_early=False,
3694
+ )
3695
+
3696
+ if not hyperparams:
3697
+ raise typer.Exit()
3698
+
3699
+ if not param_name:
3700
+ hyperparam_list = [field.name for field in fields(SubnetHyperparameters)]
3701
+ console.print("Available hyperparameters:\n")
3702
+ for idx, param in enumerate(hyperparam_list, start=1):
3703
+ console.print(f" {idx}. {param}")
3704
+ console.print()
3705
+ choice = IntPrompt.ask(
3706
+ "Enter the [bold]number[/bold] of the hyperparameter",
3707
+ choices=[str(i) for i in range(1, len(hyperparam_list) + 1)],
3708
+ show_choices=False,
3709
+ )
3710
+ param_name = hyperparam_list[choice - 1]
3711
+
3712
+ if not param_value:
3713
+ param_value = Prompt.ask(
3714
+ f"Enter the new value for [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{param_name}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] in the VALUE column format"
3715
+ )
3716
+
3717
+ wallet = self.wallet_ask(
3718
+ wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
3719
+ )
3720
+ return self._run_command(
3721
+ sudo.sudo_set_hyperparameter(
3722
+ wallet,
3723
+ self.initialize_chain(network),
3724
+ netuid,
3725
+ param_name,
3726
+ param_value,
3727
+ )
3728
+ )
3729
+
3730
+ def sudo_get(
3731
+ self,
3732
+ network: Optional[list[str]] = Options.network,
3733
+ netuid: int = Options.netuid,
3734
+ quiet: bool = Options.quiet,
3735
+ verbose: bool = Options.verbose,
3736
+ ):
3737
+ """
3738
+ Shows a list of the hyperparameters for the specified subnet.
3739
+
3740
+ The output of this command is the same as that of `btcli subnets hyperparameters`.
3741
+
3742
+ EXAMPLE
3743
+
3744
+ [green]$[/green] btcli sudo get --netuid 1
3745
+ """
3746
+ self.verbosity_handler(quiet, verbose)
3747
+ return self._run_command(
3748
+ sudo.get_hyperparameters(self.initialize_chain(network), netuid)
3749
+ )
3750
+
3751
+ def sudo_senate(
3752
+ self,
3753
+ network: Optional[list[str]] = Options.network,
3754
+ quiet: bool = Options.quiet,
3755
+ verbose: bool = Options.verbose,
3756
+ ):
3757
+ """
3758
+ Shows the Senate members of the Bittensor's governance protocol.
3759
+
3760
+ This command lists the delegates involved in the decision-making process of the Bittensor network, showing their names and wallet addresses. This information is crucial for understanding who holds governance roles within the network.
3761
+
3762
+ EXAMPLE
3763
+ [green]$[/green] btcli sudo senate
3764
+ """
3765
+ self.verbosity_handler(quiet, verbose)
3766
+ return self._run_command(sudo.get_senate(self.initialize_chain(network)))
3767
+
3768
+ def sudo_proposals(
3769
+ self,
3770
+ network: Optional[list[str]] = Options.network,
3771
+ quiet: bool = Options.quiet,
3772
+ verbose: bool = Options.verbose,
3773
+ ):
3774
+ """
3775
+ View active proposals for the senate in the Bittensor's governance protocol.
3825
3776
 
3826
- The child hotkey take must be between 0 - 18%.
3777
+ This command displays the details of ongoing proposals, including proposal hashes, votes, thresholds, and proposal data.
3827
3778
 
3828
3779
  EXAMPLE
3780
+ [green]$[/green] btcli sudo proposals
3781
+ """
3782
+ self.verbosity_handler(quiet, verbose)
3783
+ return self._run_command(sudo.proposals(self.initialize_chain(network)))
3829
3784
 
3830
- To get the current take value, do not use the '--take' option:
3785
+ def sudo_senate_vote(
3786
+ self,
3787
+ network: Optional[list[str]] = Options.network,
3788
+ wallet_name: Optional[str] = Options.wallet_name,
3789
+ wallet_path: Optional[str] = Options.wallet_path,
3790
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3791
+ proposal: str = typer.Option(
3792
+ None,
3793
+ "--proposal",
3794
+ "--proposal-hash",
3795
+ prompt="Enter the proposal hash",
3796
+ help="The hash of the proposal to vote on.",
3797
+ ),
3798
+ prompt: bool = Options.prompt,
3799
+ quiet: bool = Options.quiet,
3800
+ verbose: bool = Options.verbose,
3801
+ vote: bool = typer.Option(
3802
+ None,
3803
+ "--vote-aye/--vote-nay",
3804
+ prompt="Enter y to vote Aye, or enter n to vote Nay",
3805
+ help="The vote casted on the proposal",
3806
+ ),
3807
+ ):
3808
+ """
3809
+ Cast a vote on an active proposal in Bittensor's governance protocol.
3831
3810
 
3832
- [green]$[/green] btcli stake child take --hotkey <child_hotkey> --netuid 1
3811
+ This command is used by Senate members to vote on various proposals that shape the network's future. Use `btcli sudo proposals` to see the active proposals and their hashes.
3833
3812
 
3834
- To set a new take value, use the '--take' option:
3813
+ USAGE
3814
+ The user must specify the hash of the proposal they want to vote on. The command then allows the Senate member to cast a 'Yes' or 'No' vote, contributing to the decision-making process on the proposal. This command is crucial for Senate members to exercise their voting rights on key proposals. It plays a vital role in the governance and evolution of the Bittensor network.
3835
3815
 
3836
- [green]$[/green] btcli stake child take --hotkey <child_hotkey> --take 0.12 --netuid 1
3816
+ EXAMPLE
3817
+ [green]$[/green] btcli sudo senate_vote --proposal <proposal_hash>
3837
3818
  """
3838
3819
  self.verbosity_handler(quiet, verbose)
3839
3820
  wallet = self.wallet_ask(
3840
3821
  wallet_name,
3841
3822
  wallet_path,
3842
3823
  wallet_hotkey,
3843
- ask_for=[WO.NAME, WO.HOTKEY],
3824
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3844
3825
  validate=WV.WALLET_AND_HOTKEY,
3845
3826
  )
3846
- if all_netuids and netuid:
3847
- err_console.print("Specify either a netuid or '--all', not both.")
3848
- raise typer.Exit()
3849
- if all_netuids:
3850
- netuid = None
3851
- elif not netuid:
3852
- netuid = IntPrompt.ask(
3853
- "Enter netuid (leave blank for all)", default=None, show_default=True
3854
- )
3855
3827
  return self._run_command(
3856
- children_hotkeys.childkey_take(
3857
- wallet=wallet,
3858
- subtensor=self.initialize_chain(network),
3859
- netuid=netuid,
3860
- take=take,
3861
- hotkey=hotkey,
3862
- wait_for_inclusion=wait_for_inclusion,
3863
- wait_for_finalization=wait_for_finalization,
3864
- prompt=prompt,
3828
+ sudo.senate_vote(
3829
+ wallet, self.initialize_chain(network), proposal, vote, prompt
3865
3830
  )
3866
3831
  )
3867
3832
 
3868
- def sudo_set(
3833
+ def sudo_set_take(
3869
3834
  self,
3870
3835
  network: Optional[list[str]] = Options.network,
3871
- wallet_name: str = Options.wallet_name,
3872
- wallet_path: str = Options.wallet_path,
3873
- wallet_hotkey: str = Options.wallet_hotkey,
3874
- netuid: int = Options.netuid,
3875
- param_name: str = typer.Option(
3876
- "", "--param", "--parameter", help="The subnet hyperparameter to set"
3877
- ),
3878
- param_value: str = typer.Option(
3879
- "", "--value", help="Value to set the hyperparameter to."
3880
- ),
3836
+ wallet_name: Optional[str] = Options.wallet_name,
3837
+ wallet_path: Optional[str] = Options.wallet_path,
3838
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3839
+ take: float = typer.Option(None, help="The new take value."),
3881
3840
  quiet: bool = Options.quiet,
3882
3841
  verbose: bool = Options.verbose,
3883
3842
  ):
3884
3843
  """
3885
- Used to set hyperparameters for a specific subnet.
3844
+ Allows users to change their delegate take percentage.
3886
3845
 
3887
- This command allows subnet owners to modify hyperparameters such as its tempo, emission rates, and other hyperparameters.
3846
+ This command can be used to update the delegate takes. To run the command, the user must have a configured wallet with both hotkey and coldkey.
3847
+ The command makes sure the new take value is within 0-18% range.
3888
3848
 
3889
3849
  EXAMPLE
3890
-
3891
- [green]$[/green] btcli sudo set --netuid 1 --param tempo --value 400
3850
+ [green]$[/green] btcli sudo set-take --wallet-name my_wallet --wallet-hotkey my_hotkey
3892
3851
  """
3852
+ max_value = 0.18
3853
+ min_value = 0.00
3893
3854
  self.verbosity_handler(quiet, verbose)
3894
3855
 
3895
- if not param_name or not param_value:
3896
- hyperparams = self._run_command(
3897
- sudo.get_hyperparameters(self.initialize_chain(network), netuid)
3898
- )
3899
- if not hyperparams:
3900
- raise typer.Exit()
3856
+ wallet = self.wallet_ask(
3857
+ wallet_name,
3858
+ wallet_path,
3859
+ wallet_hotkey,
3860
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3861
+ validate=WV.WALLET_AND_HOTKEY,
3862
+ )
3901
3863
 
3902
- if not param_name:
3903
- hyperparam_list = [field.name for field in fields(SubnetHyperparameters)]
3904
- console.print("Available hyperparameters:\n")
3905
- for idx, param in enumerate(hyperparam_list, start=1):
3906
- console.print(f" {idx}. {param}")
3907
- console.print()
3908
- choice = IntPrompt.ask(
3909
- "Enter the [bold]number[/bold] of the hyperparameter",
3910
- choices=[str(i) for i in range(1, len(hyperparam_list) + 1)],
3911
- show_choices=False,
3912
- )
3913
- param_name = hyperparam_list[choice - 1]
3864
+ self._run_command(
3865
+ sudo.display_current_take(self.initialize_chain(network), wallet),
3866
+ exit_early=False,
3867
+ )
3914
3868
 
3915
- if param_name in ["alpha_high", "alpha_low"]:
3916
- param_name = "alpha_values"
3917
- low_val = FloatPrompt.ask(
3918
- "Enter the new value for [dark_orange]alpha_low[/dark_orange]"
3919
- )
3920
- high_val = FloatPrompt.ask(
3921
- "Enter the new value for [dark_orange]alpha_high[/dark_orange]"
3869
+ if not take:
3870
+ take = FloatPrompt.ask(
3871
+ f"Enter [blue]take value[/blue] (0.18 for 18%) [blue]Min: {min_value} Max: {max_value}"
3922
3872
  )
3923
- param_value = f"{low_val},{high_val}"
3924
-
3925
- if not param_value:
3926
- param_value = Prompt.ask(
3927
- f"Enter the new value for [dark_orange]{param_name}[/dark_orange] in the VALUE column format"
3873
+ if not (min_value <= take <= max_value):
3874
+ print_error(
3875
+ f"Take value must be between {min_value} and {max_value}. Provided value: {take}"
3928
3876
  )
3877
+ raise typer.Exit()
3929
3878
 
3930
- wallet = self.wallet_ask(
3931
- wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
3932
- )
3933
3879
  return self._run_command(
3934
- sudo.sudo_set_hyperparameter(
3935
- wallet,
3936
- self.initialize_chain(network),
3937
- netuid,
3938
- param_name,
3939
- param_value,
3940
- )
3880
+ sudo.set_take(wallet, self.initialize_chain(network), take)
3941
3881
  )
3942
3882
 
3943
- def sudo_get(
3883
+ def sudo_get_take(
3944
3884
  self,
3945
3885
  network: Optional[list[str]] = Options.network,
3946
- netuid: int = Options.netuid,
3886
+ wallet_name: Optional[str] = Options.wallet_name,
3887
+ wallet_path: Optional[str] = Options.wallet_path,
3888
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3947
3889
  quiet: bool = Options.quiet,
3948
3890
  verbose: bool = Options.verbose,
3949
3891
  ):
3950
3892
  """
3951
- Shows a list of the hyperparameters for the specified subnet.
3893
+ Allows users to check their delegate take percentage.
3952
3894
 
3953
- The output of this command is the same as that of `btcli subnets hyperparameters`.
3895
+ This command can be used to fetch the delegate take of your hotkey.
3954
3896
 
3955
3897
  EXAMPLE
3956
-
3957
- [green]$[/green] btcli sudo get --netuid 1
3898
+ [green]$[/green] btcli sudo get-take --wallet-name my_wallet --wallet-hotkey my_hotkey
3958
3899
  """
3959
3900
  self.verbosity_handler(quiet, verbose)
3960
- return self._run_command(
3961
- sudo.get_hyperparameters(self.initialize_chain(network), netuid)
3901
+
3902
+ wallet = self.wallet_ask(
3903
+ wallet_name,
3904
+ wallet_path,
3905
+ wallet_hotkey,
3906
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3907
+ validate=WV.WALLET_AND_HOTKEY,
3908
+ )
3909
+
3910
+ self._run_command(
3911
+ sudo.display_current_take(self.initialize_chain(network), wallet)
3962
3912
  )
3963
3913
 
3964
3914
  def subnets_list(
3965
3915
  self,
3966
3916
  network: Optional[list[str]] = Options.network,
3967
- reuse_last: bool = Options.reuse_last,
3968
- html_output: bool = Options.html_output,
3917
+ # reuse_last: bool = Options.reuse_last,
3918
+ # html_output: bool = Options.html_output,
3969
3919
  quiet: bool = Options.quiet,
3970
3920
  verbose: bool = Options.verbose,
3921
+ live_mode: bool = Options.live,
3971
3922
  ):
3972
3923
  """
3973
3924
  List all subnets and their detailed information.
@@ -3988,48 +3939,189 @@ class CLIManager:
3988
3939
  [green]$[/green] btcli subnets list
3989
3940
  """
3990
3941
  self.verbosity_handler(quiet, verbose)
3991
- if (reuse_last or html_output) and self.config.get("use_cache") is False:
3992
- err_console.print(
3993
- "Unable to use `--reuse-last` or `--html` when config 'no-cache' is set to 'True'. "
3994
- "Change the config to 'False' using `btcli config set`."
3995
- )
3996
- raise typer.Exit()
3997
- if reuse_last:
3998
- subtensor = None
3999
- else:
4000
- subtensor = self.initialize_chain(network)
3942
+ # if (reuse_last or html_output) and self.config.get("use_cache") is False:
3943
+ # err_console.print(
3944
+ # "Unable to use `--reuse-last` or `--html` when config 'no-cache' is set to 'True'. "
3945
+ # "Change the config to 'False' using `btcli config set`."
3946
+ # )
3947
+ # raise typer.Exit()
3948
+ # if reuse_last:
3949
+ # subtensor = None
3950
+ # else:
3951
+ subtensor = self.initialize_chain(network)
4001
3952
  return self._run_command(
4002
3953
  subnets.subnets_list(
4003
3954
  subtensor,
4004
- reuse_last,
4005
- html_output,
3955
+ False, # reuse-last
3956
+ False, # html-output
4006
3957
  not self.config.get("use_cache", True),
3958
+ verbose,
3959
+ live_mode,
3960
+ )
3961
+ )
3962
+
3963
+ def subnets_price(
3964
+ self,
3965
+ network: Optional[list[str]] = Options.network,
3966
+ netuids: str = typer.Option(
3967
+ None,
3968
+ "--netuids",
3969
+ "--netuid",
3970
+ "-n",
3971
+ help="Netuid(s) to show the price for.",
3972
+ ),
3973
+ interval_hours: int = typer.Option(
3974
+ 24,
3975
+ "--interval-hours",
3976
+ "--interval",
3977
+ help="The number of hours to show the historical price for.",
3978
+ ),
3979
+ all_netuids: bool = typer.Option(
3980
+ False,
3981
+ "--all-netuids",
3982
+ "--all",
3983
+ help="Show the price for all subnets.",
3984
+ ),
3985
+ log_scale: bool = typer.Option(
3986
+ False,
3987
+ "--log-scale",
3988
+ "--log",
3989
+ help="Show the price in log scale.",
3990
+ ),
3991
+ html_output: bool = Options.html_output,
3992
+ ):
3993
+ """
3994
+ Shows the historical price of a subnet for the past 24 hours.
3995
+
3996
+ This command displays the historical price of a subnet for the past 24 hours.
3997
+ If the `--all` flag is used, the command will display the price for all subnets in html format.
3998
+ If the `--html` flag is used, the command will display the price in an HTML chart.
3999
+ If the `--log-scale` flag is used, the command will display the price in log scale.
4000
+ If no html flag is used, the command will display the price in the cli.
4001
+
4002
+ EXAMPLE
4003
+
4004
+ [green]$[/green] btcli subnets price --netuid 1
4005
+ [green]$[/green] btcli subnets price --netuid 1 --html --log
4006
+ [green]$[/green] btcli subnets price --all --html
4007
+ [green]$[/green] btcli subnets price --netuids 1,2,3,4 --html
4008
+ """
4009
+ if netuids:
4010
+ netuids = parse_to_list(
4011
+ netuids,
4012
+ int,
4013
+ "Netuids must be a comma-separated list of ints, e.g., `--netuids 1,2,3,4`.",
4014
+ )
4015
+ if all_netuids and netuids:
4016
+ print_error("Cannot specify both --netuid and --all-netuids")
4017
+ raise typer.Exit()
4018
+
4019
+ if not netuids and not all_netuids:
4020
+ netuids = Prompt.ask(
4021
+ "Enter the [blue]netuid(s)[/blue] to view the price of in comma-separated format [dim](or Press Enter to view all subnets)[/dim]",
4022
+ )
4023
+ if not netuids:
4024
+ all_netuids = True
4025
+ html_output = True
4026
+ else:
4027
+ netuids = parse_to_list(
4028
+ netuids,
4029
+ int,
4030
+ "Netuids must be a comma-separated list of ints, e.g., `--netuids 1,2,3,4`.",
4031
+ )
4032
+
4033
+ if all_netuids:
4034
+ html_output = True
4035
+
4036
+ if html_output and is_linux():
4037
+ print_linux_dependency_message()
4038
+
4039
+ return self._run_command(
4040
+ price.price(
4041
+ self.initialize_chain(network),
4042
+ netuids,
4043
+ all_netuids,
4044
+ interval_hours,
4045
+ html_output,
4046
+ log_scale,
4047
+ )
4048
+ )
4049
+
4050
+ def subnets_show(
4051
+ self,
4052
+ network: Optional[list[str]] = Options.network,
4053
+ netuid: int = Options.netuid,
4054
+ quiet: bool = Options.quiet,
4055
+ verbose: bool = Options.verbose,
4056
+ prompt: bool = Options.prompt,
4057
+ ):
4058
+ """
4059
+ Displays detailed information about a subnet including participants and their state.
4060
+
4061
+ EXAMPLE
4062
+
4063
+ [green]$[/green] btcli subnets list
4064
+ """
4065
+ self.verbosity_handler(quiet, verbose)
4066
+ subtensor = self.initialize_chain(network)
4067
+ return self._run_command(
4068
+ subnets.show(
4069
+ subtensor,
4070
+ netuid,
4071
+ verbose=verbose,
4072
+ prompt=prompt,
4007
4073
  )
4008
4074
  )
4009
4075
 
4010
- def subnets_lock_cost(
4076
+ def subnets_burn_cost(
4011
4077
  self,
4012
4078
  network: Optional[list[str]] = Options.network,
4013
4079
  quiet: bool = Options.quiet,
4014
4080
  verbose: bool = Options.verbose,
4015
4081
  ):
4016
4082
  """
4017
- Shows the required amount of TAO to be locked for creating a new subnet, i.e., cost of registering a new subnet.
4083
+ Shows the required amount of TAO to be recycled for creating a new subnet, i.e., cost of registering a new subnet.
4018
4084
 
4019
4085
  The current implementation anneals the cost of creating a subnet over a period of two days. If the displayed cost is unappealing to you, check back in a day or two to see if it has decreased to a more affordable level.
4020
4086
 
4021
4087
  EXAMPLE
4022
4088
 
4023
- [green]$[/green] btcli subnets lock_cost
4089
+ [green]$[/green] btcli subnets burn_cost
4024
4090
  """
4025
4091
  self.verbosity_handler(quiet, verbose)
4026
- return self._run_command(subnets.lock_cost(self.initialize_chain(network)))
4092
+ return self._run_command(subnets.burn_cost(self.initialize_chain(network)))
4027
4093
 
4028
4094
  def subnets_create(
4029
4095
  self,
4030
4096
  wallet_name: str = Options.wallet_name,
4031
4097
  wallet_path: str = Options.wallet_path,
4098
+ wallet_hotkey: str = Options.wallet_hotkey,
4032
4099
  network: Optional[list[str]] = Options.network,
4100
+ subnet_name: Optional[str] = typer.Option(
4101
+ None, "--subnet-name", "--name", help="Name of the subnet"
4102
+ ),
4103
+ github_repo: Optional[str] = typer.Option(
4104
+ None, "--github-repo", "--repo", help="GitHub repository URL"
4105
+ ),
4106
+ subnet_contact: Optional[str] = typer.Option(
4107
+ None,
4108
+ "--subnet-contact",
4109
+ "--contact",
4110
+ "--email",
4111
+ help="Contact email for subnet",
4112
+ ),
4113
+ subnet_url: Optional[str] = typer.Option(
4114
+ None, "--subnet-url", "--url", help="Subnet URL"
4115
+ ),
4116
+ discord: Optional[str] = typer.Option(
4117
+ None, "--discord-handle", "--discord", help="Discord handle"
4118
+ ),
4119
+ description: Optional[str] = typer.Option(
4120
+ None, "--description", help="Description"
4121
+ ),
4122
+ additional_info: Optional[str] = typer.Option(
4123
+ None, "--additional-info", help="Additional information"
4124
+ ),
4033
4125
  prompt: bool = Options.prompt,
4034
4126
  quiet: bool = Options.quiet,
4035
4127
  verbose: bool = Options.verbose,
@@ -4045,13 +4137,44 @@ class CLIManager:
4045
4137
  wallet = self.wallet_ask(
4046
4138
  wallet_name,
4047
4139
  wallet_path,
4048
- None,
4049
- ask_for=[WO.NAME],
4050
- validate=WV.WALLET,
4140
+ wallet_hotkey,
4141
+ ask_for=[
4142
+ WO.NAME,
4143
+ WO.HOTKEY,
4144
+ WO.PATH,
4145
+ ],
4146
+ validate=WV.WALLET_AND_HOTKEY,
4051
4147
  )
4052
- return self._run_command(
4053
- subnets.create(wallet, self.initialize_chain(network), prompt)
4148
+ identity = prompt_for_subnet_identity(
4149
+ subnet_name=subnet_name,
4150
+ github_repo=github_repo,
4151
+ subnet_contact=subnet_contact,
4152
+ subnet_url=subnet_url,
4153
+ discord=discord,
4154
+ description=description,
4155
+ additional=additional_info,
4054
4156
  )
4157
+ success = self._run_command(
4158
+ subnets.create(wallet, self.initialize_chain(network), identity, prompt),
4159
+ exit_early=False,
4160
+ )
4161
+
4162
+ if success and prompt:
4163
+ set_id = Confirm.ask(
4164
+ "[dark_sea_green3]Do you want to set/update your identity?",
4165
+ default=False,
4166
+ show_default=True,
4167
+ )
4168
+ if set_id:
4169
+ self.wallet_set_id(
4170
+ wallet_name=wallet.name,
4171
+ wallet_hotkey=wallet.hotkey,
4172
+ wallet_path=wallet.path,
4173
+ network=network,
4174
+ prompt=prompt,
4175
+ quiet=quiet,
4176
+ verbose=verbose,
4177
+ )
4055
4178
 
4056
4179
  def subnets_pow_register(
4057
4180
  self,
@@ -4100,7 +4223,6 @@ class CLIManager:
4100
4223
  "-tbp",
4101
4224
  help="Set the number of threads per block for CUDA.",
4102
4225
  ),
4103
- prompt: bool = Options.prompt,
4104
4226
  ):
4105
4227
  """
4106
4228
  Register a neuron (a subnet validator or a subnet miner) using Proof of Work (POW).
@@ -4126,7 +4248,7 @@ class CLIManager:
4126
4248
  wallet_name,
4127
4249
  wallet_path,
4128
4250
  wallet_hotkey,
4129
- ask_for=[WO.NAME, WO.HOTKEY],
4251
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
4130
4252
  validate=WV.WALLET_AND_HOTKEY,
4131
4253
  ),
4132
4254
  self.initialize_chain(network),
@@ -4138,7 +4260,6 @@ class CLIManager:
4138
4260
  use_cuda,
4139
4261
  dev_id,
4140
4262
  threads_per_block,
4141
- prompt=prompt,
4142
4263
  )
4143
4264
  )
4144
4265
 
@@ -4169,7 +4290,7 @@ class CLIManager:
4169
4290
  wallet_name,
4170
4291
  wallet_path,
4171
4292
  wallet_hotkey,
4172
- ask_for=[WO.NAME, WO.HOTKEY],
4293
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
4173
4294
  validate=WV.WALLET_AND_HOTKEY,
4174
4295
  )
4175
4296
  return self._run_command(
@@ -4255,6 +4376,12 @@ class CLIManager:
4255
4376
  )
4256
4377
  raise typer.Exit()
4257
4378
 
4379
+ # For Rao games
4380
+ effective_network = get_effective_network(self.config, network)
4381
+ if is_rao_network(effective_network):
4382
+ print_error("This command is disabled on the 'rao' network.")
4383
+ raise typer.Exit()
4384
+
4258
4385
  if reuse_last:
4259
4386
  if netuid is not None:
4260
4387
  console.print("Cannot specify netuid when using `--reuse-last`")
@@ -4298,7 +4425,6 @@ class CLIManager:
4298
4425
  ),
4299
4426
  quiet: bool = Options.quiet,
4300
4427
  verbose: bool = Options.verbose,
4301
- prompt: bool = Options.prompt,
4302
4428
  ):
4303
4429
  """
4304
4430
  Reveal weights for a specific subnet.
@@ -4357,7 +4483,7 @@ class CLIManager:
4357
4483
  wallet_name,
4358
4484
  wallet_path,
4359
4485
  wallet_hotkey,
4360
- ask_for=[WO.NAME, WO.HOTKEY],
4486
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
4361
4487
  validate=WV.WALLET_AND_HOTKEY,
4362
4488
  )
4363
4489
 
@@ -4370,7 +4496,6 @@ class CLIManager:
4370
4496
  weights,
4371
4497
  salt,
4372
4498
  __version_as_int__,
4373
- prompt=prompt,
4374
4499
  )
4375
4500
  )
4376
4501
 
@@ -4396,7 +4521,6 @@ class CLIManager:
4396
4521
  ),
4397
4522
  quiet: bool = Options.quiet,
4398
4523
  verbose: bool = Options.verbose,
4399
- prompt: bool = Options.prompt,
4400
4524
  ):
4401
4525
  """
4402
4526
 
@@ -4455,7 +4579,7 @@ class CLIManager:
4455
4579
  wallet_name,
4456
4580
  wallet_path,
4457
4581
  wallet_hotkey,
4458
- ask_for=[WO.NAME, WO.HOTKEY],
4582
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
4459
4583
  validate=WV.WALLET_AND_HOTKEY,
4460
4584
  )
4461
4585
  return self._run_command(
@@ -4467,7 +4591,6 @@ class CLIManager:
4467
4591
  weights,
4468
4592
  salt,
4469
4593
  __version_as_int__,
4470
- prompt=prompt,
4471
4594
  )
4472
4595
  )
4473
4596