bittensor-cli 8.4.4__py3-none-any.whl → 9.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bittensor_cli/__init__.py +1 -1
- bittensor_cli/cli.py +1827 -1394
- bittensor_cli/src/__init__.py +623 -168
- bittensor_cli/src/bittensor/balances.py +41 -8
- bittensor_cli/src/bittensor/chain_data.py +557 -428
- bittensor_cli/src/bittensor/extrinsics/registration.py +129 -23
- bittensor_cli/src/bittensor/extrinsics/root.py +3 -3
- bittensor_cli/src/bittensor/extrinsics/transfer.py +6 -11
- bittensor_cli/src/bittensor/minigraph.py +46 -8
- bittensor_cli/src/bittensor/subtensor_interface.py +567 -250
- bittensor_cli/src/bittensor/utils.py +370 -25
- bittensor_cli/src/commands/stake/__init__.py +154 -0
- bittensor_cli/src/commands/stake/add.py +625 -0
- bittensor_cli/src/commands/stake/children_hotkeys.py +103 -75
- bittensor_cli/src/commands/stake/list.py +687 -0
- bittensor_cli/src/commands/stake/move.py +1000 -0
- bittensor_cli/src/commands/stake/remove.py +1146 -0
- bittensor_cli/src/commands/subnets/__init__.py +0 -0
- bittensor_cli/src/commands/subnets/price.py +867 -0
- bittensor_cli/src/commands/subnets/subnets.py +2028 -0
- bittensor_cli/src/commands/sudo.py +554 -12
- bittensor_cli/src/commands/wallets.py +225 -531
- bittensor_cli/src/commands/weights.py +2 -2
- {bittensor_cli-8.4.4.dist-info → bittensor_cli-9.0.0.dist-info}/METADATA +7 -4
- bittensor_cli-9.0.0.dist-info/RECORD +34 -0
- bittensor_cli/src/bittensor/async_substrate_interface.py +0 -2748
- bittensor_cli/src/commands/root.py +0 -1787
- bittensor_cli/src/commands/stake/stake.py +0 -1448
- bittensor_cli/src/commands/subnets.py +0 -897
- bittensor_cli-8.4.4.dist-info/RECORD +0 -31
- {bittensor_cli-8.4.4.dist-info → bittensor_cli-9.0.0.dist-info}/WHEEL +0 -0
- {bittensor_cli-8.4.4.dist-info → bittensor_cli-9.0.0.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-8.4.4.dist-info → bittensor_cli-9.0.0.dist-info}/top_level.txt +0 -0
bittensor_cli/cli.py
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
import asyncio
|
3
|
-
import binascii
|
4
3
|
import curses
|
5
|
-
|
4
|
+
import importlib
|
6
5
|
import os.path
|
7
6
|
import re
|
8
7
|
import ssl
|
@@ -19,21 +18,28 @@ from bittensor_wallet import Wallet
|
|
19
18
|
from rich import box
|
20
19
|
from rich.prompt import Confirm, FloatPrompt, Prompt, IntPrompt
|
21
20
|
from rich.table import Column, Table
|
21
|
+
from rich.tree import Tree
|
22
22
|
from bittensor_cli.src import (
|
23
23
|
defaults,
|
24
24
|
HELP_PANELS,
|
25
25
|
WalletOptions as WO,
|
26
26
|
WalletValidationTypes as WV,
|
27
27
|
Constants,
|
28
|
+
COLOR_PALETTE,
|
28
29
|
)
|
29
30
|
from bittensor_cli.src.bittensor import utils
|
30
31
|
from bittensor_cli.src.bittensor.balances import Balance
|
31
|
-
from
|
32
|
-
|
33
|
-
)
|
34
|
-
from bittensor_cli.src.commands import root, subnets, sudo, wallets
|
32
|
+
from async_substrate_interface.errors import SubstrateRequestException
|
33
|
+
from bittensor_cli.src.commands import sudo, wallets
|
35
34
|
from bittensor_cli.src.commands import weights as weights_cmds
|
36
|
-
from bittensor_cli.src.commands.
|
35
|
+
from bittensor_cli.src.commands.subnets import price, subnets
|
36
|
+
from bittensor_cli.src.commands.stake import (
|
37
|
+
children_hotkeys,
|
38
|
+
list as list_stake,
|
39
|
+
move as move_stake,
|
40
|
+
add as add_stake,
|
41
|
+
remove as remove_stake,
|
42
|
+
)
|
37
43
|
from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
|
38
44
|
from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters
|
39
45
|
from bittensor_cli.src.bittensor.utils import (
|
@@ -43,22 +49,30 @@ from bittensor_cli.src.bittensor.utils import (
|
|
43
49
|
is_valid_ss58_address,
|
44
50
|
print_error,
|
45
51
|
validate_chain_endpoint,
|
46
|
-
|
52
|
+
validate_netuid,
|
53
|
+
is_rao_network,
|
54
|
+
get_effective_network,
|
55
|
+
prompt_for_identity,
|
56
|
+
validate_uri,
|
57
|
+
prompt_for_subnet_identity,
|
58
|
+
print_linux_dependency_message,
|
59
|
+
is_linux,
|
60
|
+
validate_rate_tolerance,
|
47
61
|
)
|
48
62
|
from typing_extensions import Annotated
|
49
|
-
from
|
50
|
-
from websockets import ConnectionClosed
|
63
|
+
from websockets import ConnectionClosed, InvalidHandshake
|
51
64
|
from yaml import safe_dump, safe_load
|
52
65
|
|
53
66
|
try:
|
54
67
|
from git import Repo, GitError
|
55
68
|
except ImportError:
|
69
|
+
Repo = None
|
56
70
|
|
57
71
|
class GitError(Exception):
|
58
72
|
pass
|
59
73
|
|
60
74
|
|
61
|
-
__version__ = "
|
75
|
+
__version__ = "9.0.0"
|
62
76
|
|
63
77
|
|
64
78
|
_core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0)
|
@@ -155,8 +169,19 @@ class Options:
|
|
155
169
|
)
|
156
170
|
netuid = typer.Option(
|
157
171
|
None,
|
158
|
-
help="The netuid of the subnet in the
|
172
|
+
help="The netuid of the subnet in the 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 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,39 @@ 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
|
+
)
|
241
|
+
rate_tolerance = typer.Option(
|
242
|
+
None,
|
243
|
+
"--slippage",
|
244
|
+
"--slippage-tolerance",
|
245
|
+
"--tolerance",
|
246
|
+
help="Set the rate tolerance percentage for transactions (default: 0.05%).",
|
247
|
+
callback=validate_rate_tolerance,
|
248
|
+
)
|
249
|
+
safe_staking = typer.Option(
|
250
|
+
None,
|
251
|
+
"--safe-staking/--no-safe-staking",
|
252
|
+
"--safe/--unsafe",
|
253
|
+
help="Enable or disable safe staking mode (default: enabled).",
|
254
|
+
)
|
255
|
+
allow_partial_stake = typer.Option(
|
256
|
+
None,
|
257
|
+
"--allow-partial-stake/--no-allow-partial-stake",
|
258
|
+
"--partial/--no-partial",
|
259
|
+
"--allow/--not-allow",
|
260
|
+
"--allow-partial/--not-partial",
|
261
|
+
help="Enable or disable partial stake mode (default: disabled).",
|
262
|
+
)
|
205
263
|
|
206
264
|
|
207
265
|
def list_prompt(init_var: list, list_type: type, help_text: str) -> list:
|
@@ -244,7 +302,8 @@ def parse_to_list(
|
|
244
302
|
def verbosity_console_handler(verbosity_level: int = 1) -> None:
|
245
303
|
"""
|
246
304
|
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
|
305
|
+
:param verbosity_level: int corresponding to verbosity level of console output (0 is quiet, 1 is normal, 2 is
|
306
|
+
verbose)
|
248
307
|
"""
|
249
308
|
if verbosity_level not in range(3):
|
250
309
|
raise ValueError(
|
@@ -264,6 +323,32 @@ def verbosity_console_handler(verbosity_level: int = 1) -> None:
|
|
264
323
|
verbose_console.quiet = False
|
265
324
|
|
266
325
|
|
326
|
+
def get_optional_netuid(netuid: Optional[int], all_netuids: bool) -> Optional[int]:
|
327
|
+
"""
|
328
|
+
Parses options to determine if the user wants to use a specific netuid or all netuids (None)
|
329
|
+
|
330
|
+
Returns:
|
331
|
+
None if using all netuids, otherwise int for the netuid to use
|
332
|
+
"""
|
333
|
+
if netuid is None and all_netuids is True:
|
334
|
+
return None
|
335
|
+
elif netuid is None and all_netuids is False:
|
336
|
+
answer = Prompt.ask(
|
337
|
+
f"Enter the [{COLOR_PALETTE['GENERAL']['SUBHEADING_MAIN']}]netuid"
|
338
|
+
f"[/{COLOR_PALETTE['GENERAL']['SUBHEADING_MAIN']}] to use. Leave blank for all netuids",
|
339
|
+
default=None,
|
340
|
+
show_default=False,
|
341
|
+
)
|
342
|
+
if answer is None:
|
343
|
+
return None
|
344
|
+
if answer.lower() == "all":
|
345
|
+
return None
|
346
|
+
else:
|
347
|
+
return int(answer)
|
348
|
+
else:
|
349
|
+
return netuid
|
350
|
+
|
351
|
+
|
267
352
|
def get_n_words(n_words: Optional[int]) -> int:
|
268
353
|
"""
|
269
354
|
Prompts the user to select the number of words used in the mnemonic if not supplied or not within the
|
@@ -413,12 +498,21 @@ def version_callback(value: bool):
|
|
413
498
|
raise typer.Exit()
|
414
499
|
|
415
500
|
|
501
|
+
def commands_callback(value: bool):
|
502
|
+
"""
|
503
|
+
Prints a tree of commands for the app
|
504
|
+
"""
|
505
|
+
if value:
|
506
|
+
cli = CLIManager()
|
507
|
+
console.print(cli.generate_command_tree())
|
508
|
+
raise typer.Exit()
|
509
|
+
|
510
|
+
|
416
511
|
class CLIManager:
|
417
512
|
"""
|
418
513
|
:var app: the main CLI Typer app
|
419
514
|
:var config_app: the Typer app as it relates to config commands
|
420
515
|
: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
516
|
:var stake_app: the Typer app as it relates to stake commands
|
423
517
|
:var sudo_app: the Typer app as it relates to sudo commands
|
424
518
|
:var subnets_app: the Typer app as it relates to subnets commands
|
@@ -429,10 +523,10 @@ class CLIManager:
|
|
429
523
|
app: typer.Typer
|
430
524
|
config_app: typer.Typer
|
431
525
|
wallet_app: typer.Typer
|
432
|
-
root_app: typer.Typer
|
433
526
|
subnets_app: typer.Typer
|
434
527
|
weights_app: typer.Typer
|
435
528
|
utils_app = typer.Typer(epilog=_epilog)
|
529
|
+
asyncio_runner = asyncio
|
436
530
|
|
437
531
|
def __init__(self):
|
438
532
|
self.config = {
|
@@ -441,23 +535,29 @@ class CLIManager:
|
|
441
535
|
"wallet_hotkey": None,
|
442
536
|
"network": None,
|
443
537
|
"use_cache": True,
|
444
|
-
"
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
538
|
+
"rate_tolerance": None,
|
539
|
+
"safe_staking": True,
|
540
|
+
"allow_partial_stake": False,
|
541
|
+
# Commenting this out as this needs to get updated
|
542
|
+
# "metagraph_cols": {
|
543
|
+
# "UID": True,
|
544
|
+
# "GLOBAL_STAKE": True,
|
545
|
+
# "LOCAL_STAKE": True,
|
546
|
+
# "STAKE_WEIGHT": True,
|
547
|
+
# "RANK": True,
|
548
|
+
# "TRUST": True,
|
549
|
+
# "CONSENSUS": True,
|
550
|
+
# "INCENTIVE": True,
|
551
|
+
# "DIVIDENDS": True,
|
552
|
+
# "EMISSION": True,
|
553
|
+
# "VTRUST": True,
|
554
|
+
# "VAL": True,
|
555
|
+
# "UPDATED": True,
|
556
|
+
# "ACTIVE": True,
|
557
|
+
# "AXON": True,
|
558
|
+
# "HOTKEY": True,
|
559
|
+
# "COLDKEY": True,
|
560
|
+
# },
|
461
561
|
}
|
462
562
|
self.subtensor = None
|
463
563
|
self.config_base_path = os.path.expanduser(defaults.config.base_path)
|
@@ -471,7 +571,6 @@ class CLIManager:
|
|
471
571
|
)
|
472
572
|
self.config_app = typer.Typer(epilog=_epilog)
|
473
573
|
self.wallet_app = typer.Typer(epilog=_epilog)
|
474
|
-
self.root_app = typer.Typer(epilog=_epilog)
|
475
574
|
self.stake_app = typer.Typer(epilog=_epilog)
|
476
575
|
self.sudo_app = typer.Typer(epilog=_epilog)
|
477
576
|
self.subnets_app = typer.Typer(epilog=_epilog)
|
@@ -501,15 +600,6 @@ class CLIManager:
|
|
501
600
|
self.wallet_app, name="wallets", hidden=True, no_args_is_help=True
|
502
601
|
)
|
503
602
|
|
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
603
|
# stake aliases
|
514
604
|
self.app.add_typer(
|
515
605
|
self.stake_app,
|
@@ -558,13 +648,15 @@ class CLIManager:
|
|
558
648
|
)
|
559
649
|
|
560
650
|
# utils app
|
561
|
-
self.app.add_typer(
|
651
|
+
self.app.add_typer(
|
652
|
+
self.utils_app, name="utils", no_args_is_help=True, hidden=True
|
653
|
+
)
|
562
654
|
|
563
655
|
# config commands
|
564
656
|
self.config_app.command("set")(self.set_config)
|
565
657
|
self.config_app.command("get")(self.get_config)
|
566
658
|
self.config_app.command("clear")(self.del_config)
|
567
|
-
self.config_app.command("metagraph")(self.metagraph_config)
|
659
|
+
self.config_app.command("metagraph", hidden=True)(self.metagraph_config)
|
568
660
|
|
569
661
|
# wallet commands
|
570
662
|
self.wallet_app.command(
|
@@ -595,16 +687,21 @@ class CLIManager:
|
|
595
687
|
"balance", rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"]
|
596
688
|
)(self.wallet_balance)
|
597
689
|
self.wallet_app.command(
|
598
|
-
"history",
|
690
|
+
"history",
|
691
|
+
rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"],
|
692
|
+
hidden=True,
|
599
693
|
)(self.wallet_history)
|
600
694
|
self.wallet_app.command(
|
601
|
-
"overview",
|
695
|
+
"overview",
|
696
|
+
rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"],
|
602
697
|
)(self.wallet_overview)
|
603
698
|
self.wallet_app.command(
|
604
699
|
"transfer", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
|
605
700
|
)(self.wallet_transfer)
|
606
701
|
self.wallet_app.command(
|
607
|
-
"inspect",
|
702
|
+
"inspect",
|
703
|
+
rich_help_panel=HELP_PANELS["WALLET"]["INFORMATION"],
|
704
|
+
hidden=True,
|
608
705
|
)(self.wallet_inspect)
|
609
706
|
self.wallet_app.command(
|
610
707
|
"faucet", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
|
@@ -619,59 +716,25 @@ class CLIManager:
|
|
619
716
|
"sign", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
|
620
717
|
)(self.wallet_sign)
|
621
718
|
|
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
719
|
# stake commands
|
666
|
-
self.stake_app.command(
|
667
|
-
"show", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
|
668
|
-
)(self.stake_show)
|
669
720
|
self.stake_app.command(
|
670
721
|
"add", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
|
671
722
|
)(self.stake_add)
|
672
723
|
self.stake_app.command(
|
673
724
|
"remove", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
|
674
725
|
)(self.stake_remove)
|
726
|
+
self.stake_app.command(
|
727
|
+
"list", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
|
728
|
+
)(self.stake_list)
|
729
|
+
self.stake_app.command(
|
730
|
+
"move", rich_help_panel=HELP_PANELS["STAKE"]["MOVEMENT"]
|
731
|
+
)(self.stake_move)
|
732
|
+
self.stake_app.command(
|
733
|
+
"transfer", rich_help_panel=HELP_PANELS["STAKE"]["MOVEMENT"]
|
734
|
+
)(self.stake_transfer)
|
735
|
+
self.stake_app.command(
|
736
|
+
"swap", rich_help_panel=HELP_PANELS["STAKE"]["MOVEMENT"]
|
737
|
+
)(self.stake_swap)
|
675
738
|
|
676
739
|
# stake-children commands
|
677
740
|
children_app = typer.Typer()
|
@@ -697,6 +760,21 @@ class CLIManager:
|
|
697
760
|
self.sudo_app.command("get", rich_help_panel=HELP_PANELS["SUDO"]["CONFIG"])(
|
698
761
|
self.sudo_get
|
699
762
|
)
|
763
|
+
self.sudo_app.command(
|
764
|
+
"senate", rich_help_panel=HELP_PANELS["SUDO"]["GOVERNANCE"]
|
765
|
+
)(self.sudo_senate)
|
766
|
+
self.sudo_app.command(
|
767
|
+
"proposals", rich_help_panel=HELP_PANELS["SUDO"]["GOVERNANCE"]
|
768
|
+
)(self.sudo_proposals)
|
769
|
+
self.sudo_app.command(
|
770
|
+
"senate-vote", rich_help_panel=HELP_PANELS["SUDO"]["GOVERNANCE"]
|
771
|
+
)(self.sudo_senate_vote)
|
772
|
+
self.sudo_app.command("set-take", rich_help_panel=HELP_PANELS["SUDO"]["TAKE"])(
|
773
|
+
self.sudo_set_take
|
774
|
+
)
|
775
|
+
self.sudo_app.command("get-take", rich_help_panel=HELP_PANELS["SUDO"]["TAKE"])(
|
776
|
+
self.sudo_get_take
|
777
|
+
)
|
700
778
|
|
701
779
|
# subnets commands
|
702
780
|
self.subnets_app.command(
|
@@ -706,8 +784,8 @@ class CLIManager:
|
|
706
784
|
"list", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
|
707
785
|
)(self.subnets_list)
|
708
786
|
self.subnets_app.command(
|
709
|
-
"
|
710
|
-
)(self.
|
787
|
+
"burn-cost", rich_help_panel=HELP_PANELS["SUBNETS"]["CREATION"]
|
788
|
+
)(self.subnets_burn_cost)
|
711
789
|
self.subnets_app.command(
|
712
790
|
"create", rich_help_panel=HELP_PANELS["SUBNETS"]["CREATION"]
|
713
791
|
)(self.subnets_create)
|
@@ -718,8 +796,14 @@ class CLIManager:
|
|
718
796
|
"register", rich_help_panel=HELP_PANELS["SUBNETS"]["REGISTER"]
|
719
797
|
)(self.subnets_register)
|
720
798
|
self.subnets_app.command(
|
721
|
-
"metagraph", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
|
722
|
-
)(self.
|
799
|
+
"metagraph", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"], hidden=True
|
800
|
+
)(self.subnets_show) # Aliased to `s show` for now
|
801
|
+
self.subnets_app.command(
|
802
|
+
"show", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
|
803
|
+
)(self.subnets_show)
|
804
|
+
self.subnets_app.command(
|
805
|
+
"price", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
|
806
|
+
)(self.subnets_price)
|
723
807
|
|
724
808
|
# weights commands
|
725
809
|
self.weights_app.command(
|
@@ -764,22 +848,49 @@ class CLIManager:
|
|
764
848
|
hidden=True,
|
765
849
|
)(self.wallet_get_id)
|
766
850
|
|
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
851
|
# Subnets
|
780
|
-
self.subnets_app.command("
|
852
|
+
self.subnets_app.command("burn_cost", hidden=True)(self.subnets_burn_cost)
|
781
853
|
self.subnets_app.command("pow_register", hidden=True)(self.subnets_pow_register)
|
782
854
|
|
855
|
+
# Sudo
|
856
|
+
self.sudo_app.command("senate_vote", hidden=True)(self.sudo_senate_vote)
|
857
|
+
self.sudo_app.command("get_take", hidden=True)(self.sudo_get_take)
|
858
|
+
self.sudo_app.command("set_take", hidden=True)(self.sudo_set_take)
|
859
|
+
|
860
|
+
def generate_command_tree(self) -> Tree:
|
861
|
+
"""
|
862
|
+
Generates a rich.Tree of the commands, subcommands, and groups of this app
|
863
|
+
"""
|
864
|
+
|
865
|
+
def build_rich_tree(data: dict, parent: Tree):
|
866
|
+
for group, content in data.get("groups", {}).items():
|
867
|
+
group_node = parent.add(
|
868
|
+
f"[bold cyan]{group}[/]"
|
869
|
+
) # Add group to the tree
|
870
|
+
for command in content.get("commands", []):
|
871
|
+
group_node.add(f"[green]{command}[/]") # Add commands to the group
|
872
|
+
build_rich_tree(content, group_node) # Recurse for subgroups
|
873
|
+
|
874
|
+
def traverse_group(group: typer.Typer) -> dict:
|
875
|
+
tree = {}
|
876
|
+
if commands := [
|
877
|
+
cmd.name for cmd in group.registered_commands if not cmd.hidden
|
878
|
+
]:
|
879
|
+
tree["commands"] = commands
|
880
|
+
for group in group.registered_groups:
|
881
|
+
if "groups" not in tree:
|
882
|
+
tree["groups"] = {}
|
883
|
+
if not group.hidden:
|
884
|
+
if group_transversal := traverse_group(group.typer_instance):
|
885
|
+
tree["groups"][group.name] = group_transversal
|
886
|
+
|
887
|
+
return tree
|
888
|
+
|
889
|
+
groups_and_commands = traverse_group(self.app)
|
890
|
+
root = Tree("[bold magenta]BTCLI Commands[/]") # Root node
|
891
|
+
build_rich_tree(groups_and_commands, root)
|
892
|
+
return root
|
893
|
+
|
783
894
|
def initialize_chain(
|
784
895
|
self,
|
785
896
|
network: Optional[list[str]] = None,
|
@@ -810,13 +921,13 @@ class CLIManager:
|
|
810
921
|
elif self.config["network"]:
|
811
922
|
self.subtensor = SubtensorInterface(self.config["network"])
|
812
923
|
console.print(
|
813
|
-
f"Using the specified network [
|
924
|
+
f"Using the specified network [{COLOR_PALETTE['GENERAL']['LINKS']}]{self.config['network']}[/{COLOR_PALETTE['GENERAL']['LINKS']}] from config"
|
814
925
|
)
|
815
926
|
else:
|
816
927
|
self.subtensor = SubtensorInterface(defaults.subtensor.network)
|
817
928
|
return self.subtensor
|
818
929
|
|
819
|
-
def _run_command(self, cmd: Coroutine):
|
930
|
+
def _run_command(self, cmd: Coroutine, exit_early: bool = True):
|
820
931
|
"""
|
821
932
|
Runs the supplied coroutine with `asyncio.run`
|
822
933
|
"""
|
@@ -832,16 +943,19 @@ class CLIManager:
|
|
832
943
|
initiated = True
|
833
944
|
result = await cmd
|
834
945
|
return result
|
835
|
-
except (ConnectionRefusedError, ssl.SSLError):
|
946
|
+
except (ConnectionRefusedError, ssl.SSLError, InvalidHandshake):
|
836
947
|
err_console.print(f"Unable to connect to the chain: {self.subtensor}")
|
837
948
|
verbose_console.print(traceback.format_exc())
|
838
949
|
except (
|
839
950
|
ConnectionClosed,
|
840
951
|
SubstrateRequestException,
|
841
952
|
KeyboardInterrupt,
|
953
|
+
RuntimeError,
|
842
954
|
) as e:
|
843
955
|
if isinstance(e, SubstrateRequestException):
|
844
956
|
err_console.print(str(e))
|
957
|
+
elif isinstance(e, RuntimeError):
|
958
|
+
pass # Temporarily to handle loop bound issues
|
845
959
|
verbose_console.print(traceback.format_exc())
|
846
960
|
except Exception as e:
|
847
961
|
err_console.print(f"An unknown error has occurred: {e}")
|
@@ -849,23 +963,35 @@ class CLIManager:
|
|
849
963
|
finally:
|
850
964
|
if initiated is False:
|
851
965
|
asyncio.create_task(cmd).cancel()
|
852
|
-
|
966
|
+
if (
|
967
|
+
exit_early is True
|
968
|
+
): # temporarily to handle multiple run commands in one session
|
969
|
+
try:
|
970
|
+
raise typer.Exit()
|
971
|
+
except Exception as e: # ensures we always exit cleanly
|
972
|
+
if not isinstance(e, (typer.Exit, RuntimeError)):
|
973
|
+
err_console.print(f"An unknown error has occurred: {e}")
|
853
974
|
|
854
|
-
|
855
|
-
# For Python 3.9 or lower
|
856
|
-
return asyncio.get_event_loop().run_until_complete(_run())
|
857
|
-
else:
|
858
|
-
# For Python 3.10 or higher
|
859
|
-
return asyncio.run(_run())
|
975
|
+
return self.asyncio_runner(_run())
|
860
976
|
|
861
977
|
def main_callback(
|
862
978
|
self,
|
863
979
|
version: Annotated[
|
864
|
-
Optional[bool],
|
980
|
+
Optional[bool],
|
981
|
+
typer.Option(
|
982
|
+
"--version", callback=version_callback, help="Show BTCLI version"
|
983
|
+
),
|
984
|
+
] = None,
|
985
|
+
commands: Annotated[
|
986
|
+
Optional[bool],
|
987
|
+
typer.Option(
|
988
|
+
"--commands", callback=commands_callback, help="Show BTCLI commands"
|
989
|
+
),
|
865
990
|
] = None,
|
866
991
|
):
|
867
992
|
"""
|
868
|
-
Command line interface (CLI) for Bittensor. Uses the values in the configuration file. These values can be
|
993
|
+
Command line interface (CLI) for Bittensor. Uses the values in the configuration file. These values can be
|
994
|
+
overriden by passing them explicitly in the command line.
|
869
995
|
"""
|
870
996
|
# Load or create the config file
|
871
997
|
if os.path.exists(self.config_path):
|
@@ -897,6 +1023,20 @@ class CLIManager:
|
|
897
1023
|
if k in self.config.keys():
|
898
1024
|
self.config[k] = v
|
899
1025
|
|
1026
|
+
if sys.version_info < (3, 10):
|
1027
|
+
# For Python 3.9 or lower
|
1028
|
+
self.asyncio_runner = asyncio.get_event_loop().run_until_complete
|
1029
|
+
else:
|
1030
|
+
try:
|
1031
|
+
uvloop = importlib.import_module("uvloop")
|
1032
|
+
if sys.version_info >= (3, 11):
|
1033
|
+
self.asyncio_runner = uvloop.run
|
1034
|
+
else:
|
1035
|
+
uvloop.install()
|
1036
|
+
self.asyncio_runner = asyncio.run
|
1037
|
+
except ModuleNotFoundError:
|
1038
|
+
self.asyncio_runner = asyncio.run
|
1039
|
+
|
900
1040
|
def verbosity_handler(self, quiet: bool, verbose: bool):
|
901
1041
|
if quiet and verbose:
|
902
1042
|
err_console.print("Cannot specify both `--quiet` and `--verbose`")
|
@@ -953,9 +1093,44 @@ class CLIManager:
|
|
953
1093
|
help="Disable caching of some commands. This will disable the `--reuse-last` and `--html` flags on "
|
954
1094
|
"commands such as `subnets metagraph`, `stake show` and `subnets list`.",
|
955
1095
|
),
|
1096
|
+
rate_tolerance: Optional[float] = typer.Option(
|
1097
|
+
None,
|
1098
|
+
"--slippage",
|
1099
|
+
"--slippage-tolerance",
|
1100
|
+
"--tolerance",
|
1101
|
+
help="Set the rate tolerance percentage for transactions (e.g. 0.1 for 0.1%).",
|
1102
|
+
),
|
1103
|
+
safe_staking: Optional[bool] = typer.Option(
|
1104
|
+
None,
|
1105
|
+
"--safe-staking/--no-safe-staking",
|
1106
|
+
"--safe/--unsafe",
|
1107
|
+
help="Enable or disable safe staking mode.",
|
1108
|
+
),
|
1109
|
+
allow_partial_stake: Optional[bool] = typer.Option(
|
1110
|
+
None,
|
1111
|
+
"--allow-partial-stake/--no-allow-partial-stake",
|
1112
|
+
"--partial/--no-partial",
|
1113
|
+
"--allow/--not-allow",
|
1114
|
+
),
|
956
1115
|
):
|
957
1116
|
"""
|
958
|
-
Sets
|
1117
|
+
Sets or updates configuration values in the BTCLI config file.
|
1118
|
+
|
1119
|
+
This command allows you to set default values that will be used across all BTCLI commands.
|
1120
|
+
|
1121
|
+
USAGE
|
1122
|
+
Interactive mode:
|
1123
|
+
[green]$[/green] btcli config set
|
1124
|
+
|
1125
|
+
Set specific values:
|
1126
|
+
[green]$[/green] btcli config set --wallet-name default --network finney
|
1127
|
+
[green]$[/green] btcli config set --safe-staking --rate-tolerance 0.1
|
1128
|
+
|
1129
|
+
[bold]NOTE[/bold]:
|
1130
|
+
- Network values can be network names (e.g., 'finney', 'test') or websocket URLs
|
1131
|
+
- Rate tolerance is specified as a decimal (e.g., 0.05 for 0.05%)
|
1132
|
+
- Changes are saved to ~/.bittensor/btcli.yaml
|
1133
|
+
- Use '[green]$[/green] btcli config get' to view current settings
|
959
1134
|
"""
|
960
1135
|
args = {
|
961
1136
|
"wallet_name": wallet_name,
|
@@ -963,8 +1138,11 @@ class CLIManager:
|
|
963
1138
|
"wallet_hotkey": wallet_hotkey,
|
964
1139
|
"network": network,
|
965
1140
|
"use_cache": use_cache,
|
1141
|
+
"rate_tolerance": rate_tolerance,
|
1142
|
+
"safe_staking": safe_staking,
|
1143
|
+
"allow_partial_stake": allow_partial_stake,
|
966
1144
|
}
|
967
|
-
bools = ["use_cache"]
|
1145
|
+
bools = ["use_cache", "safe_staking", "allow_partial_stake"]
|
968
1146
|
if all(v is None for v in args.values()):
|
969
1147
|
# Print existing configs
|
970
1148
|
self.get_config()
|
@@ -988,6 +1166,20 @@ class CLIManager:
|
|
988
1166
|
default=True,
|
989
1167
|
)
|
990
1168
|
self.config[arg] = nc
|
1169
|
+
|
1170
|
+
elif arg == "rate_tolerance":
|
1171
|
+
while True:
|
1172
|
+
val = FloatPrompt.ask(
|
1173
|
+
f"What percentage would you like to set for [red]{arg}[/red]?\nValues are percentages (e.g. 0.05 for 5%)",
|
1174
|
+
default=0.05,
|
1175
|
+
)
|
1176
|
+
try:
|
1177
|
+
validated_val = validate_rate_tolerance(val)
|
1178
|
+
self.config[arg] = validated_val
|
1179
|
+
break
|
1180
|
+
except typer.BadParameter as e:
|
1181
|
+
print_error(str(e))
|
1182
|
+
continue
|
991
1183
|
else:
|
992
1184
|
val = Prompt.ask(
|
993
1185
|
f"What value would you like to assign to [red]{arg}[/red]?"
|
@@ -1045,6 +1237,18 @@ class CLIManager:
|
|
1045
1237
|
wallet_hotkey: bool = typer.Option(False, *Options.wallet_hotkey.param_decls),
|
1046
1238
|
network: bool = typer.Option(False, *Options.network.param_decls),
|
1047
1239
|
use_cache: bool = typer.Option(False, "--cache"),
|
1240
|
+
rate_tolerance: bool = typer.Option(
|
1241
|
+
False, "--slippage", "--slippage-tolerance", "--tolerance"
|
1242
|
+
),
|
1243
|
+
safe_staking: bool = typer.Option(
|
1244
|
+
False, "--safe-staking/--no-safe-staking", "--safe/--unsafe"
|
1245
|
+
),
|
1246
|
+
allow_partial_stake: bool = typer.Option(
|
1247
|
+
False,
|
1248
|
+
"--allow-partial-stake/--no-allow-partial-stake",
|
1249
|
+
"--partial/--no-partial",
|
1250
|
+
"--allow/--not-allow",
|
1251
|
+
),
|
1048
1252
|
all_items: bool = typer.Option(False, "--all"),
|
1049
1253
|
):
|
1050
1254
|
"""
|
@@ -1074,6 +1278,9 @@ class CLIManager:
|
|
1074
1278
|
"wallet_hotkey": wallet_hotkey,
|
1075
1279
|
"network": network,
|
1076
1280
|
"use_cache": use_cache,
|
1281
|
+
"rate_tolerance": rate_tolerance,
|
1282
|
+
"safe_staking": safe_staking,
|
1283
|
+
"allow_partial_stake": allow_partial_stake,
|
1077
1284
|
}
|
1078
1285
|
|
1079
1286
|
# If no specific argument is provided, iterate over all
|
@@ -1135,6 +1342,8 @@ class CLIManager:
|
|
1135
1342
|
else:
|
1136
1343
|
if value in Constants.networks:
|
1137
1344
|
value = value + f" ({Constants.network_map[value]})"
|
1345
|
+
if key == "rate_tolerance":
|
1346
|
+
value = f"{value} ({value*100}%)" if value is not None else "None"
|
1138
1347
|
|
1139
1348
|
elif key in deprecated_configs:
|
1140
1349
|
continue
|
@@ -1147,13 +1356,112 @@ class CLIManager:
|
|
1147
1356
|
table.add_row(str(key), str(value), "")
|
1148
1357
|
|
1149
1358
|
console.print(table)
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1359
|
+
|
1360
|
+
def ask_rate_tolerance(
|
1361
|
+
self,
|
1362
|
+
rate_tolerance: Optional[float],
|
1363
|
+
) -> float:
|
1364
|
+
"""
|
1365
|
+
Gets rate tolerance from args, config, or default.
|
1366
|
+
|
1367
|
+
Args:
|
1368
|
+
rate_tolerance (Optional[float]): Explicitly provided slippage value
|
1369
|
+
|
1370
|
+
Returns:
|
1371
|
+
float: rate tolerance value
|
1372
|
+
"""
|
1373
|
+
if rate_tolerance is not None:
|
1374
|
+
console.print(
|
1375
|
+
f"[dim][blue]Rate tolerance[/blue]: [bold cyan]{rate_tolerance} ({rate_tolerance*100}%)[/bold cyan]."
|
1155
1376
|
)
|
1156
|
-
|
1377
|
+
return rate_tolerance
|
1378
|
+
elif self.config.get("rate_tolerance") is not None:
|
1379
|
+
config_slippage = self.config["rate_tolerance"]
|
1380
|
+
console.print(
|
1381
|
+
f"[dim][blue]Rate tolerance[/blue]: [bold cyan]{config_slippage} ({config_slippage*100}%)[/bold cyan] (from config)."
|
1382
|
+
)
|
1383
|
+
return config_slippage
|
1384
|
+
else:
|
1385
|
+
console.print(
|
1386
|
+
"[dim][blue]Rate tolerance[/blue]: "
|
1387
|
+
+ f"[bold cyan]{defaults.rate_tolerance} ({defaults.rate_tolerance*100}%)[/bold cyan] "
|
1388
|
+
+ "by default. Set this using "
|
1389
|
+
+ "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
|
1390
|
+
+ "or "
|
1391
|
+
+ "[dark_sea_green3 italic]`--tolerance`[/dark_sea_green3 italic] flag[/dim]"
|
1392
|
+
)
|
1393
|
+
return defaults.rate_tolerance
|
1394
|
+
|
1395
|
+
def ask_safe_staking(
|
1396
|
+
self,
|
1397
|
+
safe_staking: Optional[bool],
|
1398
|
+
) -> bool:
|
1399
|
+
"""
|
1400
|
+
Gets safe staking setting from args, config, or default.
|
1401
|
+
|
1402
|
+
Args:
|
1403
|
+
safe_staking (Optional[bool]): Explicitly provided safe staking value
|
1404
|
+
|
1405
|
+
Returns:
|
1406
|
+
bool: Safe staking setting
|
1407
|
+
"""
|
1408
|
+
if safe_staking is not None:
|
1409
|
+
console.print(
|
1410
|
+
f"[dim][blue]Safe staking[/blue]: [bold cyan]{'enabled' if safe_staking else 'disabled'}[/bold cyan]."
|
1411
|
+
)
|
1412
|
+
return safe_staking
|
1413
|
+
elif self.config.get("safe_staking") is not None:
|
1414
|
+
safe_staking = self.config["safe_staking"]
|
1415
|
+
console.print(
|
1416
|
+
f"[dim][blue]Safe staking[/blue]: [bold cyan]{'enabled' if safe_staking else 'disabled'}[/bold cyan] (from config)."
|
1417
|
+
)
|
1418
|
+
return safe_staking
|
1419
|
+
else:
|
1420
|
+
safe_staking = True
|
1421
|
+
console.print(
|
1422
|
+
"[dim][blue]Safe staking[/blue]: "
|
1423
|
+
+ f"[bold cyan]{'enabled' if safe_staking else 'disabled'}[/bold cyan] "
|
1424
|
+
+ "by default. Set this using "
|
1425
|
+
+ "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
|
1426
|
+
+ "or "
|
1427
|
+
+ "[dark_sea_green3 italic]`--safe/--unsafe`[/dark_sea_green3 italic] flag[/dim]"
|
1428
|
+
)
|
1429
|
+
return safe_staking
|
1430
|
+
|
1431
|
+
def ask_partial_stake(
|
1432
|
+
self,
|
1433
|
+
allow_partial_stake: Optional[bool],
|
1434
|
+
) -> bool:
|
1435
|
+
"""
|
1436
|
+
Gets partial stake setting from args, config, or default.
|
1437
|
+
|
1438
|
+
Args:
|
1439
|
+
allow_partial_stake (Optional[bool]): Explicitly provided partial stake value
|
1440
|
+
|
1441
|
+
Returns:
|
1442
|
+
bool: Partial stake setting
|
1443
|
+
"""
|
1444
|
+
if allow_partial_stake is not None:
|
1445
|
+
console.print(
|
1446
|
+
f"[dim][blue]Partial staking[/blue]: [bold cyan]{'enabled' if allow_partial_stake else 'disabled'}[/bold cyan]."
|
1447
|
+
)
|
1448
|
+
return allow_partial_stake
|
1449
|
+
elif self.config.get("allow_partial_stake") is not None:
|
1450
|
+
config_partial = self.config["allow_partial_stake"]
|
1451
|
+
console.print(
|
1452
|
+
f"[dim][blue]Partial staking[/blue]: [bold cyan]{'enabled' if config_partial else 'disabled'}[/bold cyan] (from config)."
|
1453
|
+
)
|
1454
|
+
return config_partial
|
1455
|
+
else:
|
1456
|
+
console.print(
|
1457
|
+
"[dim][blue]Partial staking[/blue]: "
|
1458
|
+
+ f"[bold cyan]{'enabled' if allow_partial_stake else 'disabled'}[/bold cyan] "
|
1459
|
+
+ "by default. Set this using "
|
1460
|
+
+ "[dark_sea_green3 italic]`btcli config set`[/dark_sea_green3 italic] "
|
1461
|
+
+ "or "
|
1462
|
+
+ "[dark_sea_green3 italic]`--partial/--no-partial`[/dark_sea_green3 italic] flag[/dim]"
|
1463
|
+
)
|
1464
|
+
return False
|
1157
1465
|
|
1158
1466
|
def wallet_ask(
|
1159
1467
|
self,
|
@@ -1173,39 +1481,16 @@ class CLIManager:
|
|
1173
1481
|
:return: created Wallet object
|
1174
1482
|
"""
|
1175
1483
|
# 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
1484
|
if WO.NAME in ask_for and not wallet_name:
|
1200
1485
|
if self.config.get("wallet_name"):
|
1201
1486
|
wallet_name = self.config.get("wallet_name")
|
1202
1487
|
console.print(
|
1203
|
-
f"Using the wallet name from config:[bold cyan] {wallet_name}"
|
1488
|
+
f"Using the [blue]wallet name[/blue] from config:[bold cyan] {wallet_name}"
|
1204
1489
|
)
|
1205
1490
|
else:
|
1206
1491
|
wallet_name = Prompt.ask(
|
1207
1492
|
"Enter the [blue]wallet name[/blue]"
|
1208
|
-
+ " [
|
1493
|
+
+ f" [{COLOR_PALETTE['GENERAL']['HINT']} italic](Hint: You can set this with `btcli config set --wallet-name`)",
|
1209
1494
|
default=defaults.wallet.name,
|
1210
1495
|
)
|
1211
1496
|
|
@@ -1213,7 +1498,7 @@ class CLIManager:
|
|
1213
1498
|
if self.config.get("wallet_hotkey"):
|
1214
1499
|
wallet_hotkey = self.config.get("wallet_hotkey")
|
1215
1500
|
console.print(
|
1216
|
-
f"Using the wallet hotkey from config:[bold cyan] {wallet_hotkey}"
|
1501
|
+
f"Using the [blue]wallet hotkey[/blue] from config:[bold cyan] {wallet_hotkey}"
|
1217
1502
|
)
|
1218
1503
|
else:
|
1219
1504
|
wallet_hotkey = Prompt.ask(
|
@@ -1221,8 +1506,27 @@ class CLIManager:
|
|
1221
1506
|
+ " [dark_sea_green3 italic](Hint: You can set this with `btcli config set --wallet-hotkey`)[/dark_sea_green3 italic]",
|
1222
1507
|
default=defaults.wallet.hotkey,
|
1223
1508
|
)
|
1509
|
+
if wallet_path:
|
1510
|
+
if wallet_path == "default":
|
1511
|
+
wallet_path = defaults.wallet.path
|
1512
|
+
|
1513
|
+
elif self.config.get("wallet_path"):
|
1514
|
+
wallet_path = self.config.get("wallet_path")
|
1515
|
+
console.print(
|
1516
|
+
f"Using the [blue]wallet path[/blue] from config:[bold magenta] {wallet_path}"
|
1517
|
+
)
|
1518
|
+
else:
|
1519
|
+
wallet_path = defaults.wallet.path
|
1224
1520
|
|
1521
|
+
if WO.PATH in ask_for and not wallet_path:
|
1522
|
+
wallet_path = Prompt.ask(
|
1523
|
+
"Enter the [blue]wallet path[/blue]"
|
1524
|
+
+ " [dark_sea_green3 italic](Hint: You can set this with `btcli config set --wallet-path`)[/dark_sea_green3 italic]",
|
1525
|
+
default=defaults.wallet.path,
|
1526
|
+
)
|
1225
1527
|
# Create the Wallet object
|
1528
|
+
if wallet_path:
|
1529
|
+
wallet_path = os.path.expanduser(wallet_path)
|
1226
1530
|
wallet = Wallet(name=wallet_name, path=wallet_path, hotkey=wallet_hotkey)
|
1227
1531
|
|
1228
1532
|
# Validate the wallet if required
|
@@ -1252,7 +1556,7 @@ class CLIManager:
|
|
1252
1556
|
"""
|
1253
1557
|
Displays all the wallets and their corresponding hotkeys that are located in the wallet path specified in the config.
|
1254
1558
|
|
1255
|
-
The output display shows each wallet and its associated `ss58` addresses for the coldkey public key and any hotkeys. The output is presented in a hierarchical tree format, with each wallet as a root node and any associated hotkeys as child nodes. The `ss58` address is displayed for each coldkey and hotkey that
|
1559
|
+
The output display shows each wallet and its associated `ss58` addresses for the coldkey public key and any hotkeys. The output is presented in a hierarchical tree format, with each wallet as a root node and any associated hotkeys as child nodes. The `ss58` address (or an `<ENCRYPTED>` marker, for encrypted hotkeys) is displayed for each coldkey and hotkey that exists on the device.
|
1256
1560
|
|
1257
1561
|
Upon invocation, the command scans the wallet directory and prints a list of all the wallets, indicating whether the
|
1258
1562
|
public keys are available (`?` denotes unavailable or encrypted keys).
|
@@ -1315,50 +1619,9 @@ class CLIManager:
|
|
1315
1619
|
|
1316
1620
|
USAGE
|
1317
1621
|
|
1318
|
-
The command offers various options to customize the output. Users can filter the displayed data by specific
|
1319
|
-
netuid, sort by different criteria, and choose to include all the wallets in the user's wallet path location.
|
1320
|
-
The output is presented in a tabular format with the following columns:
|
1321
|
-
|
1322
|
-
- COLDKEY: The SS58 address of the coldkey.
|
1323
|
-
|
1324
|
-
- HOTKEY: The SS58 address of the hotkey.
|
1325
|
-
|
1326
|
-
- UID: Unique identifier of the neuron.
|
1327
|
-
|
1328
|
-
- ACTIVE: Indicates if the neuron is active.
|
1329
|
-
|
1330
|
-
- STAKE(τ): Amount of stake in the neuron, in TAO.
|
1331
|
-
|
1332
|
-
- RANK: The rank of the neuron within the network.
|
1333
|
-
|
1334
|
-
- TRUST: Trust score of the neuron.
|
1335
|
-
|
1336
|
-
- CONSENSUS: Consensus score of the neuron.
|
1337
|
-
|
1338
|
-
- INCENTIVE: Incentive score of the neuron.
|
1339
|
-
|
1340
|
-
- DIVIDENDS: Dividends earned by the neuron.
|
1341
|
-
|
1342
|
-
- EMISSION(p): Emission received by the neuron, expressed in rho.
|
1343
|
-
|
1344
|
-
- VTRUST: Validator trust score of the neuron.
|
1345
|
-
|
1346
|
-
- VPERMIT: Indicates if the neuron has a validator permit.
|
1347
|
-
|
1348
|
-
- UPDATED: Time since last update.
|
1349
|
-
|
1350
|
-
- AXON: IP address and port of the neuron.
|
1351
|
-
|
1352
|
-
- HOTKEY_SS58: Human-readable representation of the hotkey.
|
1353
|
-
|
1354
|
-
|
1355
|
-
# EXAMPLE:
|
1356
|
-
|
1357
1622
|
[green]$[/green] btcli wallet overview
|
1358
1623
|
|
1359
|
-
[green]$[/green] btcli wallet overview --all
|
1360
|
-
|
1361
|
-
[green]$[/green] btcli wallet overview -in hk1,hk2 --sort-by stake
|
1624
|
+
[green]$[/green] btcli wallet overview --all
|
1362
1625
|
|
1363
1626
|
[bold]NOTE[/bold]: This command is read-only and does not modify the blockchain state or account configuration.
|
1364
1627
|
It provides a quick and comprehensive view of the user's network presence, making it useful for monitoring account status,
|
@@ -1408,6 +1671,7 @@ class CLIManager:
|
|
1408
1671
|
include_hotkeys,
|
1409
1672
|
exclude_hotkeys,
|
1410
1673
|
netuids_filter=netuids,
|
1674
|
+
verbose=verbose,
|
1411
1675
|
)
|
1412
1676
|
)
|
1413
1677
|
|
@@ -1425,7 +1689,6 @@ class CLIManager:
|
|
1425
1689
|
None,
|
1426
1690
|
"--amount",
|
1427
1691
|
"-a",
|
1428
|
-
prompt=False,
|
1429
1692
|
help="Amount (in TAO) to transfer.",
|
1430
1693
|
),
|
1431
1694
|
transfer_all: bool = typer.Option(
|
@@ -1523,7 +1786,7 @@ class CLIManager:
|
|
1523
1786
|
wallet_name,
|
1524
1787
|
wallet_path,
|
1525
1788
|
wallet_hotkey,
|
1526
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
1789
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
1527
1790
|
validate=WV.WALLET_AND_HOTKEY,
|
1528
1791
|
)
|
1529
1792
|
if not destination_hotkey_name:
|
@@ -1535,7 +1798,7 @@ class CLIManager:
|
|
1535
1798
|
wallet_name,
|
1536
1799
|
wallet_path,
|
1537
1800
|
destination_hotkey_name,
|
1538
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
1801
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
1539
1802
|
validate=WV.WALLET_AND_HOTKEY,
|
1540
1803
|
)
|
1541
1804
|
self.initialize_chain(network)
|
@@ -1591,6 +1854,8 @@ class CLIManager:
|
|
1591
1854
|
|
1592
1855
|
[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
1856
|
"""
|
1857
|
+
print_error("This command is disabled on the 'rao' network.")
|
1858
|
+
raise typer.Exit()
|
1594
1859
|
self.verbosity_handler(quiet, verbose)
|
1595
1860
|
|
1596
1861
|
if netuids:
|
@@ -1601,11 +1866,12 @@ class CLIManager:
|
|
1601
1866
|
)
|
1602
1867
|
|
1603
1868
|
# if all-wallets is entered, ask for path
|
1604
|
-
ask_for = [WO.NAME] if not all_wallets else []
|
1869
|
+
ask_for = [WO.NAME, WO.PATH] if not all_wallets else [WO.PATH]
|
1605
1870
|
validate = WV.WALLET if not all_wallets else WV.NONE
|
1606
1871
|
wallet = self.wallet_ask(
|
1607
1872
|
wallet_name, wallet_path, wallet_hotkey, ask_for=ask_for, validate=validate
|
1608
1873
|
)
|
1874
|
+
|
1609
1875
|
self.initialize_chain(network)
|
1610
1876
|
return self._run_command(
|
1611
1877
|
wallets.inspect(
|
@@ -1691,7 +1957,7 @@ class CLIManager:
|
|
1691
1957
|
wallet_name,
|
1692
1958
|
wallet_path,
|
1693
1959
|
wallet_hotkey,
|
1694
|
-
ask_for=[WO.NAME],
|
1960
|
+
ask_for=[WO.NAME, WO.PATH],
|
1695
1961
|
validate=WV.WALLET,
|
1696
1962
|
)
|
1697
1963
|
return self._run_command(
|
@@ -1750,7 +2016,8 @@ class CLIManager:
|
|
1750
2016
|
|
1751
2017
|
if not wallet_name:
|
1752
2018
|
wallet_name = Prompt.ask(
|
1753
|
-
"Enter the name of the new wallet",
|
2019
|
+
f"Enter the name of the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]new wallet (coldkey)",
|
2020
|
+
default=defaults.wallet.name,
|
1754
2021
|
)
|
1755
2022
|
|
1756
2023
|
wallet = Wallet(wallet_name, wallet_hotkey, wallet_path)
|
@@ -1806,7 +2073,8 @@ class CLIManager:
|
|
1806
2073
|
|
1807
2074
|
if not wallet_name:
|
1808
2075
|
wallet_name = Prompt.ask(
|
1809
|
-
"Enter the name of the new wallet",
|
2076
|
+
f"Enter the name of the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]new wallet (coldkey)",
|
2077
|
+
default=defaults.wallet.name,
|
1810
2078
|
)
|
1811
2079
|
wallet = Wallet(wallet_name, wallet_hotkey, wallet_path)
|
1812
2080
|
|
@@ -1898,6 +2166,7 @@ class CLIManager:
|
|
1898
2166
|
False, # Overriden to False
|
1899
2167
|
help="Set to 'True' to protect the generated Bittensor key with a password.",
|
1900
2168
|
),
|
2169
|
+
uri: Optional[str] = Options.uri,
|
1901
2170
|
overwrite: bool = Options.overwrite,
|
1902
2171
|
quiet: bool = Options.quiet,
|
1903
2172
|
verbose: bool = Options.verbose,
|
@@ -1920,12 +2189,14 @@ class CLIManager:
|
|
1920
2189
|
|
1921
2190
|
if not wallet_name:
|
1922
2191
|
wallet_name = Prompt.ask(
|
1923
|
-
"Enter the wallet name",
|
2192
|
+
f"Enter the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]wallet name",
|
2193
|
+
default=defaults.wallet.name,
|
1924
2194
|
)
|
1925
2195
|
|
1926
2196
|
if not wallet_hotkey:
|
1927
2197
|
wallet_hotkey = Prompt.ask(
|
1928
|
-
"Enter the name of the new hotkey",
|
2198
|
+
f"Enter the name of the [{COLOR_PALETTE['GENERAL']['HOTKEY']}]new hotkey",
|
2199
|
+
default=defaults.wallet.hotkey,
|
1929
2200
|
)
|
1930
2201
|
|
1931
2202
|
wallet = self.wallet_ask(
|
@@ -1935,9 +2206,10 @@ class CLIManager:
|
|
1935
2206
|
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
1936
2207
|
validate=WV.WALLET,
|
1937
2208
|
)
|
1938
|
-
|
2209
|
+
if not uri:
|
2210
|
+
n_words = get_n_words(n_words)
|
1939
2211
|
return self._run_command(
|
1940
|
-
wallets.new_hotkey(wallet, n_words, use_password, overwrite)
|
2212
|
+
wallets.new_hotkey(wallet, n_words, use_password, uri, overwrite)
|
1941
2213
|
)
|
1942
2214
|
|
1943
2215
|
def wallet_new_coldkey(
|
@@ -1952,6 +2224,7 @@ class CLIManager:
|
|
1952
2224
|
help="The number of words used in the mnemonic. Options: [12, 15, 18, 21, 24]",
|
1953
2225
|
),
|
1954
2226
|
use_password: Optional[bool] = Options.use_password,
|
2227
|
+
uri: Optional[str] = Options.uri,
|
1955
2228
|
overwrite: bool = Options.overwrite,
|
1956
2229
|
quiet: bool = Options.quiet,
|
1957
2230
|
verbose: bool = Options.verbose,
|
@@ -1978,7 +2251,8 @@ class CLIManager:
|
|
1978
2251
|
|
1979
2252
|
if not wallet_name:
|
1980
2253
|
wallet_name = Prompt.ask(
|
1981
|
-
"Enter the name of the new wallet",
|
2254
|
+
f"Enter the name of the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]new wallet (coldkey)",
|
2255
|
+
default=defaults.wallet.name,
|
1982
2256
|
)
|
1983
2257
|
|
1984
2258
|
wallet = self.wallet_ask(
|
@@ -1988,9 +2262,10 @@ class CLIManager:
|
|
1988
2262
|
ask_for=[WO.NAME, WO.PATH],
|
1989
2263
|
validate=WV.NONE,
|
1990
2264
|
)
|
1991
|
-
|
2265
|
+
if not uri:
|
2266
|
+
n_words = get_n_words(n_words)
|
1992
2267
|
return self._run_command(
|
1993
|
-
wallets.new_coldkey(wallet, n_words, use_password, overwrite)
|
2268
|
+
wallets.new_coldkey(wallet, n_words, use_password, uri, overwrite)
|
1994
2269
|
)
|
1995
2270
|
|
1996
2271
|
def wallet_check_ck_swap(
|
@@ -2025,6 +2300,7 @@ class CLIManager:
|
|
2025
2300
|
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
2026
2301
|
n_words: Optional[int] = None,
|
2027
2302
|
use_password: bool = Options.use_password,
|
2303
|
+
uri: Optional[str] = Options.uri,
|
2028
2304
|
overwrite: bool = Options.overwrite,
|
2029
2305
|
quiet: bool = Options.quiet,
|
2030
2306
|
verbose: bool = Options.verbose,
|
@@ -2049,12 +2325,13 @@ class CLIManager:
|
|
2049
2325
|
|
2050
2326
|
if not wallet_name:
|
2051
2327
|
wallet_name = Prompt.ask(
|
2052
|
-
"Enter the name of the new wallet (coldkey)",
|
2328
|
+
f"Enter the name of the [{COLOR_PALETTE['GENERAL']['COLDKEY']}]new wallet (coldkey)",
|
2053
2329
|
default=defaults.wallet.name,
|
2054
2330
|
)
|
2055
2331
|
if not wallet_hotkey:
|
2056
2332
|
wallet_hotkey = Prompt.ask(
|
2057
|
-
"Enter the the name of the new hotkey",
|
2333
|
+
f"Enter the the name of the [{COLOR_PALETTE['GENERAL']['HOTKEY']}]new hotkey",
|
2334
|
+
default=defaults.wallet.hotkey,
|
2058
2335
|
)
|
2059
2336
|
|
2060
2337
|
self.verbosity_handler(quiet, verbose)
|
@@ -2065,14 +2342,10 @@ class CLIManager:
|
|
2065
2342
|
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
2066
2343
|
validate=WV.NONE,
|
2067
2344
|
)
|
2068
|
-
|
2345
|
+
if not uri:
|
2346
|
+
n_words = get_n_words(n_words)
|
2069
2347
|
return self._run_command(
|
2070
|
-
wallets.wallet_create(
|
2071
|
-
wallet,
|
2072
|
-
n_words,
|
2073
|
-
use_password,
|
2074
|
-
overwrite,
|
2075
|
-
)
|
2348
|
+
wallets.wallet_create(wallet, n_words, use_password, uri, overwrite)
|
2076
2349
|
)
|
2077
2350
|
|
2078
2351
|
def wallet_balance(
|
@@ -2116,8 +2389,18 @@ class CLIManager:
|
|
2116
2389
|
|
2117
2390
|
"""
|
2118
2391
|
self.verbosity_handler(quiet, verbose)
|
2119
|
-
|
2120
|
-
if
|
2392
|
+
wallet = None
|
2393
|
+
if all_balances:
|
2394
|
+
ask_for = [WO.PATH]
|
2395
|
+
validate = WV.NONE
|
2396
|
+
wallet = self.wallet_ask(
|
2397
|
+
wallet_name,
|
2398
|
+
wallet_path,
|
2399
|
+
wallet_hotkey,
|
2400
|
+
ask_for=ask_for,
|
2401
|
+
validate=validate,
|
2402
|
+
)
|
2403
|
+
elif ss58_addresses:
|
2121
2404
|
valid_ss58s = [
|
2122
2405
|
ss58 for ss58 in set(ss58_addresses) if is_valid_ss58_address(ss58)
|
2123
2406
|
]
|
@@ -2127,20 +2410,45 @@ class CLIManager:
|
|
2127
2410
|
print_error(f"Incorrect ss58 address: {invalid_ss58}. Skipping.")
|
2128
2411
|
|
2129
2412
|
if valid_ss58s:
|
2130
|
-
wallet = None
|
2131
2413
|
ss58_addresses = valid_ss58s
|
2132
2414
|
else:
|
2133
2415
|
raise typer.Exit()
|
2134
2416
|
else:
|
2135
|
-
|
2136
|
-
|
2137
|
-
|
2138
|
-
|
2139
|
-
|
2140
|
-
|
2141
|
-
|
2142
|
-
|
2143
|
-
|
2417
|
+
if wallet_name:
|
2418
|
+
coldkey_or_ss58 = wallet_name
|
2419
|
+
else:
|
2420
|
+
coldkey_or_ss58 = Prompt.ask(
|
2421
|
+
"Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 addresses[/blue] (comma-separated)",
|
2422
|
+
default=self.config.get("wallet_name") or defaults.wallet.name,
|
2423
|
+
)
|
2424
|
+
|
2425
|
+
# Split and validate ss58 addresses
|
2426
|
+
coldkey_or_ss58_list = [x.strip() for x in coldkey_or_ss58.split(",")]
|
2427
|
+
if any(is_valid_ss58_address(x) for x in coldkey_or_ss58_list):
|
2428
|
+
valid_ss58s = [
|
2429
|
+
ss58 for ss58 in coldkey_or_ss58_list if is_valid_ss58_address(ss58)
|
2430
|
+
]
|
2431
|
+
invalid_ss58s = set(coldkey_or_ss58_list) - set(valid_ss58s)
|
2432
|
+
for invalid_ss58 in invalid_ss58s:
|
2433
|
+
print_error(f"Incorrect ss58 address: {invalid_ss58}. Skipping.")
|
2434
|
+
|
2435
|
+
if valid_ss58s:
|
2436
|
+
ss58_addresses = valid_ss58s
|
2437
|
+
else:
|
2438
|
+
raise typer.Exit()
|
2439
|
+
else:
|
2440
|
+
wallet_name = (
|
2441
|
+
coldkey_or_ss58_list[0] if coldkey_or_ss58_list else wallet_name
|
2442
|
+
)
|
2443
|
+
ask_for = [WO.NAME, WO.PATH]
|
2444
|
+
validate = WV.WALLET
|
2445
|
+
wallet = self.wallet_ask(
|
2446
|
+
wallet_name,
|
2447
|
+
wallet_path,
|
2448
|
+
wallet_hotkey,
|
2449
|
+
ask_for=ask_for,
|
2450
|
+
validate=validate,
|
2451
|
+
)
|
2144
2452
|
subtensor = self.initialize_chain(network)
|
2145
2453
|
return self._run_command(
|
2146
2454
|
wallets.wallet_balance(wallet, subtensor, all_balances, ss58_addresses)
|
@@ -2166,19 +2474,23 @@ class CLIManager:
|
|
2166
2474
|
[green]$[/green] btcli wallet history
|
2167
2475
|
|
2168
2476
|
"""
|
2477
|
+
# TODO: Fetch effective network and redirect users accordingly - this only works on finney
|
2478
|
+
# no_use_config_str = "Using the network [dark_orange]finney[/dark_orange] and ignoring network/chain configs"
|
2169
2479
|
|
2170
|
-
|
2480
|
+
# if self.config.get("network"):
|
2481
|
+
# if self.config.get("network") != "finney":
|
2482
|
+
# console.print(no_use_config_str)
|
2171
2483
|
|
2172
|
-
|
2173
|
-
|
2174
|
-
|
2484
|
+
# For Rao games
|
2485
|
+
print_error("This command is disabled on the 'rao' network.")
|
2486
|
+
raise typer.Exit()
|
2175
2487
|
|
2176
2488
|
self.verbosity_handler(quiet, verbose)
|
2177
2489
|
wallet = self.wallet_ask(
|
2178
2490
|
wallet_name,
|
2179
2491
|
wallet_path,
|
2180
2492
|
wallet_hotkey,
|
2181
|
-
ask_for=[WO.NAME],
|
2493
|
+
ask_for=[WO.NAME, WO.PATH],
|
2182
2494
|
validate=WV.WALLET,
|
2183
2495
|
)
|
2184
2496
|
return self._run_command(wallets.wallet_history(wallet))
|
@@ -2189,69 +2501,42 @@ class CLIManager:
|
|
2189
2501
|
wallet_path: Optional[str] = Options.wallet_path,
|
2190
2502
|
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
2191
2503
|
network: Optional[list[str]] = Options.network,
|
2192
|
-
|
2504
|
+
name: str = typer.Option(
|
2193
2505
|
"",
|
2194
|
-
"--
|
2195
|
-
"--display",
|
2506
|
+
"--name",
|
2196
2507
|
help="The display name for the identity.",
|
2197
2508
|
),
|
2198
|
-
legal_name: str = typer.Option(
|
2199
|
-
"",
|
2200
|
-
"--legal-name",
|
2201
|
-
"--legal",
|
2202
|
-
help="The legal name for the identity.",
|
2203
|
-
),
|
2204
2509
|
web_url: str = typer.Option(
|
2205
2510
|
"",
|
2206
2511
|
"--web-url",
|
2207
2512
|
"--web",
|
2208
2513
|
help="The web URL for the identity.",
|
2209
2514
|
),
|
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
2515
|
image_url: str = typer.Option(
|
2227
2516
|
"",
|
2228
2517
|
"--image-url",
|
2229
2518
|
"--image",
|
2230
2519
|
help="The image URL for the identity.",
|
2231
2520
|
),
|
2232
|
-
|
2521
|
+
discord: str = typer.Option(
|
2233
2522
|
"",
|
2234
|
-
"--
|
2235
|
-
"
|
2236
|
-
help="The info for the identity.",
|
2523
|
+
"--discord",
|
2524
|
+
help="The Discord handle for the identity.",
|
2237
2525
|
),
|
2238
|
-
|
2526
|
+
description: str = typer.Option(
|
2239
2527
|
"",
|
2240
|
-
"
|
2241
|
-
"
|
2242
|
-
"--twitter-url",
|
2243
|
-
"--twitter",
|
2244
|
-
help="The 𝕏 (Twitter) URL for the identity.",
|
2528
|
+
"--description",
|
2529
|
+
help="The description for the identity.",
|
2245
2530
|
),
|
2246
|
-
|
2247
|
-
|
2248
|
-
"--
|
2249
|
-
help="
|
2531
|
+
additional: str = typer.Option(
|
2532
|
+
"",
|
2533
|
+
"--additional",
|
2534
|
+
help="Additional details for the identity.",
|
2250
2535
|
),
|
2251
|
-
|
2252
|
-
|
2253
|
-
"--
|
2254
|
-
help="
|
2536
|
+
github_repo: str = typer.Option(
|
2537
|
+
"",
|
2538
|
+
"--github",
|
2539
|
+
help="The GitHub repository for the identity.",
|
2255
2540
|
),
|
2256
2541
|
quiet: bool = Options.quiet,
|
2257
2542
|
verbose: bool = Options.verbose,
|
@@ -2279,94 +2564,67 @@ class CLIManager:
|
|
2279
2564
|
wallet_name,
|
2280
2565
|
wallet_path,
|
2281
2566
|
wallet_hotkey,
|
2282
|
-
ask_for=[WO.
|
2283
|
-
validate=WV.
|
2567
|
+
ask_for=[WO.NAME],
|
2568
|
+
validate=WV.WALLET,
|
2284
2569
|
)
|
2285
2570
|
|
2286
|
-
|
2287
|
-
|
2288
|
-
|
2289
|
-
|
2290
|
-
|
2291
|
-
|
2292
|
-
|
2293
|
-
|
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
|
-
)
|
2571
|
+
current_identity = self._run_command(
|
2572
|
+
wallets.get_id(
|
2573
|
+
self.initialize_chain(network),
|
2574
|
+
wallet.coldkeypub.ss58_address,
|
2575
|
+
"Current on-chain identity",
|
2576
|
+
),
|
2577
|
+
exit_early=False,
|
2578
|
+
)
|
2307
2579
|
|
2308
|
-
|
2309
|
-
|
2310
|
-
|
2311
|
-
|
2312
|
-
|
2313
|
-
|
2314
|
-
|
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
|
-
)
|
2580
|
+
if prompt:
|
2581
|
+
if not Confirm.ask(
|
2582
|
+
"Cost to register an [blue]Identity[/blue] is [blue]0.1 TAO[/blue],"
|
2583
|
+
" are you sure you wish to continue?"
|
2584
|
+
):
|
2585
|
+
console.print(":cross_mark: Aborted!")
|
2586
|
+
raise typer.Exit()
|
2338
2587
|
|
2339
|
-
|
2340
|
-
|
2588
|
+
identity = prompt_for_identity(
|
2589
|
+
current_identity,
|
2590
|
+
name,
|
2591
|
+
web_url,
|
2592
|
+
image_url,
|
2593
|
+
discord,
|
2594
|
+
description,
|
2595
|
+
additional,
|
2596
|
+
github_repo,
|
2597
|
+
)
|
2341
2598
|
|
2342
2599
|
return self._run_command(
|
2343
2600
|
wallets.set_id(
|
2344
2601
|
wallet,
|
2345
2602
|
self.initialize_chain(network),
|
2346
|
-
|
2347
|
-
|
2348
|
-
|
2349
|
-
|
2350
|
-
|
2351
|
-
|
2352
|
-
|
2353
|
-
twitter_url,
|
2354
|
-
info_,
|
2355
|
-
validator_id,
|
2356
|
-
subnet_netuid,
|
2603
|
+
identity["name"],
|
2604
|
+
identity["url"],
|
2605
|
+
identity["image"],
|
2606
|
+
identity["discord"],
|
2607
|
+
identity["description"],
|
2608
|
+
identity["additional"],
|
2609
|
+
identity["github_repo"],
|
2357
2610
|
prompt,
|
2358
2611
|
)
|
2359
2612
|
)
|
2360
2613
|
|
2361
2614
|
def wallet_get_id(
|
2362
2615
|
self,
|
2363
|
-
|
2364
|
-
|
2616
|
+
wallet_name: Optional[str] = Options.wallet_name,
|
2617
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
2618
|
+
wallet_path: Optional[str] = Options.wallet_path,
|
2619
|
+
coldkey_ss58=typer.Option(
|
2620
|
+
None,
|
2621
|
+
"--ss58",
|
2622
|
+
"--coldkey_ss58",
|
2623
|
+
"--coldkey.ss58_address",
|
2624
|
+
"--coldkey.ss58",
|
2365
2625
|
"--key",
|
2366
2626
|
"-k",
|
2367
|
-
"
|
2368
|
-
help="The coldkey or hotkey ss58 address to query.",
|
2369
|
-
prompt=True,
|
2627
|
+
help="Coldkey address of the wallet",
|
2370
2628
|
),
|
2371
2629
|
network: Optional[list[str]] = Options.network,
|
2372
2630
|
quiet: bool = Options.quiet,
|
@@ -2389,13 +2647,28 @@ class CLIManager:
|
|
2389
2647
|
|
2390
2648
|
[bold]Note[/bold]: This command is primarily used for informational purposes and has no side effects on the blockchain network state.
|
2391
2649
|
"""
|
2392
|
-
|
2393
|
-
|
2394
|
-
|
2650
|
+
wallet = None
|
2651
|
+
if coldkey_ss58:
|
2652
|
+
if not is_valid_ss58_address(coldkey_ss58):
|
2653
|
+
print_error("You entered an invalid ss58 address")
|
2654
|
+
raise typer.Exit()
|
2655
|
+
else:
|
2656
|
+
coldkey_or_ss58 = Prompt.ask(
|
2657
|
+
"Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 address[/blue]",
|
2658
|
+
default=self.config.get("wallet_name") or defaults.wallet.name,
|
2659
|
+
)
|
2660
|
+
if is_valid_ss58_address(coldkey_or_ss58):
|
2661
|
+
coldkey_ss58 = coldkey_or_ss58
|
2662
|
+
else:
|
2663
|
+
wallet_name = coldkey_or_ss58 if coldkey_or_ss58 else wallet_name
|
2664
|
+
wallet = self.wallet_ask(
|
2665
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
2666
|
+
)
|
2667
|
+
coldkey_ss58 = wallet.coldkeypub.ss58_address
|
2395
2668
|
|
2396
2669
|
self.verbosity_handler(quiet, verbose)
|
2397
2670
|
return self._run_command(
|
2398
|
-
wallets.get_id(self.initialize_chain(network),
|
2671
|
+
wallets.get_id(self.initialize_chain(network), coldkey_ss58)
|
2399
2672
|
)
|
2400
2673
|
|
2401
2674
|
def wallet_sign(
|
@@ -2429,934 +2702,493 @@ class CLIManager:
|
|
2429
2702
|
self.verbosity_handler(quiet, verbose)
|
2430
2703
|
if use_hotkey is None:
|
2431
2704
|
use_hotkey = Confirm.ask(
|
2432
|
-
"Would you like to sign the transaction using your [
|
2433
|
-
"\n[Type [
|
2434
|
-
"(default is [
|
2705
|
+
f"Would you like to sign the transaction using your [{COLOR_PALETTE['GENERAL']['HOTKEY']}]hotkey[/{COLOR_PALETTE['GENERAL']['HOTKEY']}]?"
|
2706
|
+
f"\n[Type [{COLOR_PALETTE['GENERAL']['HOTKEY']}]y[/{COLOR_PALETTE['GENERAL']['HOTKEY']}] for [{COLOR_PALETTE['GENERAL']['HOTKEY']}]hotkey[/{COLOR_PALETTE['GENERAL']['HOTKEY']}]"
|
2707
|
+
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
2708
|
default=False,
|
2436
2709
|
)
|
2437
2710
|
|
2438
|
-
ask_for = [WO.HOTKEY, WO.NAME] if use_hotkey else [WO.NAME]
|
2711
|
+
ask_for = [WO.HOTKEY, WO.PATH, WO.NAME] if use_hotkey else [WO.NAME, WO.PATH]
|
2439
2712
|
validate = WV.WALLET_AND_HOTKEY if use_hotkey else WV.WALLET
|
2440
2713
|
|
2441
2714
|
wallet = self.wallet_ask(
|
2442
2715
|
wallet_name, wallet_path, wallet_hotkey, ask_for=ask_for, validate=validate
|
2443
2716
|
)
|
2444
2717
|
if not message:
|
2445
|
-
message =
|
2718
|
+
message = Prompt.ask("Enter the [blue]message[/blue] to encode and sign")
|
2446
2719
|
|
2447
2720
|
return self._run_command(wallets.sign(wallet, message, use_hotkey))
|
2448
2721
|
|
2449
|
-
def
|
2722
|
+
def stake_list(
|
2450
2723
|
self,
|
2451
2724
|
network: Optional[list[str]] = Options.network,
|
2725
|
+
wallet_name: Optional[str] = Options.wallet_name,
|
2726
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
2727
|
+
wallet_path: Optional[str] = Options.wallet_path,
|
2728
|
+
coldkey_ss58=typer.Option(
|
2729
|
+
None,
|
2730
|
+
"--ss58",
|
2731
|
+
"--coldkey_ss58",
|
2732
|
+
"--coldkey.ss58_address",
|
2733
|
+
"--coldkey.ss58",
|
2734
|
+
help="Coldkey address of the wallet",
|
2735
|
+
),
|
2736
|
+
live: bool = Options.live,
|
2452
2737
|
quiet: bool = Options.quiet,
|
2453
2738
|
verbose: bool = Options.verbose,
|
2739
|
+
no_prompt: bool = Options.prompt,
|
2740
|
+
# TODO add: all-wallets, reuse_last, html_output
|
2454
2741
|
):
|
2455
2742
|
"""
|
2456
|
-
|
2743
|
+
Display detailed stake information for a wallet across all subnets.
|
2457
2744
|
|
2458
|
-
|
2745
|
+
Shows stake allocations, exchange rates, and emissions for each hotkey.
|
2459
2746
|
|
2460
|
-
|
2747
|
+
[bold]Common Examples:[/bold]
|
2461
2748
|
|
2462
|
-
|
2749
|
+
1. Basic stake overview:
|
2750
|
+
[green]$[/green] btcli stake list --wallet.name my_wallet
|
2463
2751
|
|
2464
|
-
|
2752
|
+
2. Live updating view with refresh:
|
2753
|
+
[green]$[/green] btcli stake list --wallet.name my_wallet --live
|
2465
2754
|
|
2466
|
-
|
2755
|
+
3. View specific coldkey by address:
|
2756
|
+
[green]$[/green] btcli stake list --ss58 5Dk...X3q
|
2757
|
+
|
2758
|
+
4. Verbose output with full values:
|
2759
|
+
[green]$[/green] btcli stake list --wallet.name my_wallet --verbose
|
2467
2760
|
"""
|
2468
2761
|
self.verbosity_handler(quiet, verbose)
|
2762
|
+
|
2763
|
+
wallet = None
|
2764
|
+
if coldkey_ss58:
|
2765
|
+
if not is_valid_ss58_address(coldkey_ss58):
|
2766
|
+
print_error("You entered an invalid ss58 address")
|
2767
|
+
raise typer.Exit()
|
2768
|
+
else:
|
2769
|
+
if wallet_name:
|
2770
|
+
coldkey_or_ss58 = wallet_name
|
2771
|
+
else:
|
2772
|
+
coldkey_or_ss58 = Prompt.ask(
|
2773
|
+
"Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 address[/blue]",
|
2774
|
+
default=self.config.get("wallet_name") or defaults.wallet.name,
|
2775
|
+
)
|
2776
|
+
if is_valid_ss58_address(coldkey_or_ss58):
|
2777
|
+
coldkey_ss58 = coldkey_or_ss58
|
2778
|
+
else:
|
2779
|
+
wallet_name = coldkey_or_ss58 if coldkey_or_ss58 else wallet_name
|
2780
|
+
wallet = self.wallet_ask(
|
2781
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
2782
|
+
)
|
2783
|
+
|
2469
2784
|
return self._run_command(
|
2470
|
-
|
2785
|
+
list_stake.stake_list(
|
2786
|
+
wallet,
|
2787
|
+
coldkey_ss58,
|
2788
|
+
self.initialize_chain(network),
|
2789
|
+
live,
|
2790
|
+
verbose,
|
2791
|
+
no_prompt,
|
2792
|
+
)
|
2471
2793
|
)
|
2472
2794
|
|
2473
|
-
def
|
2795
|
+
def stake_add(
|
2474
2796
|
self,
|
2475
|
-
|
2797
|
+
stake_all: bool = typer.Option(
|
2798
|
+
False,
|
2799
|
+
"--all-tokens",
|
2800
|
+
"--all",
|
2801
|
+
"-a",
|
2802
|
+
help="When set, the command stakes all the available TAO from the coldkey.",
|
2803
|
+
),
|
2804
|
+
amount: float = typer.Option(
|
2805
|
+
0.0, "--amount", help="The amount of TAO to stake"
|
2806
|
+
),
|
2807
|
+
include_hotkeys: str = typer.Option(
|
2808
|
+
"",
|
2809
|
+
"--include-hotkeys",
|
2810
|
+
"-in",
|
2811
|
+
"--hotkey-ss58-address",
|
2812
|
+
help="Specifies hotkeys by name or ss58 address to stake to. For example, `-in hk1,hk2`",
|
2813
|
+
),
|
2814
|
+
exclude_hotkeys: str = typer.Option(
|
2815
|
+
"",
|
2816
|
+
"--exclude-hotkeys",
|
2817
|
+
"-ex",
|
2818
|
+
help="Specifies hotkeys by name or ss58 address to not to stake to (use this option only with `--all-hotkeys`)"
|
2819
|
+
" i.e. `--all-hotkeys -ex hk3,hk4`",
|
2820
|
+
),
|
2821
|
+
all_hotkeys: bool = typer.Option(
|
2822
|
+
False,
|
2823
|
+
help="When set, this command stakes to all hotkeys associated with the wallet. Do not use if specifying "
|
2824
|
+
"hotkeys in `--include-hotkeys`.",
|
2825
|
+
),
|
2826
|
+
netuid: Optional[int] = Options.netuid_not_req,
|
2827
|
+
all_netuids: bool = Options.all_netuids,
|
2476
2828
|
wallet_name: str = Options.wallet_name,
|
2477
2829
|
wallet_path: str = Options.wallet_path,
|
2478
2830
|
wallet_hotkey: str = Options.wallet_hotkey,
|
2479
|
-
|
2480
|
-
|
2481
|
-
|
2482
|
-
|
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,
|
2831
|
+
network: Optional[list[str]] = Options.network,
|
2832
|
+
rate_tolerance: Optional[float] = Options.rate_tolerance,
|
2833
|
+
safe_staking: Optional[bool] = Options.safe_staking,
|
2834
|
+
allow_partial_stake: Optional[bool] = Options.allow_partial_stake,
|
2487
2835
|
prompt: bool = Options.prompt,
|
2488
2836
|
quiet: bool = Options.quiet,
|
2489
2837
|
verbose: bool = Options.verbose,
|
2490
2838
|
):
|
2491
2839
|
"""
|
2492
|
-
|
2840
|
+
Stake TAO to one or more hotkeys on specific netuids with your coldkey.
|
2493
2841
|
|
2494
|
-
|
2842
|
+
Stake is always added through your coldkey's free balance. For stake movement, please see `[green]$[/green] btcli stake move` command.
|
2495
2843
|
|
2496
|
-
|
2844
|
+
[bold]Common Examples:[/bold]
|
2497
2845
|
|
2498
|
-
|
2846
|
+
1. Interactive staking (guided prompts):
|
2847
|
+
[green]$[/green] btcli stake add
|
2848
|
+
|
2849
|
+
2. Safe staking with rate tolerance of 10% with partial transaction disabled:
|
2850
|
+
[green]$[/green] btcli stake add --amount 100 --netuid 1 --safe --tolerance 0.1 --no-partial
|
2851
|
+
|
2852
|
+
3. Allow partial stake if rates change with tolerance of 10%:
|
2853
|
+
[green]$[/green] btcli stake add --amount 300 --safe --partial --tolerance 0.1
|
2499
2854
|
|
2500
|
-
|
2855
|
+
4. Unsafe staking with no rate protection:
|
2856
|
+
[green]$[/green] btcli stake add --amount 300 --netuid 1 --unsafe
|
2501
2857
|
|
2502
|
-
|
2858
|
+
5. Stake to multiple hotkeys:
|
2859
|
+
[green]$[/green] btcli stake add --amount 200 --include-hotkeys hk_ss58_1,hk_ss58_2,hk_ss58_3
|
2503
2860
|
|
2504
|
-
|
2861
|
+
6. Stake all balance to a subnet:
|
2862
|
+
[green]$[/green] btcli stake add --all --netuid 3
|
2505
2863
|
|
2506
|
-
|
2864
|
+
[bold]Safe Staking Parameters:[/bold]
|
2865
|
+
• [blue]--safe[/blue]: Enables rate tolerance checks
|
2866
|
+
• [blue]--tolerance[/blue]: Maximum % rate change allowed (0.05 = 5%)
|
2867
|
+
• [blue]--partial[/blue]: Complete partial stake if rates exceed tolerance
|
2507
2868
|
|
2508
|
-
[green]$[/green] btcli root set-weights --netuids "1, 2" --weights "0.2, 0.3"
|
2509
2869
|
"""
|
2510
2870
|
self.verbosity_handler(quiet, verbose)
|
2871
|
+
safe_staking = self.ask_safe_staking(safe_staking)
|
2872
|
+
if safe_staking:
|
2873
|
+
rate_tolerance = self.ask_rate_tolerance(rate_tolerance)
|
2874
|
+
allow_partial_stake = self.ask_partial_stake(allow_partial_stake)
|
2875
|
+
console.print("\n")
|
2876
|
+
netuid = get_optional_netuid(netuid, all_netuids)
|
2511
2877
|
|
2512
|
-
if
|
2513
|
-
|
2514
|
-
|
2515
|
-
int,
|
2516
|
-
"Netuids must be a comma-separated list of ints, e.g., `--netuid 1,2,3,4`.",
|
2878
|
+
if stake_all and amount:
|
2879
|
+
print_error(
|
2880
|
+
"Cannot specify an amount and 'stake-all'. Choose one or the other."
|
2517
2881
|
)
|
2518
|
-
|
2519
|
-
netuids = list_prompt(netuids, int, "Enter netuids (e.g: 1, 4, 6)")
|
2882
|
+
raise typer.Exit()
|
2520
2883
|
|
2521
|
-
if
|
2522
|
-
|
2523
|
-
|
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
|
-
)
|
2884
|
+
if stake_all and not amount:
|
2885
|
+
if not Confirm.ask("Stake all the available TAO tokens?", default=False):
|
2886
|
+
raise typer.Exit()
|
2531
2887
|
|
2532
|
-
if
|
2533
|
-
|
2534
|
-
"
|
2888
|
+
if all_hotkeys and include_hotkeys:
|
2889
|
+
print_error(
|
2890
|
+
"You have specified hotkeys to include and also the `--all-hotkeys` flag. The flag"
|
2891
|
+
"should only be used standalone (to use all hotkeys) or with `--exclude-hotkeys`."
|
2535
2892
|
)
|
2893
|
+
raise typer.Exit()
|
2536
2894
|
|
2537
|
-
|
2538
|
-
|
2539
|
-
|
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
|
2895
|
+
if include_hotkeys and exclude_hotkeys:
|
2896
|
+
print_error(
|
2897
|
+
"You have specified options for both including and excluding hotkeys. Select one or the other."
|
2547
2898
|
)
|
2548
|
-
|
2899
|
+
raise typer.Exit()
|
2549
2900
|
|
2550
|
-
|
2551
|
-
|
2552
|
-
|
2553
|
-
|
2554
|
-
|
2555
|
-
|
2556
|
-
|
2557
|
-
|
2558
|
-
|
2559
|
-
|
2560
|
-
|
2561
|
-
|
2562
|
-
|
2563
|
-
|
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.
|
2901
|
+
if not wallet_hotkey and not all_hotkeys and not include_hotkeys:
|
2902
|
+
if not wallet_name:
|
2903
|
+
wallet_name = Prompt.ask(
|
2904
|
+
"Enter the [blue]wallet name[/blue]",
|
2905
|
+
default=self.config.get("wallet_name") or defaults.wallet.name,
|
2906
|
+
)
|
2907
|
+
if netuid is not None:
|
2908
|
+
hotkey_or_ss58 = Prompt.ask(
|
2909
|
+
"Enter the [blue]wallet hotkey[/blue] name or [blue]ss58 address[/blue] to stake to [dim](or Press Enter to view delegates)[/dim]",
|
2910
|
+
)
|
2911
|
+
else:
|
2912
|
+
hotkey_or_ss58 = Prompt.ask(
|
2913
|
+
"Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to stake to",
|
2914
|
+
default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
|
2915
|
+
)
|
2572
2916
|
|
2573
|
-
|
2917
|
+
if hotkey_or_ss58 == "":
|
2918
|
+
wallet = self.wallet_ask(
|
2919
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
2920
|
+
)
|
2921
|
+
selected_hotkey = self._run_command(
|
2922
|
+
subnets.show(
|
2923
|
+
subtensor=self.initialize_chain(network),
|
2924
|
+
netuid=netuid,
|
2925
|
+
sort=False,
|
2926
|
+
max_rows=12,
|
2927
|
+
prompt=False,
|
2928
|
+
delegate_selection=True,
|
2929
|
+
),
|
2930
|
+
exit_early=False,
|
2931
|
+
)
|
2932
|
+
if selected_hotkey is None:
|
2933
|
+
print_error("No delegate selected. Exiting.")
|
2934
|
+
raise typer.Exit()
|
2935
|
+
include_hotkeys = selected_hotkey
|
2936
|
+
elif is_valid_ss58_address(hotkey_or_ss58):
|
2937
|
+
wallet = self.wallet_ask(
|
2938
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
2939
|
+
)
|
2940
|
+
include_hotkeys = hotkey_or_ss58
|
2941
|
+
else:
|
2942
|
+
wallet_hotkey = hotkey_or_ss58
|
2943
|
+
wallet = self.wallet_ask(
|
2944
|
+
wallet_name,
|
2945
|
+
wallet_path,
|
2946
|
+
wallet_hotkey,
|
2947
|
+
ask_for=[WO.NAME, WO.HOTKEY, WO.PATH],
|
2948
|
+
validate=WV.WALLET_AND_HOTKEY,
|
2949
|
+
)
|
2950
|
+
include_hotkeys = wallet.hotkey.ss58_address
|
2574
2951
|
|
2575
|
-
|
2952
|
+
elif all_hotkeys or include_hotkeys or exclude_hotkeys:
|
2953
|
+
wallet = self.wallet_ask(
|
2954
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
2955
|
+
)
|
2956
|
+
else:
|
2957
|
+
wallet = self.wallet_ask(
|
2958
|
+
wallet_name,
|
2959
|
+
wallet_path,
|
2960
|
+
wallet_hotkey,
|
2961
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
2962
|
+
validate=WV.WALLET_AND_HOTKEY,
|
2963
|
+
)
|
2576
2964
|
|
2577
|
-
|
2578
|
-
|
2579
|
-
|
2580
|
-
|
2581
|
-
|
2582
|
-
|
2583
|
-
"Change it to 'False' using `btcli config set`."
|
2965
|
+
if include_hotkeys:
|
2966
|
+
include_hotkeys = parse_to_list(
|
2967
|
+
include_hotkeys,
|
2968
|
+
str,
|
2969
|
+
"Hotkeys must be a comma-separated list of ss58s, e.g., `--include-hotkeys 5Grw....,5Grw....`.",
|
2970
|
+
is_ss58=True,
|
2584
2971
|
)
|
2585
|
-
raise typer.Exit()
|
2586
|
-
if not reuse_last:
|
2587
|
-
subtensor = self.initialize_chain(network)
|
2588
2972
|
else:
|
2589
|
-
|
2973
|
+
include_hotkeys = []
|
2974
|
+
|
2975
|
+
if exclude_hotkeys:
|
2976
|
+
exclude_hotkeys = parse_to_list(
|
2977
|
+
exclude_hotkeys,
|
2978
|
+
str,
|
2979
|
+
"Hotkeys must be a comma-separated list of ss58s, e.g., `--exclude-hotkeys 5Grw....,5Grw....`.",
|
2980
|
+
is_ss58=True,
|
2981
|
+
)
|
2982
|
+
else:
|
2983
|
+
exclude_hotkeys = []
|
2984
|
+
|
2985
|
+
# TODO: Ask amount for each subnet explicitly if more than one
|
2986
|
+
if not stake_all and not amount:
|
2987
|
+
free_balance = self._run_command(
|
2988
|
+
wallets.wallet_balance(
|
2989
|
+
wallet, self.initialize_chain(network), False, None
|
2990
|
+
),
|
2991
|
+
exit_early=False,
|
2992
|
+
)
|
2993
|
+
if free_balance == Balance.from_tao(0):
|
2994
|
+
print_error("You dont have any balance to stake.")
|
2995
|
+
raise typer.Exit()
|
2996
|
+
if netuid is not None:
|
2997
|
+
amount = FloatPrompt.ask(
|
2998
|
+
f"Amount to [{COLOR_PALETTE['GENERAL']['SUBHEADING_MAIN']}]stake (TAO τ)"
|
2999
|
+
)
|
3000
|
+
else:
|
3001
|
+
amount = FloatPrompt.ask(
|
3002
|
+
f"Amount to [{COLOR_PALETTE['GENERAL']['SUBHEADING_MAIN']}]stake to each netuid (TAO τ)"
|
3003
|
+
)
|
3004
|
+
|
3005
|
+
if amount <= 0:
|
3006
|
+
print_error(f"You entered an incorrect stake amount: {amount}")
|
3007
|
+
raise typer.Exit()
|
3008
|
+
if Balance.from_tao(amount) > free_balance:
|
3009
|
+
print_error(
|
3010
|
+
f"You dont have enough balance to stake. Current free Balance: {free_balance}."
|
3011
|
+
)
|
3012
|
+
raise typer.Exit()
|
3013
|
+
|
2590
3014
|
return self._run_command(
|
2591
|
-
|
2592
|
-
|
2593
|
-
|
2594
|
-
|
2595
|
-
|
2596
|
-
|
2597
|
-
|
3015
|
+
add_stake.stake_add(
|
3016
|
+
wallet,
|
3017
|
+
self.initialize_chain(network),
|
3018
|
+
netuid,
|
3019
|
+
stake_all,
|
3020
|
+
amount,
|
3021
|
+
prompt,
|
3022
|
+
all_hotkeys,
|
3023
|
+
include_hotkeys,
|
3024
|
+
exclude_hotkeys,
|
3025
|
+
safe_staking,
|
3026
|
+
rate_tolerance,
|
3027
|
+
allow_partial_stake,
|
2598
3028
|
)
|
2599
3029
|
)
|
2600
3030
|
|
2601
|
-
def
|
3031
|
+
def stake_remove(
|
2602
3032
|
self,
|
2603
3033
|
network: Optional[list[str]] = Options.network,
|
2604
3034
|
wallet_name: str = Options.wallet_name,
|
2605
|
-
wallet_path:
|
2606
|
-
wallet_hotkey:
|
2607
|
-
netuid: int = Options.
|
3035
|
+
wallet_path: str = Options.wallet_path,
|
3036
|
+
wallet_hotkey: str = Options.wallet_hotkey,
|
3037
|
+
netuid: Optional[int] = Options.netuid_not_req,
|
3038
|
+
all_netuids: bool = Options.all_netuids,
|
3039
|
+
unstake_all: bool = typer.Option(
|
3040
|
+
False,
|
3041
|
+
"--unstake-all",
|
3042
|
+
"--all",
|
3043
|
+
hidden=True,
|
3044
|
+
help="When set, this command unstakes all staked TAO + Alpha from the all hotkeys.",
|
3045
|
+
),
|
3046
|
+
unstake_all_alpha: bool = typer.Option(
|
3047
|
+
False,
|
3048
|
+
"--unstake-all-alpha",
|
3049
|
+
"--all-alpha",
|
3050
|
+
hidden=True,
|
3051
|
+
help="When set, this command unstakes all staked Alpha from the all hotkeys.",
|
3052
|
+
),
|
2608
3053
|
amount: float = typer.Option(
|
2609
|
-
|
2610
|
-
|
2611
|
-
|
2612
|
-
"
|
2613
|
-
|
2614
|
-
|
3054
|
+
0.0, "--amount", "-a", help="The amount of TAO to unstake."
|
3055
|
+
),
|
3056
|
+
hotkey_ss58_address: str = typer.Option(
|
3057
|
+
"",
|
3058
|
+
help="The ss58 address of the hotkey to unstake from.",
|
3059
|
+
),
|
3060
|
+
include_hotkeys: str = typer.Option(
|
3061
|
+
"",
|
3062
|
+
"--include-hotkeys",
|
3063
|
+
"-in",
|
3064
|
+
help="Specifies the hotkeys by name or ss58 address to unstake from. For example, `-in hk1,hk2`",
|
3065
|
+
),
|
3066
|
+
exclude_hotkeys: str = typer.Option(
|
3067
|
+
"",
|
3068
|
+
"--exclude-hotkeys",
|
3069
|
+
"-ex",
|
3070
|
+
help="Specifies the hotkeys by name or ss58 address not to unstake from (only use with `--all-hotkeys`)"
|
3071
|
+
" i.e. `--all-hotkeys -ex hk3,hk4`",
|
3072
|
+
),
|
3073
|
+
all_hotkeys: bool = typer.Option(
|
3074
|
+
False,
|
3075
|
+
help="When set, this command unstakes from all the hotkeys associated with the wallet. Do not use if specifying "
|
3076
|
+
"hotkeys in `--include-hotkeys`.",
|
2615
3077
|
),
|
3078
|
+
rate_tolerance: Optional[float] = Options.rate_tolerance,
|
3079
|
+
safe_staking: Optional[bool] = Options.safe_staking,
|
3080
|
+
allow_partial_stake: Optional[bool] = Options.allow_partial_stake,
|
2616
3081
|
prompt: bool = Options.prompt,
|
3082
|
+
interactive: bool = typer.Option(
|
3083
|
+
False,
|
3084
|
+
"--interactive",
|
3085
|
+
"-i",
|
3086
|
+
help="Enter interactive mode for unstaking.",
|
3087
|
+
),
|
2617
3088
|
quiet: bool = Options.quiet,
|
2618
3089
|
verbose: bool = Options.verbose,
|
2619
3090
|
):
|
2620
3091
|
"""
|
2621
|
-
|
3092
|
+
Unstake TAO from one or more hotkeys and transfer them back to the user's coldkey wallet.
|
2622
3093
|
|
2623
|
-
|
3094
|
+
This command is used to withdraw TAO or Alpha stake from different hotkeys.
|
2624
3095
|
|
2625
|
-
[
|
3096
|
+
[bold]Common Examples:[/bold]
|
3097
|
+
|
3098
|
+
1. Interactive unstaking (guided prompts):
|
3099
|
+
[green]$[/green] btcli stake remove
|
3100
|
+
|
3101
|
+
2. Safe unstaking with 10% rate tolerance:
|
3102
|
+
[green]$[/green] btcli stake remove --amount 100 --netuid 1 --safe --tolerance 0.1
|
3103
|
+
|
3104
|
+
3. Allow partial unstake if rates change:
|
3105
|
+
[green]$[/green] btcli stake remove --amount 300 --safe --partial
|
3106
|
+
|
3107
|
+
4. Unstake from multiple hotkeys:
|
3108
|
+
[green]$[/green] btcli stake remove --amount 200 --include-hotkeys hk1,hk2,hk3
|
3109
|
+
|
3110
|
+
5. Unstake all from a hotkey:
|
3111
|
+
[green]$[/green] btcli stake remove --all
|
3112
|
+
|
3113
|
+
6. Unstake all Alpha from a hotkey and stake to Root:
|
3114
|
+
[green]$[/green] btcli stake remove --all-alpha
|
3115
|
+
|
3116
|
+
[bold]Safe Staking Parameters:[/bold]
|
3117
|
+
• [blue]--safe[/blue]: Enables rate tolerance checks during unstaking
|
3118
|
+
• [blue]--tolerance[/blue]: Max allowed rate change (0.05 = 5%)
|
3119
|
+
• [blue]--partial[/blue]: Complete partial unstake if rates exceed tolerance
|
2626
3120
|
"""
|
2627
3121
|
self.verbosity_handler(quiet, verbose)
|
2628
|
-
|
2629
|
-
|
2630
|
-
|
2631
|
-
|
2632
|
-
|
2633
|
-
|
2634
|
-
|
2635
|
-
|
2636
|
-
|
2637
|
-
|
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(
|
2802
|
-
root.proposals(self.initialize_chain(network), verbose)
|
2803
|
-
)
|
2804
|
-
|
2805
|
-
def root_set_take(
|
2806
|
-
self,
|
2807
|
-
network: Optional[list[str]] = Options.network,
|
2808
|
-
wallet_name: Optional[str] = Options.wallet_name,
|
2809
|
-
wallet_path: Optional[str] = Options.wallet_path,
|
2810
|
-
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
2811
|
-
take: float = typer.Option(None, help="The new take value."),
|
2812
|
-
quiet: bool = Options.quiet,
|
2813
|
-
verbose: bool = Options.verbose,
|
2814
|
-
):
|
2815
|
-
"""
|
2816
|
-
Allows users to change their delegate take percentage.
|
2817
|
-
|
2818
|
-
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:
|
2819
|
-
|
2820
|
-
1. The provided hotkey is already a delegate.
|
2821
|
-
2. The new take value is within 0-18% range.
|
2822
|
-
|
2823
|
-
EXAMPLE
|
2824
|
-
|
2825
|
-
[green]$[/green] btcli root set_take --wallet-name my_wallet --wallet-hotkey my_hotkey
|
2826
|
-
"""
|
2827
|
-
max_value = 0.18
|
2828
|
-
min_value = 0.00
|
2829
|
-
self.verbosity_handler(quiet, verbose)
|
2830
|
-
|
2831
|
-
if not take:
|
2832
|
-
max_value_style = typer.style(f"Max: {max_value}", fg="magenta")
|
2833
|
-
min_value_style = typer.style(f"Min: {min_value}", fg="magenta")
|
2834
|
-
prompt_text = typer.style(
|
2835
|
-
"Enter take value (0.18 for 18%)", fg="bright_cyan", bold=True
|
2836
|
-
)
|
2837
|
-
take = FloatPrompt.ask(f"{prompt_text} {min_value_style} {max_value_style}")
|
2838
|
-
|
2839
|
-
if not (min_value <= take <= max_value):
|
2840
|
-
print_error(
|
2841
|
-
f"Take value must be between {min_value} and {max_value}. Provided value: {take}"
|
2842
|
-
)
|
2843
|
-
raise typer.Exit()
|
2844
|
-
|
2845
|
-
wallet = self.wallet_ask(
|
2846
|
-
wallet_name,
|
2847
|
-
wallet_path,
|
2848
|
-
wallet_hotkey,
|
2849
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
2850
|
-
validate=WV.WALLET_AND_HOTKEY,
|
2851
|
-
)
|
2852
|
-
|
2853
|
-
return self._run_command(
|
2854
|
-
root.set_take(wallet, self.initialize_chain(network), take)
|
2855
|
-
)
|
2856
|
-
|
2857
|
-
def root_delegate_stake(
|
2858
|
-
self,
|
2859
|
-
delegate_ss58key: str = typer.Option(
|
2860
|
-
None,
|
2861
|
-
help="The ss58 address of the delegate hotkey to stake TAO to.",
|
2862
|
-
prompt="Enter the hotkey ss58 address you want to delegate TAO to.",
|
2863
|
-
),
|
2864
|
-
amount: Optional[float] = typer.Option(
|
2865
|
-
None, help="The amount of TAO to stake. Do no specify if using `--all`"
|
2866
|
-
),
|
2867
|
-
stake_all: Optional[bool] = typer.Option(
|
2868
|
-
False,
|
2869
|
-
"--all",
|
2870
|
-
"-a",
|
2871
|
-
help="If specified, the command stakes all available TAO. Do not specify if using"
|
2872
|
-
" `--amount`",
|
2873
|
-
),
|
2874
|
-
wallet_name: Optional[str] = Options.wallet_name,
|
2875
|
-
wallet_path: Optional[str] = Options.wallet_path,
|
2876
|
-
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
2877
|
-
network: Optional[list[str]] = Options.network,
|
2878
|
-
prompt: bool = Options.prompt,
|
2879
|
-
quiet: bool = Options.quiet,
|
2880
|
-
verbose: bool = Options.verbose,
|
2881
|
-
):
|
2882
|
-
"""
|
2883
|
-
Stakes TAO to a specified delegate hotkey.
|
2884
|
-
|
2885
|
-
This command allocates the user's TAO to the specified hotkey of a delegate, potentially earning staking rewards in return. If the
|
2886
|
-
`--all` flag is used, it delegates the entire TAO balance available in the user's wallet.
|
2887
|
-
|
2888
|
-
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.
|
2889
|
-
|
2890
|
-
EXAMPLE
|
2891
|
-
|
2892
|
-
[green]$[/green] btcli root delegate-stake --delegate_ss58key <SS58_ADDRESS> --amount <AMOUNT>
|
2893
|
-
|
2894
|
-
[green]$[/green] btcli root delegate-stake --delegate_ss58key <SS58_ADDRESS> --all
|
2895
|
-
|
2896
|
-
[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.
|
2897
|
-
"""
|
2898
|
-
self.verbosity_handler(quiet, verbose)
|
2899
|
-
if amount and stake_all:
|
2900
|
-
err_console.print(
|
2901
|
-
"Both `--amount` and `--all` are specified. Choose one or the other."
|
2902
|
-
)
|
2903
|
-
if not stake_all and not amount:
|
2904
|
-
while True:
|
2905
|
-
amount = FloatPrompt.ask(
|
2906
|
-
"Amount to [blue]stake (TAO τ)[/blue]", console=console
|
2907
|
-
)
|
2908
|
-
confirmation = FloatPrompt.ask(
|
2909
|
-
"Confirm the amount to stake [blue](TAO τ)[/blue]",
|
2910
|
-
console=console,
|
2911
|
-
)
|
2912
|
-
if amount == confirmation:
|
2913
|
-
break
|
2914
|
-
else:
|
2915
|
-
err_console.print(
|
2916
|
-
"[red]The amounts do not match. Please try again.[/red]"
|
2917
|
-
)
|
2918
|
-
|
2919
|
-
wallet = self.wallet_ask(
|
2920
|
-
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
2921
|
-
)
|
2922
|
-
return self._run_command(
|
2923
|
-
root.delegate_stake(
|
2924
|
-
wallet,
|
2925
|
-
self.initialize_chain(network),
|
2926
|
-
amount,
|
2927
|
-
delegate_ss58key,
|
2928
|
-
prompt,
|
2929
|
-
)
|
2930
|
-
)
|
2931
|
-
|
2932
|
-
def root_undelegate_stake(
|
2933
|
-
self,
|
2934
|
-
delegate_ss58key: str = typer.Option(
|
2935
|
-
None,
|
2936
|
-
help="The ss58 address of the delegate to undelegate from.",
|
2937
|
-
prompt="Enter the hotkey ss58 address you want to undelegate from",
|
2938
|
-
),
|
2939
|
-
amount: Optional[float] = typer.Option(
|
2940
|
-
None, help="The amount of TAO to unstake. Do no specify if using `--all`"
|
2941
|
-
),
|
2942
|
-
unstake_all: Optional[bool] = typer.Option(
|
2943
|
-
False,
|
2944
|
-
"--all",
|
2945
|
-
"-a",
|
2946
|
-
help="If specified, the command undelegates all staked TAO from the delegate. Do not specify if using"
|
2947
|
-
" `--amount`",
|
2948
|
-
),
|
2949
|
-
wallet_name: Optional[str] = Options.wallet_name,
|
2950
|
-
wallet_path: Optional[str] = Options.wallet_path,
|
2951
|
-
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
2952
|
-
network: Optional[list[str]] = Options.network,
|
2953
|
-
prompt: bool = Options.prompt,
|
2954
|
-
quiet: bool = Options.quiet,
|
2955
|
-
verbose: bool = Options.verbose,
|
2956
|
-
):
|
2957
|
-
"""
|
2958
|
-
Allows users to withdraw their staked TAO from a delegate.
|
2959
|
-
|
2960
|
-
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.
|
2961
|
-
|
2962
|
-
EXAMPLE
|
2963
|
-
|
2964
|
-
[green]$[/green] btcli undelegate --delegate_ss58key <SS58_ADDRESS> --amount <AMOUNT>
|
2965
|
-
|
2966
|
-
[green]$[/green] btcli undelegate --delegate_ss58key <SS58_ADDRESS> --all
|
2967
|
-
"""
|
2968
|
-
self.verbosity_handler(quiet, verbose)
|
2969
|
-
if amount and unstake_all:
|
2970
|
-
err_console.print(
|
2971
|
-
"Both `--amount` and `--all` are specified. Choose one or the other."
|
2972
|
-
)
|
2973
|
-
if not unstake_all and not amount:
|
2974
|
-
while True:
|
2975
|
-
amount = FloatPrompt.ask(
|
2976
|
-
"Amount to [blue]unstake (TAO τ)[/blue]", console=console
|
2977
|
-
)
|
2978
|
-
confirmation = FloatPrompt.ask(
|
2979
|
-
"Confirm the amount to unstake [blue](TAO τ)[/blue]",
|
2980
|
-
console=console,
|
2981
|
-
)
|
2982
|
-
if amount == confirmation:
|
2983
|
-
break
|
2984
|
-
else:
|
2985
|
-
err_console.print(
|
2986
|
-
"[red]The amounts do not match. Please try again.[/red]"
|
2987
|
-
)
|
2988
|
-
|
2989
|
-
wallet = self.wallet_ask(
|
2990
|
-
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
2991
|
-
)
|
2992
|
-
self._run_command(
|
2993
|
-
root.delegate_unstake(
|
2994
|
-
wallet,
|
2995
|
-
self.initialize_chain(network),
|
2996
|
-
amount,
|
2997
|
-
delegate_ss58key,
|
2998
|
-
prompt,
|
2999
|
-
)
|
3000
|
-
)
|
3001
|
-
|
3002
|
-
def root_my_delegates(
|
3003
|
-
self,
|
3004
|
-
network: Optional[list[str]] = Options.network,
|
3005
|
-
wallet_name: Optional[str] = Options.wallet_name,
|
3006
|
-
wallet_path: Optional[str] = Options.wallet_path,
|
3007
|
-
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
3008
|
-
all_wallets: bool = typer.Option(
|
3009
|
-
False,
|
3010
|
-
"--all-wallets",
|
3011
|
-
"--all",
|
3012
|
-
"-a",
|
3013
|
-
help="If specified, the command aggregates information across all the wallets.",
|
3014
|
-
),
|
3015
|
-
quiet: bool = Options.quiet,
|
3016
|
-
verbose: bool = Options.verbose,
|
3017
|
-
):
|
3018
|
-
"""
|
3019
|
-
Shows a table with the details on the user's delegates.
|
3020
|
-
|
3021
|
-
The table output includes the following columns:
|
3022
|
-
|
3023
|
-
- Wallet: The name of the user's wallet (coldkey).
|
3024
|
-
|
3025
|
-
- OWNER: The name of the delegate who owns the hotkey.
|
3026
|
-
|
3027
|
-
- SS58: The truncated SS58 address of the delegate's hotkey.
|
3028
|
-
|
3029
|
-
- Delegation: The amount of TAO staked by the user to the delegate.
|
3030
|
-
|
3031
|
-
- τ/24h: The earnings from the delegate to the user over the past 24 hours.
|
3032
|
-
|
3033
|
-
- NOMS: The number of nominators for the delegate.
|
3034
|
-
|
3035
|
-
- OWNER STAKE(τ): The stake amount owned by the delegate.
|
3036
|
-
|
3037
|
-
- TOTAL STAKE(τ): The total stake amount held by the delegate.
|
3038
|
-
|
3039
|
-
- SUBNETS: The list of subnets the delegate is a part of.
|
3040
|
-
|
3041
|
-
- VPERMIT: Validator permits held by the delegate for various subnets.
|
3042
|
-
|
3043
|
-
- 24h/kτ: Earnings per 1000 TAO staked over the last 24 hours.
|
3044
|
-
|
3045
|
-
- Desc: A description of the delegate.
|
3046
|
-
|
3047
|
-
The command also sums and prints the total amount of TAO delegated across all wallets.
|
3048
|
-
|
3049
|
-
EXAMPLE
|
3050
|
-
|
3051
|
-
[green]$[/green] btcli root my-delegates
|
3052
|
-
[green]$[/green] btcli root my-delegates --all
|
3053
|
-
[green]$[/green] btcli root my-delegates --wallet-name my_wallet
|
3054
|
-
|
3055
|
-
[blue bold]Note[/blue bold]: This command is not intended to be used directly in user code.
|
3056
|
-
"""
|
3057
|
-
self.verbosity_handler(quiet, verbose)
|
3058
|
-
wallet = self.wallet_ask(
|
3059
|
-
wallet_name,
|
3060
|
-
wallet_path,
|
3061
|
-
wallet_hotkey,
|
3062
|
-
ask_for=([WO.NAME] if not all_wallets else []),
|
3063
|
-
validate=WV.WALLET if not all_wallets else WV.NONE,
|
3064
|
-
)
|
3065
|
-
self._run_command(
|
3066
|
-
root.my_delegates(wallet, self.initialize_chain(network), all_wallets)
|
3067
|
-
)
|
3068
|
-
|
3069
|
-
def root_list_delegates(
|
3070
|
-
self,
|
3071
|
-
network: Optional[list[str]] = Options.network,
|
3072
|
-
quiet: bool = Options.quiet,
|
3073
|
-
verbose: bool = Options.verbose,
|
3074
|
-
):
|
3075
|
-
"""
|
3076
|
-
Displays a table of Bittensor network-wide delegates, providing a comprehensive overview of delegate statistics and information.
|
3077
|
-
|
3078
|
-
This table helps users make informed decisions on which delegates to allocate their TAO stake.
|
3079
|
-
|
3080
|
-
The table columns include:
|
3081
|
-
|
3082
|
-
- INDEX: The delegate's index in the sorted list.
|
3083
|
-
|
3084
|
-
- DELEGATE: The name of the delegate.
|
3085
|
-
|
3086
|
-
- SS58: The delegate's unique ss58 address (truncated for display).
|
3087
|
-
|
3088
|
-
- NOMINATORS: The count of nominators backing the delegate.
|
3089
|
-
|
3090
|
-
- OWN STAKE(τ): The amount of delegate's own stake (not the TAO delegated from any nominators).
|
3091
|
-
|
3092
|
-
- TOTAL STAKE(τ): The delegate's total stake, i.e., the sum of delegate's own stake and nominators' stakes.
|
3093
|
-
|
3094
|
-
- CHANGE/(4h): The percentage change in the delegate's stake over the last four hours.
|
3095
|
-
|
3096
|
-
- SUBNETS: The subnets in which the delegate is registered.
|
3097
|
-
|
3098
|
-
- VPERMIT: Indicates the subnets in which the delegate has validator permits.
|
3099
|
-
|
3100
|
-
- NOMINATOR/(24h)/kτ: The earnings per 1000 τ staked by nominators in the last 24 hours.
|
3101
|
-
|
3102
|
-
- DELEGATE/(24h): The total earnings of the delegate in the last 24 hours.
|
3103
|
-
|
3104
|
-
- DESCRIPTION: A brief description of the delegate's purpose and operations.
|
3105
|
-
|
3106
|
-
[blue bold]NOTES:[/blue bold]
|
3107
|
-
|
3108
|
-
- Sorting is done based on the `TOTAL STAKE` column in descending order.
|
3109
|
-
- Changes in stake are shown as: increases in green and decreases in red.
|
3110
|
-
- Entries with no previous data are marked with `NA`.
|
3111
|
-
- Each delegate's name is a hyperlink to more information, if available.
|
3112
|
-
|
3113
|
-
EXAMPLE
|
3114
|
-
|
3115
|
-
[green]$[/green] btcli root list_delegates
|
3116
|
-
|
3117
|
-
[green]$[/green] btcli root list_delegates --subtensor.network finney # can also be `test` or `local`
|
3118
|
-
|
3119
|
-
[blue bold]NOTE[/blue bold]: This command is intended for use within a
|
3120
|
-
console application. It prints directly to the console and does not return any value.
|
3121
|
-
"""
|
3122
|
-
self.verbosity_handler(quiet, verbose)
|
3123
|
-
|
3124
|
-
if network:
|
3125
|
-
if "finney" in network:
|
3126
|
-
network = ["wss://archive.chain.opentensor.ai:443"]
|
3127
|
-
elif (conf_net := self.config.get("network")) == "finney":
|
3128
|
-
network = ["wss://archive.chain.opentensor.ai:443"]
|
3129
|
-
elif conf_net:
|
3130
|
-
network = [conf_net]
|
3131
|
-
else:
|
3132
|
-
network = ["wss://archive.chain.opentensor.ai:443"]
|
3133
|
-
|
3134
|
-
sub = self.initialize_chain(network)
|
3135
|
-
return self._run_command(root.list_delegates(sub))
|
3136
|
-
|
3137
|
-
# TODO: Confirm if we need a command for this - currently registering to root auto makes u delegate
|
3138
|
-
def root_nominate(
|
3139
|
-
self,
|
3140
|
-
wallet_name: Optional[str] = Options.wallet_name,
|
3141
|
-
wallet_path: Optional[str] = Options.wallet_path,
|
3142
|
-
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
3143
|
-
network: Optional[list[str]] = Options.network,
|
3144
|
-
prompt: bool = Options.prompt,
|
3145
|
-
quiet: bool = Options.quiet,
|
3146
|
-
verbose: bool = Options.verbose,
|
3147
|
-
):
|
3148
|
-
"""
|
3149
|
-
Enables a wallet's hotkey to become a delegate.
|
3150
|
-
|
3151
|
-
This command handles the nomination process, including wallet unlocking and verification of the hotkey's current delegate status.
|
3152
|
-
|
3153
|
-
The command performs several checks:
|
3154
|
-
|
3155
|
-
- Verifies that the hotkey is not already a delegate to prevent redundant nominations.
|
3156
|
-
|
3157
|
-
- Tries to nominate the wallet and reports success or failure.
|
3158
|
-
|
3159
|
-
Upon success, the wallet's hotkey is registered as a delegate on the network.
|
3160
|
-
|
3161
|
-
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.
|
3162
|
-
|
3163
|
-
EXAMPLE
|
3164
|
-
|
3165
|
-
[green]$[/green] btcli root nominate
|
3166
|
-
|
3167
|
-
[green]$[/green] btcli root nominate --wallet-name my_wallet --wallet-hotkey my_hotkey
|
3168
|
-
|
3169
|
-
[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.
|
3170
|
-
"""
|
3171
|
-
self.verbosity_handler(quiet, verbose)
|
3172
|
-
wallet = self.wallet_ask(
|
3173
|
-
wallet_name,
|
3174
|
-
wallet_path,
|
3175
|
-
wallet_hotkey,
|
3176
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
3177
|
-
validate=WV.WALLET_AND_HOTKEY,
|
3178
|
-
)
|
3179
|
-
return self._run_command(
|
3180
|
-
root.nominate(wallet, self.initialize_chain(network), prompt)
|
3181
|
-
)
|
3182
|
-
|
3183
|
-
def stake_show(
|
3184
|
-
self,
|
3185
|
-
all_wallets: bool = typer.Option(
|
3186
|
-
False,
|
3187
|
-
"--all",
|
3188
|
-
"--all-wallets",
|
3189
|
-
"-a",
|
3190
|
-
help="When set, the command checks all the coldkey wallets of the user instead of just the specified wallet.",
|
3191
|
-
),
|
3192
|
-
network: Optional[list[str]] = Options.network,
|
3193
|
-
wallet_name: Optional[str] = Options.wallet_name,
|
3194
|
-
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
3195
|
-
wallet_path: Optional[str] = Options.wallet_path,
|
3196
|
-
reuse_last: bool = Options.reuse_last,
|
3197
|
-
html_output: bool = Options.html_output,
|
3198
|
-
quiet: bool = Options.quiet,
|
3199
|
-
verbose: bool = Options.verbose,
|
3200
|
-
):
|
3201
|
-
"""
|
3202
|
-
Lists all the stake accounts associated with a user's wallet.
|
3203
|
-
|
3204
|
-
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.
|
3205
|
-
|
3206
|
-
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.
|
3207
|
-
|
3208
|
-
The command shows a table with the below columns:
|
3209
|
-
|
3210
|
-
- Coldkey: The coldkey associated with the wallet.
|
3211
|
-
|
3212
|
-
- Balance: The balance of the coldkey.
|
3213
|
-
|
3214
|
-
- Hotkey: The names of the coldkey's own hotkeys and the delegate hotkeys to which this coldkey has staked.
|
3215
|
-
|
3216
|
-
- Stake: The amount of TAO staked to all the hotkeys.
|
3217
|
-
|
3218
|
-
- Rate: The rate of return on the stake, shown in TAO per day.
|
3219
|
-
|
3220
|
-
EXAMPLE
|
3221
|
-
|
3222
|
-
[green]$[/green] btcli stake show --all
|
3223
|
-
"""
|
3224
|
-
self.verbosity_handler(quiet, verbose)
|
3225
|
-
if (reuse_last or html_output) and self.config.get("use_cache") is False:
|
3226
|
-
err_console.print(
|
3227
|
-
"Unable to use `--reuse-last` or `--html` when config 'no-cache' is set to 'True'. "
|
3228
|
-
"Please change the config to 'False' using `btcli config set`"
|
3122
|
+
if not unstake_all and not unstake_all_alpha:
|
3123
|
+
safe_staking = self.ask_safe_staking(safe_staking)
|
3124
|
+
if safe_staking:
|
3125
|
+
rate_tolerance = self.ask_rate_tolerance(rate_tolerance)
|
3126
|
+
allow_partial_stake = self.ask_partial_stake(allow_partial_stake)
|
3127
|
+
console.print("\n")
|
3128
|
+
|
3129
|
+
if interactive and any(
|
3130
|
+
[hotkey_ss58_address, include_hotkeys, exclude_hotkeys, all_hotkeys]
|
3131
|
+
):
|
3132
|
+
print_error(
|
3133
|
+
"Interactive mode cannot be used with hotkey selection options like --include-hotkeys, --exclude-hotkeys, --all-hotkeys, or --hotkey."
|
3229
3134
|
)
|
3230
3135
|
raise typer.Exit()
|
3231
|
-
if not reuse_last:
|
3232
|
-
subtensor = self.initialize_chain(network)
|
3233
|
-
else:
|
3234
|
-
subtensor = None
|
3235
|
-
|
3236
|
-
if all_wallets:
|
3237
|
-
wallet = self.wallet_ask(
|
3238
|
-
wallet_name,
|
3239
|
-
wallet_path,
|
3240
|
-
wallet_hotkey,
|
3241
|
-
ask_for=[],
|
3242
|
-
validate=WV.NONE,
|
3243
|
-
)
|
3244
|
-
else:
|
3245
|
-
wallet = self.wallet_ask(
|
3246
|
-
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
3247
|
-
)
|
3248
|
-
|
3249
|
-
return self._run_command(
|
3250
|
-
stake.show(
|
3251
|
-
wallet,
|
3252
|
-
subtensor,
|
3253
|
-
all_wallets,
|
3254
|
-
reuse_last,
|
3255
|
-
html_output,
|
3256
|
-
not self.config.get("use_cache", True),
|
3257
|
-
)
|
3258
|
-
)
|
3259
|
-
|
3260
|
-
def stake_add(
|
3261
|
-
self,
|
3262
|
-
stake_all: bool = typer.Option(
|
3263
|
-
False,
|
3264
|
-
"--all-tokens",
|
3265
|
-
"--all",
|
3266
|
-
"-a",
|
3267
|
-
help="When set, the command stakes all the available TAO from the coldkey.",
|
3268
|
-
),
|
3269
|
-
amount: float = typer.Option(
|
3270
|
-
0.0, "--amount", help="The amount of TAO to stake"
|
3271
|
-
),
|
3272
|
-
max_stake: float = typer.Option(
|
3273
|
-
0.0,
|
3274
|
-
"--max-stake",
|
3275
|
-
"-m",
|
3276
|
-
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.",
|
3277
|
-
),
|
3278
|
-
hotkey_ss58_address: str = typer.Option(
|
3279
|
-
"",
|
3280
|
-
help="The ss58 address of the hotkey to stake to.",
|
3281
|
-
),
|
3282
|
-
include_hotkeys: str = typer.Option(
|
3283
|
-
"",
|
3284
|
-
"--include-hotkeys",
|
3285
|
-
"-in",
|
3286
|
-
help="Specifies hotkeys by name or ss58 address to stake to. For example, `-in hk1,hk2`",
|
3287
|
-
),
|
3288
|
-
exclude_hotkeys: str = typer.Option(
|
3289
|
-
"",
|
3290
|
-
"--exclude-hotkeys",
|
3291
|
-
"-ex",
|
3292
|
-
help="Specifies hotkeys by name or ss58 address to not to stake to (use this option only with `--all-hotkeys`)"
|
3293
|
-
" i.e. `--all-hotkeys -ex hk3,hk4`",
|
3294
|
-
),
|
3295
|
-
all_hotkeys: bool = typer.Option(
|
3296
|
-
False,
|
3297
|
-
help="When set, this command stakes to all hotkeys associated with the wallet. Do not use if specifying "
|
3298
|
-
"hotkeys in `--include-hotkeys`.",
|
3299
|
-
),
|
3300
|
-
wallet_name: str = Options.wallet_name,
|
3301
|
-
wallet_path: str = Options.wallet_path,
|
3302
|
-
wallet_hotkey: str = Options.wallet_hotkey,
|
3303
|
-
network: Optional[list[str]] = Options.network,
|
3304
|
-
prompt: bool = Options.prompt,
|
3305
|
-
quiet: bool = Options.quiet,
|
3306
|
-
verbose: bool = Options.verbose,
|
3307
|
-
):
|
3308
|
-
"""
|
3309
|
-
Stake TAO to one or more hotkeys associated with the user's coldkey.
|
3310
|
-
|
3311
|
-
This command is used by a subnet validator to stake to their own hotkey. Compare this command with "btcli root delegate" that is typically run by a TAO holder to delegate their TAO to a delegate's hotkey.
|
3312
|
-
|
3313
|
-
This command is used by a subnet validator to allocate stake TAO to their different hotkeys, securing their position and influence on the network.
|
3314
|
-
|
3315
|
-
EXAMPLE
|
3316
|
-
|
3317
|
-
[green]$[/green] btcli stake add --amount 100 --wallet-name <my_wallet> --wallet-hotkey <my_hotkey>
|
3318
|
-
"""
|
3319
|
-
self.verbosity_handler(quiet, verbose)
|
3320
3136
|
|
3321
|
-
if
|
3322
|
-
print_error(
|
3323
|
-
"Cannot specify an amount and 'stake-all'. Choose one or the other."
|
3324
|
-
)
|
3137
|
+
if unstake_all and unstake_all_alpha:
|
3138
|
+
print_error("Cannot specify both unstake-all and unstake-all-alpha.")
|
3325
3139
|
raise typer.Exit()
|
3326
3140
|
|
3327
|
-
if not
|
3328
|
-
|
3141
|
+
if not interactive and not unstake_all and not unstake_all_alpha:
|
3142
|
+
netuid = get_optional_netuid(netuid, all_netuids)
|
3143
|
+
if all_hotkeys and include_hotkeys:
|
3144
|
+
print_error(
|
3145
|
+
"You have specified hotkeys to include and also the `--all-hotkeys` flag. The flag"
|
3146
|
+
" should only be used standalone (to use all hotkeys) or with `--exclude-hotkeys`."
|
3147
|
+
)
|
3148
|
+
raise typer.Exit()
|
3329
3149
|
|
3330
|
-
|
3331
|
-
|
3150
|
+
if include_hotkeys and exclude_hotkeys:
|
3151
|
+
print_error(
|
3152
|
+
"You have specified both including and excluding hotkeys options. Select one or the other."
|
3153
|
+
)
|
3332
3154
|
raise typer.Exit()
|
3333
3155
|
|
3334
|
-
|
3335
|
-
|
3336
|
-
|
3337
|
-
|
3338
|
-
|
3339
|
-
raise typer.Exit()
|
3156
|
+
if unstake_all and amount:
|
3157
|
+
print_error(
|
3158
|
+
"Cannot specify both a specific amount and 'unstake-all'. Choose one or the other."
|
3159
|
+
)
|
3160
|
+
raise typer.Exit()
|
3340
3161
|
|
3341
|
-
|
3342
|
-
|
3343
|
-
|
3344
|
-
)
|
3345
|
-
raise typer.Exit()
|
3162
|
+
if amount and amount <= 0:
|
3163
|
+
print_error(f"You entered an incorrect unstake amount: {amount}")
|
3164
|
+
raise typer.Exit()
|
3346
3165
|
|
3347
3166
|
if (
|
3348
3167
|
not wallet_hotkey
|
3168
|
+
and not hotkey_ss58_address
|
3349
3169
|
and not all_hotkeys
|
3350
3170
|
and not include_hotkeys
|
3351
|
-
and not
|
3171
|
+
and not interactive
|
3172
|
+
and not unstake_all
|
3173
|
+
and not unstake_all_alpha
|
3352
3174
|
):
|
3175
|
+
if not wallet_name:
|
3176
|
+
wallet_name = Prompt.ask(
|
3177
|
+
"Enter the [blue]wallet name[/blue]",
|
3178
|
+
default=self.config.get("wallet_name") or defaults.wallet.name,
|
3179
|
+
)
|
3353
3180
|
hotkey_or_ss58 = Prompt.ask(
|
3354
|
-
"Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to
|
3181
|
+
"Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake from [dim](or Press Enter to view existing staked hotkeys)[/dim]",
|
3355
3182
|
)
|
3356
|
-
if
|
3183
|
+
if hotkey_or_ss58 == "":
|
3184
|
+
wallet = self.wallet_ask(
|
3185
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
3186
|
+
)
|
3187
|
+
interactive = True
|
3188
|
+
elif is_valid_ss58_address(hotkey_or_ss58):
|
3357
3189
|
hotkey_ss58_address = hotkey_or_ss58
|
3358
3190
|
wallet = self.wallet_ask(
|
3359
|
-
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
3191
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
3360
3192
|
)
|
3361
3193
|
else:
|
3362
3194
|
wallet_hotkey = hotkey_or_ss58
|
@@ -3364,214 +3196,478 @@ class CLIManager:
|
|
3364
3196
|
wallet_name,
|
3365
3197
|
wallet_path,
|
3366
3198
|
wallet_hotkey,
|
3367
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
3199
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
3368
3200
|
validate=WV.WALLET_AND_HOTKEY,
|
3369
3201
|
)
|
3370
3202
|
|
3371
|
-
elif
|
3203
|
+
elif unstake_all or unstake_all_alpha:
|
3204
|
+
if not wallet_name:
|
3205
|
+
wallet_name = Prompt.ask(
|
3206
|
+
"Enter the [blue]wallet name[/blue]",
|
3207
|
+
default=self.config.get("wallet_name") or defaults.wallet.name,
|
3208
|
+
)
|
3209
|
+
if include_hotkeys:
|
3210
|
+
if len(include_hotkeys) > 1:
|
3211
|
+
print_error("Cannot unstake_all from multiple hotkeys at once.")
|
3212
|
+
raise typer.Exit()
|
3213
|
+
elif is_valid_ss58_address(include_hotkeys[0]):
|
3214
|
+
hotkey_ss58_address = include_hotkeys[0]
|
3215
|
+
else:
|
3216
|
+
print_error("Invalid hotkey ss58 address.")
|
3217
|
+
raise typer.Exit()
|
3218
|
+
else:
|
3219
|
+
hotkey_or_ss58 = Prompt.ask(
|
3220
|
+
"Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake all from",
|
3221
|
+
default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
|
3222
|
+
)
|
3223
|
+
if is_valid_ss58_address(hotkey_or_ss58):
|
3224
|
+
hotkey_ss58_address = hotkey_or_ss58
|
3225
|
+
wallet = self.wallet_ask(
|
3226
|
+
wallet_name,
|
3227
|
+
wallet_path,
|
3228
|
+
wallet_hotkey,
|
3229
|
+
ask_for=[WO.NAME, WO.PATH],
|
3230
|
+
)
|
3231
|
+
else:
|
3232
|
+
wallet_hotkey = hotkey_or_ss58
|
3233
|
+
wallet = self.wallet_ask(
|
3234
|
+
wallet_name,
|
3235
|
+
wallet_path,
|
3236
|
+
wallet_hotkey,
|
3237
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
3238
|
+
validate=WV.WALLET_AND_HOTKEY,
|
3239
|
+
)
|
3240
|
+
return self._run_command(
|
3241
|
+
remove_stake.unstake_all(
|
3242
|
+
wallet=wallet,
|
3243
|
+
subtensor=self.initialize_chain(network),
|
3244
|
+
hotkey_ss58_address=hotkey_ss58_address,
|
3245
|
+
unstake_all_alpha=unstake_all_alpha,
|
3246
|
+
prompt=prompt,
|
3247
|
+
)
|
3248
|
+
)
|
3249
|
+
elif (
|
3250
|
+
all_hotkeys
|
3251
|
+
or include_hotkeys
|
3252
|
+
or exclude_hotkeys
|
3253
|
+
or hotkey_ss58_address
|
3254
|
+
or interactive
|
3255
|
+
or unstake_all
|
3256
|
+
or unstake_all_alpha
|
3257
|
+
):
|
3372
3258
|
wallet = self.wallet_ask(
|
3373
|
-
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
3259
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
3374
3260
|
)
|
3375
3261
|
else:
|
3376
3262
|
wallet = self.wallet_ask(
|
3377
3263
|
wallet_name,
|
3378
3264
|
wallet_path,
|
3379
3265
|
wallet_hotkey,
|
3380
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
3266
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
3381
3267
|
validate=WV.WALLET_AND_HOTKEY,
|
3382
3268
|
)
|
3383
3269
|
|
3384
3270
|
if include_hotkeys:
|
3385
|
-
|
3271
|
+
include_hotkeys = parse_to_list(
|
3386
3272
|
include_hotkeys,
|
3387
3273
|
str,
|
3388
|
-
"Hotkeys must be a comma-separated list of ss58s or
|
3389
|
-
|
3274
|
+
"Hotkeys must be a comma-separated list of ss58s or names, e.g., `--include-hotkeys hk1,hk2`.",
|
3275
|
+
is_ss58=False,
|
3390
3276
|
)
|
3391
|
-
else:
|
3392
|
-
included_hotkeys = []
|
3393
3277
|
|
3394
3278
|
if exclude_hotkeys:
|
3395
|
-
|
3279
|
+
exclude_hotkeys = parse_to_list(
|
3396
3280
|
exclude_hotkeys,
|
3397
3281
|
str,
|
3398
|
-
"Hotkeys must be a comma-separated list of ss58s or
|
3399
|
-
|
3282
|
+
"Hotkeys must be a comma-separated list of ss58s or names, e.g., `--exclude-hotkeys hk3,hk4`.",
|
3283
|
+
is_ss58=False,
|
3400
3284
|
)
|
3401
|
-
else:
|
3402
|
-
excluded_hotkeys = []
|
3403
3285
|
|
3404
3286
|
return self._run_command(
|
3405
|
-
|
3406
|
-
wallet,
|
3407
|
-
self.initialize_chain(network),
|
3408
|
-
|
3409
|
-
|
3410
|
-
|
3411
|
-
|
3412
|
-
|
3413
|
-
|
3414
|
-
|
3415
|
-
|
3287
|
+
remove_stake.unstake(
|
3288
|
+
wallet=wallet,
|
3289
|
+
subtensor=self.initialize_chain(network),
|
3290
|
+
hotkey_ss58_address=hotkey_ss58_address,
|
3291
|
+
all_hotkeys=all_hotkeys,
|
3292
|
+
include_hotkeys=include_hotkeys,
|
3293
|
+
exclude_hotkeys=exclude_hotkeys,
|
3294
|
+
amount=amount,
|
3295
|
+
prompt=prompt,
|
3296
|
+
interactive=interactive,
|
3297
|
+
netuid=netuid,
|
3298
|
+
safe_staking=safe_staking,
|
3299
|
+
rate_tolerance=rate_tolerance,
|
3300
|
+
allow_partial_stake=allow_partial_stake,
|
3416
3301
|
)
|
3417
3302
|
)
|
3418
3303
|
|
3419
|
-
def
|
3304
|
+
def stake_move(
|
3420
3305
|
self,
|
3421
3306
|
network: Optional[list[str]] = Options.network,
|
3422
|
-
wallet_name
|
3423
|
-
wallet_path
|
3424
|
-
wallet_hotkey
|
3425
|
-
|
3426
|
-
|
3427
|
-
"--unstake-all",
|
3428
|
-
"--all",
|
3429
|
-
help="When set, this commmand unstakes all staked TAO from the specified hotkeys.",
|
3430
|
-
),
|
3431
|
-
amount: float = typer.Option(
|
3432
|
-
0.0, "--amount", "-a", help="The amount of TAO to unstake."
|
3307
|
+
wallet_name=Options.wallet_name,
|
3308
|
+
wallet_path=Options.wallet_path,
|
3309
|
+
wallet_hotkey=Options.wallet_hotkey,
|
3310
|
+
origin_netuid: Optional[int] = typer.Option(
|
3311
|
+
None, "--origin-netuid", help="Origin netuid"
|
3433
3312
|
),
|
3434
|
-
|
3435
|
-
"",
|
3436
|
-
help="The ss58 address of the hotkey to unstake from.",
|
3437
|
-
),
|
3438
|
-
keep_stake: float = typer.Option(
|
3439
|
-
0.0,
|
3440
|
-
"--keep-stake",
|
3441
|
-
"--keep",
|
3442
|
-
help="Sets the maximum amount of TAO to remain staked in each hotkey.",
|
3313
|
+
destination_netuid: Optional[int] = typer.Option(
|
3314
|
+
None, "--dest-netuid", help="Destination netuid"
|
3443
3315
|
),
|
3444
|
-
|
3445
|
-
"",
|
3446
|
-
"--include-hotkeys",
|
3447
|
-
"-in",
|
3448
|
-
help="Specifies the hotkeys by name or ss58 address to unstake from. For example, `-in hk1,hk2`",
|
3316
|
+
destination_hotkey: Optional[str] = typer.Option(
|
3317
|
+
None, "--dest-ss58", "--dest", help="Destination hotkey", prompt=False
|
3449
3318
|
),
|
3450
|
-
|
3451
|
-
|
3452
|
-
"--
|
3453
|
-
"
|
3454
|
-
|
3455
|
-
" i.e. `--all-hotkeys -ex hk3,hk4`",
|
3319
|
+
amount: float = typer.Option(
|
3320
|
+
None,
|
3321
|
+
"--amount",
|
3322
|
+
help="The amount of TAO to stake",
|
3323
|
+
prompt=False,
|
3456
3324
|
),
|
3457
|
-
|
3458
|
-
False,
|
3459
|
-
help="When set, this command unstakes from all the hotkeys associated with the wallet. Do not use if specifying "
|
3460
|
-
"hotkeys in `--include-hotkeys`.",
|
3325
|
+
stake_all: bool = typer.Option(
|
3326
|
+
False, "--stake-all", "--all", help="Stake all", prompt=False
|
3461
3327
|
),
|
3462
3328
|
prompt: bool = Options.prompt,
|
3463
|
-
quiet: bool = Options.quiet,
|
3464
|
-
verbose: bool = Options.verbose,
|
3465
3329
|
):
|
3466
3330
|
"""
|
3467
|
-
|
3331
|
+
Move staked TAO between hotkeys while keeping the same coldkey ownership.
|
3468
3332
|
|
3469
|
-
This command
|
3333
|
+
This command allows you to:
|
3334
|
+
- Move stake from one hotkey to another hotkey
|
3335
|
+
- Move stake between different subnets
|
3336
|
+
- Keep the same coldkey ownership
|
3470
3337
|
|
3471
|
-
|
3338
|
+
You can specify:
|
3339
|
+
- The origin subnet (--origin-netuid)
|
3340
|
+
- The destination subnet (--dest-netuid)
|
3341
|
+
- The destination hotkey (--dest-hotkey)
|
3342
|
+
- The amount to move (--amount)
|
3472
3343
|
|
3473
|
-
|
3344
|
+
If no arguments are provided, an interactive selection menu will be shown.
|
3474
3345
|
|
3475
|
-
|
3476
|
-
"""
|
3477
|
-
self.verbosity_handler(quiet, verbose)
|
3346
|
+
EXAMPLE
|
3478
3347
|
|
3479
|
-
|
3480
|
-
|
3481
|
-
|
3482
|
-
|
3348
|
+
[green]$[/green] btcli stake move
|
3349
|
+
"""
|
3350
|
+
console.print(
|
3351
|
+
"[dim]This command moves stake from one hotkey to another hotkey while keeping the same coldkey.[/dim]"
|
3352
|
+
)
|
3353
|
+
if not destination_hotkey:
|
3354
|
+
dest_wallet_or_ss58 = Prompt.ask(
|
3355
|
+
"Enter the [blue]destination wallet[/blue] where destination hotkey is located or [blue]ss58 address[/blue]"
|
3483
3356
|
)
|
3484
|
-
|
3357
|
+
if is_valid_ss58_address(dest_wallet_or_ss58):
|
3358
|
+
destination_hotkey = dest_wallet_or_ss58
|
3359
|
+
else:
|
3360
|
+
dest_wallet = self.wallet_ask(
|
3361
|
+
dest_wallet_or_ss58,
|
3362
|
+
wallet_path,
|
3363
|
+
None,
|
3364
|
+
ask_for=[WO.NAME, WO.PATH],
|
3365
|
+
validate=WV.WALLET,
|
3366
|
+
)
|
3367
|
+
destination_hotkey = Prompt.ask(
|
3368
|
+
"Enter the [blue]destination hotkey[/blue] name",
|
3369
|
+
default=dest_wallet.hotkey_str,
|
3370
|
+
)
|
3371
|
+
destination_wallet = self.wallet_ask(
|
3372
|
+
dest_wallet_or_ss58,
|
3373
|
+
wallet_path,
|
3374
|
+
destination_hotkey,
|
3375
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
3376
|
+
validate=WV.WALLET_AND_HOTKEY,
|
3377
|
+
)
|
3378
|
+
destination_hotkey = destination_wallet.hotkey.ss58_address
|
3379
|
+
else:
|
3380
|
+
if is_valid_ss58_address(destination_hotkey):
|
3381
|
+
destination_hotkey = destination_hotkey
|
3382
|
+
else:
|
3383
|
+
print_error(
|
3384
|
+
"Invalid destination hotkey ss58 address. Please enter a valid ss58 address or wallet name."
|
3385
|
+
)
|
3386
|
+
raise typer.Exit()
|
3485
3387
|
|
3486
|
-
if
|
3487
|
-
|
3488
|
-
"
|
3388
|
+
if not wallet_name:
|
3389
|
+
wallet_name = Prompt.ask(
|
3390
|
+
"Enter the [blue]origin wallet name[/blue]",
|
3391
|
+
default=self.config.get("wallet_name") or defaults.wallet.name,
|
3489
3392
|
)
|
3490
|
-
|
3393
|
+
wallet = self.wallet_ask(
|
3394
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
3395
|
+
)
|
3491
3396
|
|
3492
|
-
|
3493
|
-
|
3494
|
-
|
3397
|
+
interactive_selection = False
|
3398
|
+
if not wallet_hotkey:
|
3399
|
+
origin_hotkey = Prompt.ask(
|
3400
|
+
"Enter the [blue]origin hotkey[/blue] name or "
|
3401
|
+
"[blue]ss58 address[/blue] where the stake will be moved from "
|
3402
|
+
"[dim](or Press Enter to view existing stakes)[/dim]"
|
3495
3403
|
)
|
3496
|
-
|
3497
|
-
|
3498
|
-
if not unstake_all and not amount and not keep_stake:
|
3499
|
-
amount = FloatPrompt.ask("Amount to [blue]unstake (TAO τ)[/blue]")
|
3404
|
+
if origin_hotkey == "":
|
3405
|
+
interactive_selection = True
|
3500
3406
|
|
3501
|
-
|
3502
|
-
|
3503
|
-
|
3504
|
-
|
3505
|
-
if (
|
3506
|
-
not wallet_hotkey
|
3507
|
-
and not hotkey_ss58_address
|
3508
|
-
and not all_hotkeys
|
3509
|
-
and not include_hotkeys
|
3510
|
-
):
|
3511
|
-
hotkey_or_ss58 = Prompt.ask(
|
3512
|
-
"Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake from"
|
3513
|
-
)
|
3514
|
-
if is_valid_ss58_address(hotkey_or_ss58):
|
3515
|
-
hotkey_ss58_address = hotkey_or_ss58
|
3407
|
+
elif is_valid_ss58_address(origin_hotkey):
|
3408
|
+
origin_hotkey = origin_hotkey
|
3409
|
+
else:
|
3516
3410
|
wallet = self.wallet_ask(
|
3517
|
-
wallet_name,
|
3411
|
+
wallet_name,
|
3412
|
+
wallet_path,
|
3413
|
+
origin_hotkey,
|
3414
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
3415
|
+
validate=WV.WALLET_AND_HOTKEY,
|
3518
3416
|
)
|
3417
|
+
origin_hotkey = wallet.hotkey.ss58_address
|
3418
|
+
else:
|
3419
|
+
if is_valid_ss58_address(wallet_hotkey):
|
3420
|
+
origin_hotkey = wallet_hotkey
|
3519
3421
|
else:
|
3520
|
-
wallet_hotkey = hotkey_or_ss58
|
3521
3422
|
wallet = self.wallet_ask(
|
3522
3423
|
wallet_name,
|
3523
3424
|
wallet_path,
|
3524
3425
|
wallet_hotkey,
|
3525
|
-
ask_for=[
|
3426
|
+
ask_for=[],
|
3526
3427
|
validate=WV.WALLET_AND_HOTKEY,
|
3527
3428
|
)
|
3429
|
+
origin_hotkey = wallet.hotkey.ss58_address
|
3528
3430
|
|
3529
|
-
|
3530
|
-
|
3531
|
-
|
3431
|
+
if not interactive_selection:
|
3432
|
+
if origin_netuid is None:
|
3433
|
+
origin_netuid = IntPrompt.ask(
|
3434
|
+
"Enter the [blue]origin subnet[/blue] (netuid) to move stake from"
|
3435
|
+
)
|
3436
|
+
|
3437
|
+
if destination_netuid is None:
|
3438
|
+
destination_netuid = IntPrompt.ask(
|
3439
|
+
"Enter the [blue]destination subnet[/blue] (netuid) to move stake to"
|
3440
|
+
)
|
3441
|
+
|
3442
|
+
return self._run_command(
|
3443
|
+
move_stake.move_stake(
|
3444
|
+
subtensor=self.initialize_chain(network),
|
3445
|
+
wallet=wallet,
|
3446
|
+
origin_netuid=origin_netuid,
|
3447
|
+
origin_hotkey=origin_hotkey,
|
3448
|
+
destination_netuid=destination_netuid,
|
3449
|
+
destination_hotkey=destination_hotkey,
|
3450
|
+
amount=amount,
|
3451
|
+
stake_all=stake_all,
|
3452
|
+
interactive_selection=interactive_selection,
|
3453
|
+
prompt=prompt,
|
3454
|
+
)
|
3455
|
+
)
|
3456
|
+
|
3457
|
+
def stake_transfer(
|
3458
|
+
self,
|
3459
|
+
network: Optional[list[str]] = Options.network,
|
3460
|
+
wallet_name: Optional[str] = Options.wallet_name,
|
3461
|
+
wallet_path: Optional[str] = Options.wallet_path,
|
3462
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
3463
|
+
origin_netuid: Optional[int] = typer.Option(
|
3464
|
+
None,
|
3465
|
+
"--origin-netuid",
|
3466
|
+
help="The netuid to transfer stake from",
|
3467
|
+
),
|
3468
|
+
dest_netuid: Optional[int] = typer.Option(
|
3469
|
+
None,
|
3470
|
+
"--dest-netuid",
|
3471
|
+
help="The netuid to transfer stake to",
|
3472
|
+
),
|
3473
|
+
dest_ss58: Optional[str] = typer.Option(
|
3474
|
+
None,
|
3475
|
+
"--dest-ss58",
|
3476
|
+
"--dest",
|
3477
|
+
"--dest-coldkey",
|
3478
|
+
help="The destination wallet name or SS58 address to transfer stake to",
|
3479
|
+
),
|
3480
|
+
amount: float = typer.Option(
|
3481
|
+
None,
|
3482
|
+
"--amount",
|
3483
|
+
"-a",
|
3484
|
+
help="Amount of stake to transfer",
|
3485
|
+
),
|
3486
|
+
prompt: bool = Options.prompt,
|
3487
|
+
quiet: bool = Options.quiet,
|
3488
|
+
verbose: bool = Options.verbose,
|
3489
|
+
):
|
3490
|
+
"""
|
3491
|
+
Transfer stake between coldkeys while keeping the same hotkey ownership.
|
3492
|
+
|
3493
|
+
This command allows you to:
|
3494
|
+
- Transfer stake from one coldkey to another coldkey
|
3495
|
+
- Keep the same hotkey ownership
|
3496
|
+
- Transfer stake between different subnets
|
3497
|
+
|
3498
|
+
You can specify:
|
3499
|
+
- The origin subnet (--origin-netuid)
|
3500
|
+
- The destination subnet (--dest-netuid)
|
3501
|
+
- The destination wallet/address (--dest)
|
3502
|
+
- The amount to transfer (--amount)
|
3503
|
+
|
3504
|
+
If no arguments are provided, an interactive selection menu will be shown.
|
3505
|
+
|
3506
|
+
EXAMPLE
|
3507
|
+
|
3508
|
+
Transfer 100 TAO from subnet 1 to subnet 2:
|
3509
|
+
[green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest wallet2 --amount 100
|
3510
|
+
|
3511
|
+
Using SS58 address:
|
3512
|
+
[green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest 5FrLxJsyJ5x9n2rmxFwosFraxFCKcXZDngEP9H7qjkKgHLcK --amount 100
|
3513
|
+
"""
|
3514
|
+
console.print(
|
3515
|
+
"[dim]This command transfers stake from one coldkey to another while keeping the same hotkey.[/dim]"
|
3516
|
+
)
|
3517
|
+
self.verbosity_handler(quiet, verbose)
|
3518
|
+
|
3519
|
+
wallet = self.wallet_ask(
|
3520
|
+
wallet_name,
|
3521
|
+
wallet_path,
|
3522
|
+
wallet_hotkey,
|
3523
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
3524
|
+
validate=WV.WALLET_AND_HOTKEY,
|
3525
|
+
)
|
3526
|
+
|
3527
|
+
if not dest_ss58:
|
3528
|
+
dest_ss58 = Prompt.ask(
|
3529
|
+
"Enter the [blue]destination wallet name[/blue] or [blue]coldkey SS58 address[/blue]"
|
3532
3530
|
)
|
3533
3531
|
|
3532
|
+
if is_valid_ss58_address(dest_ss58):
|
3533
|
+
dest_ss58 = dest_ss58
|
3534
3534
|
else:
|
3535
|
-
|
3536
|
-
|
3535
|
+
dest_wallet = self.wallet_ask(
|
3536
|
+
dest_ss58,
|
3537
3537
|
wallet_path,
|
3538
|
-
|
3539
|
-
ask_for=[WO.NAME, WO.
|
3540
|
-
validate=WV.
|
3538
|
+
None,
|
3539
|
+
ask_for=[WO.NAME, WO.PATH],
|
3540
|
+
validate=WV.WALLET,
|
3541
3541
|
)
|
3542
|
+
dest_ss58 = dest_wallet.coldkeypub.ss58_address
|
3542
3543
|
|
3543
|
-
|
3544
|
-
|
3545
|
-
|
3546
|
-
str,
|
3547
|
-
"Hotkeys must be a comma-separated list of ss58s or hotkey names, e.g., "
|
3548
|
-
"`--include-hotkeys 5Grw....,5Grw....`.",
|
3549
|
-
)
|
3544
|
+
interactive_selection = False
|
3545
|
+
if origin_netuid is None and dest_netuid is None and not amount:
|
3546
|
+
interactive_selection = True
|
3550
3547
|
else:
|
3551
|
-
|
3548
|
+
if origin_netuid is None:
|
3549
|
+
origin_netuid = IntPrompt.ask(
|
3550
|
+
"Enter the [blue]origin subnet[/blue] (netuid)"
|
3551
|
+
)
|
3552
|
+
if not amount:
|
3553
|
+
amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to transfer")
|
3552
3554
|
|
3553
|
-
|
3554
|
-
|
3555
|
-
|
3556
|
-
|
3557
|
-
|
3558
|
-
|
3555
|
+
if dest_netuid is None:
|
3556
|
+
dest_netuid = IntPrompt.ask(
|
3557
|
+
"Enter the [blue]destination subnet[/blue] (netuid)"
|
3558
|
+
)
|
3559
|
+
|
3560
|
+
return self._run_command(
|
3561
|
+
move_stake.transfer_stake(
|
3562
|
+
wallet=wallet,
|
3563
|
+
subtensor=self.initialize_chain(network),
|
3564
|
+
origin_netuid=origin_netuid,
|
3565
|
+
dest_netuid=dest_netuid,
|
3566
|
+
dest_coldkey_ss58=dest_ss58,
|
3567
|
+
amount=amount,
|
3568
|
+
interactive_selection=interactive_selection,
|
3569
|
+
prompt=prompt,
|
3559
3570
|
)
|
3571
|
+
)
|
3572
|
+
|
3573
|
+
def stake_swap(
|
3574
|
+
self,
|
3575
|
+
network: Optional[list[str]] = Options.network,
|
3576
|
+
wallet_name: Optional[str] = Options.wallet_name,
|
3577
|
+
wallet_path: Optional[str] = Options.wallet_path,
|
3578
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
3579
|
+
origin_netuid: Optional[int] = typer.Option(
|
3580
|
+
None,
|
3581
|
+
"--origin-netuid",
|
3582
|
+
"-o",
|
3583
|
+
"--origin",
|
3584
|
+
help="The netuid to swap stake from",
|
3585
|
+
),
|
3586
|
+
dest_netuid: Optional[int] = typer.Option(
|
3587
|
+
None,
|
3588
|
+
"--dest-netuid",
|
3589
|
+
"-d",
|
3590
|
+
"--dest",
|
3591
|
+
help="The netuid to swap stake to",
|
3592
|
+
),
|
3593
|
+
amount: float = typer.Option(
|
3594
|
+
None,
|
3595
|
+
"--amount",
|
3596
|
+
"-a",
|
3597
|
+
help="Amount of stake to swap",
|
3598
|
+
),
|
3599
|
+
swap_all: bool = typer.Option(
|
3600
|
+
False,
|
3601
|
+
"--swap-all",
|
3602
|
+
"--all",
|
3603
|
+
help="Swap all available stake",
|
3604
|
+
),
|
3605
|
+
prompt: bool = Options.prompt,
|
3606
|
+
wait_for_inclusion: bool = Options.wait_for_inclusion,
|
3607
|
+
wait_for_finalization: bool = Options.wait_for_finalization,
|
3608
|
+
quiet: bool = Options.quiet,
|
3609
|
+
verbose: bool = Options.verbose,
|
3610
|
+
):
|
3611
|
+
"""
|
3612
|
+
Swap stake between different subnets while keeping the same coldkey-hotkey pair ownership.
|
3613
|
+
|
3614
|
+
This command allows you to:
|
3615
|
+
- Move stake from one subnet to another subnet
|
3616
|
+
- Keep the same coldkey ownership
|
3617
|
+
- Keep the same hotkey ownership
|
3618
|
+
|
3619
|
+
You can specify:
|
3620
|
+
- The origin subnet (--origin-netuid)
|
3621
|
+
- The destination subnet (--dest-netuid)
|
3622
|
+
- The amount to swap (--amount)
|
3623
|
+
|
3624
|
+
If no arguments are provided, an interactive selection menu will be shown.
|
3625
|
+
|
3626
|
+
EXAMPLE
|
3627
|
+
|
3628
|
+
Swap 100 TAO from subnet 1 to subnet 2:
|
3629
|
+
[green]$[/green] btcli stake swap --wallet-name default --wallet-hotkey default --origin-netuid 1 --dest-netuid 2 --amount 100
|
3630
|
+
"""
|
3631
|
+
console.print(
|
3632
|
+
"[dim]This command moves stake from one subnet to another subnet while keeping the same coldkey-hotkey pair.[/dim]"
|
3633
|
+
)
|
3634
|
+
self.verbosity_handler(quiet, verbose)
|
3635
|
+
|
3636
|
+
wallet = self.wallet_ask(
|
3637
|
+
wallet_name,
|
3638
|
+
wallet_path,
|
3639
|
+
wallet_hotkey,
|
3640
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
3641
|
+
validate=WV.WALLET_AND_HOTKEY,
|
3642
|
+
)
|
3643
|
+
|
3644
|
+
interactive_selection = False
|
3645
|
+
if origin_netuid is None and dest_netuid is None and not amount:
|
3646
|
+
interactive_selection = True
|
3560
3647
|
else:
|
3561
|
-
|
3648
|
+
if origin_netuid is None:
|
3649
|
+
origin_netuid = IntPrompt.ask(
|
3650
|
+
"Enter the [blue]origin subnet[/blue] (netuid)"
|
3651
|
+
)
|
3652
|
+
if dest_netuid is None:
|
3653
|
+
dest_netuid = IntPrompt.ask(
|
3654
|
+
"Enter the [blue]destination subnet[/blue] (netuid)"
|
3655
|
+
)
|
3656
|
+
if not amount and not swap_all:
|
3657
|
+
amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to swap")
|
3562
3658
|
|
3563
3659
|
return self._run_command(
|
3564
|
-
|
3565
|
-
wallet,
|
3566
|
-
self.initialize_chain(network),
|
3567
|
-
|
3568
|
-
|
3569
|
-
|
3570
|
-
|
3571
|
-
|
3572
|
-
|
3573
|
-
|
3574
|
-
|
3660
|
+
move_stake.swap_stake(
|
3661
|
+
wallet=wallet,
|
3662
|
+
subtensor=self.initialize_chain(network),
|
3663
|
+
origin_netuid=origin_netuid,
|
3664
|
+
destination_netuid=dest_netuid,
|
3665
|
+
amount=amount,
|
3666
|
+
swap_all=swap_all,
|
3667
|
+
interactive_selection=interactive_selection,
|
3668
|
+
prompt=prompt,
|
3669
|
+
wait_for_inclusion=wait_for_inclusion,
|
3670
|
+
wait_for_finalization=wait_for_finalization,
|
3575
3671
|
)
|
3576
3672
|
)
|
3577
3673
|
|
@@ -3611,7 +3707,7 @@ class CLIManager:
|
|
3611
3707
|
wallet_name,
|
3612
3708
|
wallet_path,
|
3613
3709
|
wallet_hotkey,
|
3614
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
3710
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
3615
3711
|
validate=WV.WALLET_AND_HOTKEY,
|
3616
3712
|
)
|
3617
3713
|
|
@@ -3642,23 +3738,12 @@ class CLIManager:
|
|
3642
3738
|
wallet_hotkey: str = Options.wallet_hotkey,
|
3643
3739
|
wallet_path: str = Options.wallet_path,
|
3644
3740
|
network: Optional[list[str]] = Options.network,
|
3645
|
-
netuid: Optional[int] =
|
3646
|
-
|
3647
|
-
help="The netuid of the subnet, (e.g. 4)",
|
3648
|
-
prompt=False,
|
3649
|
-
),
|
3650
|
-
all_netuids: bool = typer.Option(
|
3651
|
-
False,
|
3652
|
-
"--all-netuids",
|
3653
|
-
"--all",
|
3654
|
-
"--allnetuids",
|
3655
|
-
help="When this flag is used it sets child hotkeys on all subnets.",
|
3656
|
-
),
|
3741
|
+
netuid: Optional[int] = Options.netuid_not_req,
|
3742
|
+
all_netuids: bool = Options.all_netuids,
|
3657
3743
|
proportions: list[float] = typer.Option(
|
3658
3744
|
[],
|
3659
3745
|
"--proportions",
|
3660
3746
|
"--prop",
|
3661
|
-
"-p",
|
3662
3747
|
help="Enter the stake weight proportions for the child hotkeys (sum should be less than or equal to 1)",
|
3663
3748
|
prompt=False,
|
3664
3749
|
),
|
@@ -3678,17 +3763,10 @@ class CLIManager:
|
|
3678
3763
|
EXAMPLE
|
3679
3764
|
|
3680
3765
|
[green]$[/green] btcli stake child set -c 5FCL3gmjtQV4xxxxuEPEFQVhyyyyqYgNwX7drFLw7MSdBnxP -c 5Hp5dxxxxtGg7pu8dN2btyyyyVA1vELmM9dy8KQv3LxV8PA7 --hotkey default --netuid 1 -p 0.3 -p 0.7
|
3681
|
-
"""
|
3682
|
-
self.verbosity_handler(quiet, verbose)
|
3683
|
-
|
3684
|
-
|
3685
|
-
raise typer.Exit()
|
3686
|
-
if all_netuids:
|
3687
|
-
netuid = None
|
3688
|
-
elif not netuid:
|
3689
|
-
netuid = IntPrompt.ask(
|
3690
|
-
"Enter a netuid (leave blank for all)", default=None, show_default=True
|
3691
|
-
)
|
3766
|
+
"""
|
3767
|
+
self.verbosity_handler(quiet, verbose)
|
3768
|
+
netuid = get_optional_netuid(netuid, all_netuids)
|
3769
|
+
|
3692
3770
|
children = list_prompt(
|
3693
3771
|
children,
|
3694
3772
|
str,
|
@@ -3896,7 +3974,8 @@ class CLIManager:
|
|
3896
3974
|
|
3897
3975
|
if not param_name or not param_value:
|
3898
3976
|
hyperparams = self._run_command(
|
3899
|
-
sudo.get_hyperparameters(self.initialize_chain(network), netuid)
|
3977
|
+
sudo.get_hyperparameters(self.initialize_chain(network), netuid),
|
3978
|
+
exit_early=False,
|
3900
3979
|
)
|
3901
3980
|
if not hyperparams:
|
3902
3981
|
raise typer.Exit()
|
@@ -3926,11 +4005,11 @@ class CLIManager:
|
|
3926
4005
|
|
3927
4006
|
if not param_value:
|
3928
4007
|
param_value = Prompt.ask(
|
3929
|
-
f"Enter the new value for [
|
4008
|
+
f"Enter the new value for [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{param_name}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] in the VALUE column format"
|
3930
4009
|
)
|
3931
4010
|
|
3932
4011
|
wallet = self.wallet_ask(
|
3933
|
-
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
|
4012
|
+
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
3934
4013
|
)
|
3935
4014
|
return self._run_command(
|
3936
4015
|
sudo.sudo_set_hyperparameter(
|
@@ -3952,8 +4031,6 @@ class CLIManager:
|
|
3952
4031
|
"""
|
3953
4032
|
Shows a list of the hyperparameters for the specified subnet.
|
3954
4033
|
|
3955
|
-
The output of this command is the same as that of `btcli subnets hyperparameters`.
|
3956
|
-
|
3957
4034
|
EXAMPLE
|
3958
4035
|
|
3959
4036
|
[green]$[/green] btcli sudo get --netuid 1
|
@@ -3963,98 +4040,448 @@ class CLIManager:
|
|
3963
4040
|
sudo.get_hyperparameters(self.initialize_chain(network), netuid)
|
3964
4041
|
)
|
3965
4042
|
|
3966
|
-
def
|
4043
|
+
def sudo_senate(
|
3967
4044
|
self,
|
3968
4045
|
network: Optional[list[str]] = Options.network,
|
3969
|
-
reuse_last: bool = Options.reuse_last,
|
3970
|
-
html_output: bool = Options.html_output,
|
3971
4046
|
quiet: bool = Options.quiet,
|
3972
4047
|
verbose: bool = Options.verbose,
|
3973
4048
|
):
|
3974
4049
|
"""
|
3975
|
-
|
4050
|
+
Shows the Senate members of the Bittensor's governance protocol.
|
4051
|
+
|
4052
|
+
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.
|
4053
|
+
|
4054
|
+
EXAMPLE
|
4055
|
+
[green]$[/green] btcli sudo senate
|
4056
|
+
"""
|
4057
|
+
self.verbosity_handler(quiet, verbose)
|
4058
|
+
return self._run_command(sudo.get_senate(self.initialize_chain(network)))
|
3976
4059
|
|
3977
|
-
|
4060
|
+
def sudo_proposals(
|
4061
|
+
self,
|
4062
|
+
network: Optional[list[str]] = Options.network,
|
4063
|
+
quiet: bool = Options.quiet,
|
4064
|
+
verbose: bool = Options.verbose,
|
4065
|
+
):
|
4066
|
+
"""
|
4067
|
+
View active proposals for the senate in the Bittensor's governance protocol.
|
3978
4068
|
|
3979
|
-
|
3980
|
-
- N: The number of neurons (subnet validators and subnet miners) in the subnet.
|
3981
|
-
- MAX_N: The maximum allowed number of neurons in the subnet.
|
3982
|
-
- EMISSION: The percentage of emissions to the subnet as of the last tempo.
|
3983
|
-
- TEMPO: The subnet's tempo, expressed in number of blocks.
|
3984
|
-
- RECYCLE: The recycle register cost for this subnet.
|
3985
|
-
- POW: The proof of work (PoW) difficulty.
|
3986
|
-
- SUDO: The subnet owner's name or the owner's ss58 address.
|
4069
|
+
This command displays the details of ongoing proposals, including proposal hashes, votes, thresholds, and proposal data.
|
3987
4070
|
|
3988
4071
|
EXAMPLE
|
4072
|
+
[green]$[/green] btcli sudo proposals
|
4073
|
+
"""
|
4074
|
+
self.verbosity_handler(quiet, verbose)
|
4075
|
+
return self._run_command(
|
4076
|
+
sudo.proposals(self.initialize_chain(network), verbose)
|
4077
|
+
)
|
3989
4078
|
|
3990
|
-
|
4079
|
+
def sudo_senate_vote(
|
4080
|
+
self,
|
4081
|
+
network: Optional[list[str]] = Options.network,
|
4082
|
+
wallet_name: Optional[str] = Options.wallet_name,
|
4083
|
+
wallet_path: Optional[str] = Options.wallet_path,
|
4084
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
4085
|
+
proposal: str = typer.Option(
|
4086
|
+
None,
|
4087
|
+
"--proposal",
|
4088
|
+
"--proposal-hash",
|
4089
|
+
prompt="Enter the proposal hash",
|
4090
|
+
help="The hash of the proposal to vote on.",
|
4091
|
+
),
|
4092
|
+
prompt: bool = Options.prompt,
|
4093
|
+
quiet: bool = Options.quiet,
|
4094
|
+
verbose: bool = Options.verbose,
|
4095
|
+
vote: bool = typer.Option(
|
4096
|
+
None,
|
4097
|
+
"--vote-aye/--vote-nay",
|
4098
|
+
prompt="Enter y to vote Aye, or enter n to vote Nay",
|
4099
|
+
help="The vote casted on the proposal",
|
4100
|
+
),
|
4101
|
+
):
|
4102
|
+
"""
|
4103
|
+
Cast a vote on an active proposal in Bittensor's governance protocol.
|
4104
|
+
|
4105
|
+
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.
|
4106
|
+
|
4107
|
+
USAGE
|
4108
|
+
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.
|
4109
|
+
|
4110
|
+
EXAMPLE
|
4111
|
+
[green]$[/green] btcli sudo senate_vote --proposal <proposal_hash>
|
3991
4112
|
"""
|
3992
4113
|
self.verbosity_handler(quiet, verbose)
|
3993
|
-
|
3994
|
-
|
3995
|
-
|
3996
|
-
|
4114
|
+
wallet = self.wallet_ask(
|
4115
|
+
wallet_name,
|
4116
|
+
wallet_path,
|
4117
|
+
wallet_hotkey,
|
4118
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
4119
|
+
validate=WV.WALLET_AND_HOTKEY,
|
4120
|
+
)
|
4121
|
+
return self._run_command(
|
4122
|
+
sudo.senate_vote(
|
4123
|
+
wallet, self.initialize_chain(network), proposal, vote, prompt
|
4124
|
+
)
|
4125
|
+
)
|
4126
|
+
|
4127
|
+
def sudo_set_take(
|
4128
|
+
self,
|
4129
|
+
network: Optional[list[str]] = Options.network,
|
4130
|
+
wallet_name: Optional[str] = Options.wallet_name,
|
4131
|
+
wallet_path: Optional[str] = Options.wallet_path,
|
4132
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
4133
|
+
take: float = typer.Option(None, help="The new take value."),
|
4134
|
+
quiet: bool = Options.quiet,
|
4135
|
+
verbose: bool = Options.verbose,
|
4136
|
+
):
|
4137
|
+
"""
|
4138
|
+
Allows users to change their delegate take percentage.
|
4139
|
+
|
4140
|
+
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.
|
4141
|
+
The command makes sure the new take value is within 0-18% range.
|
4142
|
+
|
4143
|
+
EXAMPLE
|
4144
|
+
[green]$[/green] btcli sudo set-take --wallet-name my_wallet --wallet-hotkey my_hotkey
|
4145
|
+
"""
|
4146
|
+
max_value = 0.18
|
4147
|
+
min_value = 0.00
|
4148
|
+
self.verbosity_handler(quiet, verbose)
|
4149
|
+
|
4150
|
+
wallet = self.wallet_ask(
|
4151
|
+
wallet_name,
|
4152
|
+
wallet_path,
|
4153
|
+
wallet_hotkey,
|
4154
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
4155
|
+
validate=WV.WALLET_AND_HOTKEY,
|
4156
|
+
)
|
4157
|
+
|
4158
|
+
self._run_command(
|
4159
|
+
sudo.display_current_take(self.initialize_chain(network), wallet),
|
4160
|
+
exit_early=False,
|
4161
|
+
)
|
4162
|
+
|
4163
|
+
if not take:
|
4164
|
+
take = FloatPrompt.ask(
|
4165
|
+
f"Enter [blue]take value[/blue] (0.18 for 18%) [blue]Min: {min_value} Max: {max_value}"
|
4166
|
+
)
|
4167
|
+
if not (min_value <= take <= max_value):
|
4168
|
+
print_error(
|
4169
|
+
f"Take value must be between {min_value} and {max_value}. Provided value: {take}"
|
3997
4170
|
)
|
3998
4171
|
raise typer.Exit()
|
3999
|
-
|
4000
|
-
|
4001
|
-
|
4002
|
-
|
4172
|
+
|
4173
|
+
return self._run_command(
|
4174
|
+
sudo.set_take(wallet, self.initialize_chain(network), take)
|
4175
|
+
)
|
4176
|
+
|
4177
|
+
def sudo_get_take(
|
4178
|
+
self,
|
4179
|
+
network: Optional[list[str]] = Options.network,
|
4180
|
+
wallet_name: Optional[str] = Options.wallet_name,
|
4181
|
+
wallet_path: Optional[str] = Options.wallet_path,
|
4182
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
4183
|
+
quiet: bool = Options.quiet,
|
4184
|
+
verbose: bool = Options.verbose,
|
4185
|
+
):
|
4186
|
+
"""
|
4187
|
+
Allows users to check their delegate take percentage.
|
4188
|
+
|
4189
|
+
This command can be used to fetch the delegate take of your hotkey.
|
4190
|
+
|
4191
|
+
EXAMPLE
|
4192
|
+
[green]$[/green] btcli sudo get-take --wallet-name my_wallet --wallet-hotkey my_hotkey
|
4193
|
+
"""
|
4194
|
+
self.verbosity_handler(quiet, verbose)
|
4195
|
+
|
4196
|
+
wallet = self.wallet_ask(
|
4197
|
+
wallet_name,
|
4198
|
+
wallet_path,
|
4199
|
+
wallet_hotkey,
|
4200
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
4201
|
+
validate=WV.WALLET_AND_HOTKEY,
|
4202
|
+
)
|
4203
|
+
|
4204
|
+
self._run_command(
|
4205
|
+
sudo.display_current_take(self.initialize_chain(network), wallet)
|
4206
|
+
)
|
4207
|
+
|
4208
|
+
def subnets_list(
|
4209
|
+
self,
|
4210
|
+
network: Optional[list[str]] = Options.network,
|
4211
|
+
quiet: bool = Options.quiet,
|
4212
|
+
verbose: bool = Options.verbose,
|
4213
|
+
live_mode: bool = Options.live,
|
4214
|
+
):
|
4215
|
+
"""
|
4216
|
+
List all subnets and their detailed information.
|
4217
|
+
|
4218
|
+
[bold]Common Examples:[/bold]
|
4219
|
+
|
4220
|
+
1. List all subnets:
|
4221
|
+
[green]$[/green] btcli subnets list
|
4222
|
+
|
4223
|
+
2. List all subnets in live mode:
|
4224
|
+
[green]$[/green] btcli subnets list --live
|
4225
|
+
|
4226
|
+
[bold]Output Columns:[/bold]
|
4227
|
+
• [white]Netuid[/white] - Subnet identifier number
|
4228
|
+
• [white]Name[/white] - Subnet name with currency symbol (τ/α/β etc)
|
4229
|
+
• [white]Price (τ_in/α_in)[/white] - Exchange rate (TAO per alpha token)
|
4230
|
+
• [white]Market Cap (α * Price)[/white] - Total value in TAO (alpha tokens × price)
|
4231
|
+
• [white]Emission (τ)[/white] - TAO rewards emitted per block to subnet
|
4232
|
+
• [white]P (τ_in, α_in)[/white] - Pool reserves (Tao reserves, alpha reserves) in liquidity pool
|
4233
|
+
• [white]Stake (α_out)[/white] - Total staked alpha tokens across all hotkeys (alpha outstanding)
|
4234
|
+
• [white]Supply (α)[/white] - Circulating alpha token supply
|
4235
|
+
• [white]Tempo (k/n)[/white] - Block interval for subnet updates
|
4236
|
+
|
4237
|
+
EXAMPLE
|
4238
|
+
|
4239
|
+
[green]$[/green] btcli subnets list
|
4240
|
+
"""
|
4241
|
+
self.verbosity_handler(quiet, verbose)
|
4242
|
+
subtensor = self.initialize_chain(network)
|
4003
4243
|
return self._run_command(
|
4004
4244
|
subnets.subnets_list(
|
4005
4245
|
subtensor,
|
4006
|
-
|
4007
|
-
|
4246
|
+
False, # reuse-last
|
4247
|
+
False, # html-output
|
4008
4248
|
not self.config.get("use_cache", True),
|
4249
|
+
verbose,
|
4250
|
+
live_mode,
|
4251
|
+
)
|
4252
|
+
)
|
4253
|
+
|
4254
|
+
def subnets_price(
|
4255
|
+
self,
|
4256
|
+
network: Optional[list[str]] = Options.network,
|
4257
|
+
netuids: str = typer.Option(
|
4258
|
+
None,
|
4259
|
+
"--netuids",
|
4260
|
+
"--netuid",
|
4261
|
+
"-n",
|
4262
|
+
help="Netuid(s) to show the price for.",
|
4263
|
+
),
|
4264
|
+
interval_hours: int = typer.Option(
|
4265
|
+
24,
|
4266
|
+
"--interval-hours",
|
4267
|
+
"--interval",
|
4268
|
+
help="The number of hours to show the historical price for.",
|
4269
|
+
),
|
4270
|
+
all_netuids: bool = typer.Option(
|
4271
|
+
False,
|
4272
|
+
"--all-netuids",
|
4273
|
+
"--all",
|
4274
|
+
help="Show the price for all subnets.",
|
4275
|
+
),
|
4276
|
+
log_scale: bool = typer.Option(
|
4277
|
+
False,
|
4278
|
+
"--log-scale",
|
4279
|
+
"--log",
|
4280
|
+
help="Show the price in log scale.",
|
4281
|
+
),
|
4282
|
+
html_output: bool = Options.html_output,
|
4283
|
+
):
|
4284
|
+
"""
|
4285
|
+
Shows the historical price of a subnet for the past 24 hours.
|
4286
|
+
|
4287
|
+
This command displays the historical price of a subnet for the past 24 hours.
|
4288
|
+
If the `--all` flag is used, the command will display the price for all subnets in html format.
|
4289
|
+
If the `--html` flag is used, the command will display the price in an HTML chart.
|
4290
|
+
If the `--log-scale` flag is used, the command will display the price in log scale.
|
4291
|
+
If no html flag is used, the command will display the price in the cli.
|
4292
|
+
|
4293
|
+
EXAMPLE
|
4294
|
+
|
4295
|
+
[green]$[/green] btcli subnets price --netuid 1
|
4296
|
+
[green]$[/green] btcli subnets price --netuid 1 --html --log
|
4297
|
+
[green]$[/green] btcli subnets price --all --html
|
4298
|
+
[green]$[/green] btcli subnets price --netuids 1,2,3,4 --html
|
4299
|
+
"""
|
4300
|
+
if netuids:
|
4301
|
+
netuids = parse_to_list(
|
4302
|
+
netuids,
|
4303
|
+
int,
|
4304
|
+
"Netuids must be a comma-separated list of ints, e.g., `--netuids 1,2,3,4`.",
|
4305
|
+
)
|
4306
|
+
if all_netuids and netuids:
|
4307
|
+
print_error("Cannot specify both --netuid and --all-netuids")
|
4308
|
+
raise typer.Exit()
|
4309
|
+
|
4310
|
+
if not netuids and not all_netuids:
|
4311
|
+
netuids = Prompt.ask(
|
4312
|
+
"Enter the [blue]netuid(s)[/blue] to view the price of in comma-separated format [dim](or Press Enter to view all subnets)[/dim]",
|
4313
|
+
)
|
4314
|
+
if not netuids:
|
4315
|
+
all_netuids = True
|
4316
|
+
html_output = True
|
4317
|
+
else:
|
4318
|
+
netuids = parse_to_list(
|
4319
|
+
netuids,
|
4320
|
+
int,
|
4321
|
+
"Netuids must be a comma-separated list of ints, e.g., `--netuids 1,2,3,4`.",
|
4322
|
+
)
|
4323
|
+
|
4324
|
+
if all_netuids:
|
4325
|
+
html_output = True
|
4326
|
+
|
4327
|
+
if html_output and is_linux():
|
4328
|
+
print_linux_dependency_message()
|
4329
|
+
|
4330
|
+
return self._run_command(
|
4331
|
+
price.price(
|
4332
|
+
self.initialize_chain(network),
|
4333
|
+
netuids,
|
4334
|
+
all_netuids,
|
4335
|
+
interval_hours,
|
4336
|
+
html_output,
|
4337
|
+
log_scale,
|
4338
|
+
)
|
4339
|
+
)
|
4340
|
+
|
4341
|
+
def subnets_show(
|
4342
|
+
self,
|
4343
|
+
network: Optional[list[str]] = Options.network,
|
4344
|
+
netuid: int = Options.netuid,
|
4345
|
+
sort: bool = typer.Option(
|
4346
|
+
False,
|
4347
|
+
"--sort",
|
4348
|
+
help="Sort the subnets by uid.",
|
4349
|
+
),
|
4350
|
+
quiet: bool = Options.quiet,
|
4351
|
+
verbose: bool = Options.verbose,
|
4352
|
+
prompt: bool = Options.prompt,
|
4353
|
+
):
|
4354
|
+
"""
|
4355
|
+
Displays detailed information about a subnet including participants and their state.
|
4356
|
+
|
4357
|
+
EXAMPLE
|
4358
|
+
|
4359
|
+
[green]$[/green] btcli subnets list
|
4360
|
+
"""
|
4361
|
+
self.verbosity_handler(quiet, verbose)
|
4362
|
+
subtensor = self.initialize_chain(network)
|
4363
|
+
return self._run_command(
|
4364
|
+
subnets.show(
|
4365
|
+
subtensor=subtensor,
|
4366
|
+
netuid=netuid,
|
4367
|
+
sort=sort,
|
4368
|
+
max_rows=None,
|
4369
|
+
delegate_selection=False,
|
4370
|
+
verbose=verbose,
|
4371
|
+
prompt=prompt,
|
4009
4372
|
)
|
4010
4373
|
)
|
4011
4374
|
|
4012
|
-
def
|
4375
|
+
def subnets_burn_cost(
|
4013
4376
|
self,
|
4014
4377
|
network: Optional[list[str]] = Options.network,
|
4015
4378
|
quiet: bool = Options.quiet,
|
4016
4379
|
verbose: bool = Options.verbose,
|
4017
4380
|
):
|
4018
4381
|
"""
|
4019
|
-
Shows the required amount of TAO to be
|
4382
|
+
Shows the required amount of TAO to be recycled for creating a new subnet, i.e., cost of registering a new subnet.
|
4020
4383
|
|
4021
4384
|
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.
|
4022
4385
|
|
4023
4386
|
EXAMPLE
|
4024
4387
|
|
4025
|
-
[green]$[/green] btcli subnets
|
4388
|
+
[green]$[/green] btcli subnets burn_cost
|
4026
4389
|
"""
|
4027
4390
|
self.verbosity_handler(quiet, verbose)
|
4028
|
-
return self._run_command(subnets.
|
4391
|
+
return self._run_command(subnets.burn_cost(self.initialize_chain(network)))
|
4029
4392
|
|
4030
4393
|
def subnets_create(
|
4031
4394
|
self,
|
4032
4395
|
wallet_name: str = Options.wallet_name,
|
4033
4396
|
wallet_path: str = Options.wallet_path,
|
4397
|
+
wallet_hotkey: str = Options.wallet_hotkey,
|
4034
4398
|
network: Optional[list[str]] = Options.network,
|
4399
|
+
subnet_name: Optional[str] = typer.Option(
|
4400
|
+
None, "--subnet-name", "--name", help="Name of the subnet"
|
4401
|
+
),
|
4402
|
+
github_repo: Optional[str] = typer.Option(
|
4403
|
+
None, "--github-repo", "--repo", help="GitHub repository URL"
|
4404
|
+
),
|
4405
|
+
subnet_contact: Optional[str] = typer.Option(
|
4406
|
+
None,
|
4407
|
+
"--subnet-contact",
|
4408
|
+
"--contact",
|
4409
|
+
"--email",
|
4410
|
+
help="Contact email for subnet",
|
4411
|
+
),
|
4412
|
+
subnet_url: Optional[str] = typer.Option(
|
4413
|
+
None, "--subnet-url", "--url", help="Subnet URL"
|
4414
|
+
),
|
4415
|
+
discord: Optional[str] = typer.Option(
|
4416
|
+
None, "--discord-handle", "--discord", help="Discord handle"
|
4417
|
+
),
|
4418
|
+
description: Optional[str] = typer.Option(
|
4419
|
+
None, "--description", help="Description"
|
4420
|
+
),
|
4421
|
+
additional_info: Optional[str] = typer.Option(
|
4422
|
+
None, "--additional-info", help="Additional information"
|
4423
|
+
),
|
4035
4424
|
prompt: bool = Options.prompt,
|
4036
4425
|
quiet: bool = Options.quiet,
|
4037
4426
|
verbose: bool = Options.verbose,
|
4038
4427
|
):
|
4039
4428
|
"""
|
4040
|
-
Registers a new subnet.
|
4429
|
+
Registers a new subnet on the network.
|
4041
4430
|
|
4042
|
-
|
4431
|
+
This command allows you to create a new subnet and set the subnet's identity.
|
4432
|
+
You also have the option to set your own identity after the registration is complete.
|
4433
|
+
|
4434
|
+
[bold]Common Examples:[/bold]
|
4043
4435
|
|
4436
|
+
1. Interactive subnet creation:
|
4044
4437
|
[green]$[/green] btcli subnets create
|
4438
|
+
|
4439
|
+
2. Create with GitHub repo and contact email:
|
4440
|
+
[green]$[/green] btcli subnets create --subnet-name MySubnet --github-repo https://github.com/myorg/mysubnet --subnet-contact team@mysubnet.net
|
4045
4441
|
"""
|
4046
4442
|
self.verbosity_handler(quiet, verbose)
|
4047
4443
|
wallet = self.wallet_ask(
|
4048
4444
|
wallet_name,
|
4049
4445
|
wallet_path,
|
4050
|
-
|
4051
|
-
ask_for=[
|
4052
|
-
|
4446
|
+
wallet_hotkey,
|
4447
|
+
ask_for=[
|
4448
|
+
WO.NAME,
|
4449
|
+
WO.HOTKEY,
|
4450
|
+
WO.PATH,
|
4451
|
+
],
|
4452
|
+
validate=WV.WALLET_AND_HOTKEY,
|
4053
4453
|
)
|
4054
|
-
|
4055
|
-
|
4454
|
+
identity = prompt_for_subnet_identity(
|
4455
|
+
subnet_name=subnet_name,
|
4456
|
+
github_repo=github_repo,
|
4457
|
+
subnet_contact=subnet_contact,
|
4458
|
+
subnet_url=subnet_url,
|
4459
|
+
discord=discord,
|
4460
|
+
description=description,
|
4461
|
+
additional=additional_info,
|
4462
|
+
)
|
4463
|
+
success = self._run_command(
|
4464
|
+
subnets.create(wallet, self.initialize_chain(network), identity, prompt),
|
4465
|
+
exit_early=False,
|
4056
4466
|
)
|
4057
4467
|
|
4468
|
+
if success and prompt:
|
4469
|
+
set_id = Confirm.ask(
|
4470
|
+
"[dark_sea_green3]Do you want to set/update your identity?",
|
4471
|
+
default=False,
|
4472
|
+
show_default=True,
|
4473
|
+
)
|
4474
|
+
if set_id:
|
4475
|
+
self.wallet_set_id(
|
4476
|
+
wallet_name=wallet.name,
|
4477
|
+
wallet_hotkey=wallet.hotkey,
|
4478
|
+
wallet_path=wallet.path,
|
4479
|
+
network=network,
|
4480
|
+
prompt=prompt,
|
4481
|
+
quiet=quiet,
|
4482
|
+
verbose=verbose,
|
4483
|
+
)
|
4484
|
+
|
4058
4485
|
def subnets_pow_register(
|
4059
4486
|
self,
|
4060
4487
|
wallet_name: Optional[str] = Options.wallet_name,
|
@@ -4128,7 +4555,7 @@ class CLIManager:
|
|
4128
4555
|
wallet_name,
|
4129
4556
|
wallet_path,
|
4130
4557
|
wallet_hotkey,
|
4131
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
4558
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
4132
4559
|
validate=WV.WALLET_AND_HOTKEY,
|
4133
4560
|
),
|
4134
4561
|
self.initialize_chain(network),
|
@@ -4257,6 +4684,12 @@ class CLIManager:
|
|
4257
4684
|
)
|
4258
4685
|
raise typer.Exit()
|
4259
4686
|
|
4687
|
+
# For Rao games
|
4688
|
+
effective_network = get_effective_network(self.config, network)
|
4689
|
+
if is_rao_network(effective_network):
|
4690
|
+
print_error("This command is disabled on the 'rao' network.")
|
4691
|
+
raise typer.Exit()
|
4692
|
+
|
4260
4693
|
if reuse_last:
|
4261
4694
|
if netuid is not None:
|
4262
4695
|
console.print("Cannot specify netuid when using `--reuse-last`")
|
@@ -4359,7 +4792,7 @@ class CLIManager:
|
|
4359
4792
|
wallet_name,
|
4360
4793
|
wallet_path,
|
4361
4794
|
wallet_hotkey,
|
4362
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
4795
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
4363
4796
|
validate=WV.WALLET_AND_HOTKEY,
|
4364
4797
|
)
|
4365
4798
|
|
@@ -4457,7 +4890,7 @@ class CLIManager:
|
|
4457
4890
|
wallet_name,
|
4458
4891
|
wallet_path,
|
4459
4892
|
wallet_hotkey,
|
4460
|
-
ask_for=[WO.NAME, WO.HOTKEY],
|
4893
|
+
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
|
4461
4894
|
validate=WV.WALLET_AND_HOTKEY,
|
4462
4895
|
)
|
4463
4896
|
return self._run_command(
|