From f02fbe8740c82ed3a2b84026d81ae21c8016bc54 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 19 Sep 2025 16:44:00 -0700 Subject: [PATCH 01/16] Add get_auto_stake_destinations --- .../src/bittensor/subtensor_interface.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index 27df7d94c..a8d6aa1b8 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -224,6 +224,30 @@ async def get_stake_for_coldkey( stakes: list[StakeInfo] = StakeInfo.list_from_any(result) return [stake for stake in stakes if stake.stake > 0] + async def get_auto_stake_destinations( + self, + coldkey_ss58: str, + block_hash: Optional[str] = None, + reuse_block: bool = False, + ) -> dict[int, str]: + """Retrieve auto-stake destinations configured for a coldkey.""" + + query = await self.substrate.query_map( + module="SubtensorModule", + storage_function="AutoStakeDestination", + params=[coldkey_ss58], + block_hash=block_hash, + reuse_block_hash=reuse_block, + ) + + destinations: dict[int, str] = {} + async for netuid, destination in query: + hotkey_ss58 = decode_account_id(destination.value[0]) + if hotkey_ss58: + destinations[int(netuid)] = hotkey_ss58 + + return destinations + async def get_stake_for_coldkey_and_hotkey( self, hotkey_ss58: str, From 1321ab42263a9d8bd6622d87b302a7d2ea88f52f Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 19 Sep 2025 16:44:38 -0700 Subject: [PATCH 02/16] add cmd get_auto_stake --- bittensor_cli/cli.py | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 4484f5288..5bb30fd48 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -72,6 +72,7 @@ prompt_position_id, ) from bittensor_cli.src.commands.stake import ( + auto_staking as auto_stake, children_hotkeys, list as list_stake, move as move_stake, @@ -905,6 +906,9 @@ def __init__(self): self.stake_app.command( "add", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"] )(self.stake_add) + self.stake_app.command( + "auto", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"] + )(self.get_auto_stake) self.stake_app.command( "remove", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"] )(self.stake_remove) @@ -3505,6 +3509,62 @@ def wallet_swap_coldkey( ) ) + def get_auto_stake( + self, + network: Optional[list[str]] = Options.network, + wallet_name: Optional[str] = Options.wallet_name, + wallet_path: Optional[str] = Options.wallet_path, + coldkey_ss58=typer.Option( + None, + "--ss58", + "--coldkey_ss58", + "--coldkey.ss58_address", + "--coldkey.ss58", + help="Coldkey address of the wallet", + ), + quiet: bool = Options.quiet, + verbose: bool = Options.verbose, + json_output: bool = Options.json_output, + ): + """Display auto-stake destinations for a wallet across all subnets.""" + + self.verbosity_handler(quiet, verbose, json_output) + + wallet = None + if coldkey_ss58: + if not is_valid_ss58_address(coldkey_ss58): + print_error("You entered an invalid ss58 address") + raise typer.Exit() + else: + if wallet_name: + coldkey_or_ss58 = wallet_name + else: + coldkey_or_ss58 = Prompt.ask( + "Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 address[/blue]", + default=self.config.get("wallet_name") or defaults.wallet.name, + ) + if is_valid_ss58_address(coldkey_or_ss58): + coldkey_ss58 = coldkey_or_ss58 + else: + wallet_name = coldkey_or_ss58 if coldkey_or_ss58 else wallet_name + wallet = self.wallet_ask( + wallet_name, + wallet_path, + None, + ask_for=[WO.NAME, WO.PATH], + validate=WV.WALLET, + ) + + return self._run_command( + auto_stake.show_auto_destinations( + wallet, + self.initialize_chain(network), + coldkey_ss58=coldkey_ss58, + json_output=json_output, + verbose=verbose, + ) + ) + def stake_list( self, network: Optional[list[str]] = Options.network, From 9e6503bfa398c02ab983e03920b5857ec3412d7b Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 19 Sep 2025 16:50:18 -0700 Subject: [PATCH 03/16] add set-auto --- bittensor_cli/cli.py | 78 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 5bb30fd48..961be3189 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -909,6 +909,9 @@ def __init__(self): self.stake_app.command( "auto", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"] )(self.get_auto_stake) + self.stake_app.command( + "set-auto", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"] + )(self.set_auto_stake) self.stake_app.command( "remove", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"] )(self.stake_remove) @@ -3565,6 +3568,81 @@ def get_auto_stake( ) ) + def set_auto_stake( + self, + network: Optional[list[str]] = Options.network, + wallet_name: Optional[str] = Options.wallet_name, + wallet_path: Optional[str] = Options.wallet_path, + netuid: Optional[int] = Options.netuid_not_req, + quiet: bool = Options.quiet, + verbose: bool = Options.verbose, + prompt: bool = Options.prompt, + wait_for_inclusion: bool = Options.wait_for_inclusion, + wait_for_finalization: bool = Options.wait_for_finalization, + json_output: bool = Options.json_output, + ): + """Set the auto-stake destination hotkey for a coldkey.""" + + self.verbosity_handler(quiet, verbose, json_output) + + wallet = self.wallet_ask( + wallet_name, + wallet_path, + None, + ask_for=[WO.NAME, WO.PATH], + validate=WV.WALLET, + ) + + if netuid is None: + netuid = IntPrompt.ask( + "Enter the [blue]netuid[/blue] to configure", + default=defaults.netuid, + ) + validate_netuid(netuid) + + hotkey_prompt = Prompt.ask( + "Enter the [blue]hotkey ss58 address[/blue] to auto-stake to " + "[dim](Press Enter to view delegates)[/dim]", + default="", + show_default=False, + ).strip() + + if not hotkey_prompt: + selected_hotkey = self._run_command( + subnets.show( + subtensor=self.initialize_chain(network), + netuid=netuid, + sort=False, + max_rows=20, + prompt=False, + delegate_selection=True, + ), + exit_early=False, + ) + if not selected_hotkey: + print_error("No delegate selected. Exiting.") + return + hotkey_ss58 = selected_hotkey + else: + hotkey_ss58 = hotkey_prompt + + if not is_valid_ss58_address(hotkey_ss58): + print_error("You entered an invalid hotkey ss58 address") + return + + return self._run_command( + auto_stake.set_auto_stake_destination( + wallet, + self.initialize_chain(network), + netuid, + hotkey_ss58, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + prompt_user=prompt, + json_output=json_output, + ) + ) + def stake_list( self, network: Optional[list[str]] = Options.network, From b65de6080b5040fa65fee0d9f87f54d3e7b780f7 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 19 Sep 2025 16:50:51 -0700 Subject: [PATCH 04/16] show_auto_stake --- .../src/commands/stake/auto_staking.py | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 bittensor_cli/src/commands/stake/auto_staking.py diff --git a/bittensor_cli/src/commands/stake/auto_staking.py b/bittensor_cli/src/commands/stake/auto_staking.py new file mode 100644 index 000000000..82c38a33d --- /dev/null +++ b/bittensor_cli/src/commands/stake/auto_staking.py @@ -0,0 +1,169 @@ +import asyncio +import json +from typing import Optional, TYPE_CHECKING + +from bittensor_wallet import Wallet +from rich import box +from rich.table import Table +from rich.prompt import Confirm + +from bittensor_cli.src import COLOR_PALETTE +from bittensor_cli.src.bittensor.utils import ( + console, + json_console, + get_subnet_name, + is_valid_ss58_address, + print_error, + err_console, + unlock_key, +) + +if TYPE_CHECKING: + from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface + + +async def show_auto_destinations( + wallet: Optional[Wallet], + subtensor: "SubtensorInterface", + coldkey_ss58: Optional[str] = None, + json_output: bool = False, + verbose: bool = False, +) -> Optional[dict[int, dict[str, Optional[str]]]]: + """Display auto-stake destinations for the supplied wallet.""" + + wallet_name: Optional[str] = wallet.name if wallet else None + coldkey_ss58 = coldkey_ss58 or (wallet.coldkeypub.ss58_address if wallet else None) + if not coldkey_ss58: + raise ValueError("A wallet or coldkey SS58 address must be provided") + + with console.status( + f"Retrieving auto-stake configuration from {subtensor.network}...", + spinner="earth", + ): + chain_head = await subtensor.substrate.get_chain_head() + ( + subnet_info, + auto_destinations, + identities, + delegate_identities, + ) = await asyncio.gather( + subtensor.all_subnets(block_hash=chain_head), + subtensor.get_auto_stake_destinations( + coldkey_ss58=coldkey_ss58, + block_hash=chain_head, + reuse_block=True, + ), + subtensor.fetch_coldkey_hotkey_identities(block_hash=chain_head), + subtensor.get_delegate_identities(block_hash=chain_head), + ) + + subnet_map = {info.netuid: info for info in subnet_info} + auto_destinations = auto_destinations or {} + identities = identities or {} + delegate_identities = delegate_identities or {} + hotkey_identities = identities.get("hotkeys", {}) + + def resolve_identity(hotkey: str) -> Optional[str]: + if not hotkey: + return None + + identity_entry = hotkey_identities.get(hotkey, {}).get("identity") + if identity_entry: + display_name = identity_entry.get("name") or identity_entry.get("display") + if display_name: + return display_name + + delegate_info = delegate_identities.get(hotkey) + if delegate_info and getattr(delegate_info, "display", ""): + return delegate_info.display + + return None + + coldkey_display = wallet_name + if not coldkey_display: + coldkey_identity = identities.get("coldkeys", {}).get(coldkey_ss58, {}) + if identity_data := coldkey_identity.get("identity"): + coldkey_display = identity_data.get("name") or identity_data.get("display") + if not coldkey_display: + coldkey_display = f"{coldkey_ss58[:6]}...{coldkey_ss58[-6:]}" + + rows = [] + data_output: dict[int, dict[str, Optional[str]]] = {} + + for netuid in sorted(subnet_map): + subnet = subnet_map[netuid] + subnet_name = get_subnet_name(subnet) + hotkey_ss58 = auto_destinations.get(netuid) + identity_str = resolve_identity(hotkey_ss58) if hotkey_ss58 else None + is_custom = hotkey_ss58 is not None + + data_output[netuid] = { + "subnet_name": subnet_name, + "status": "custom" if is_custom else "default", + "destination": hotkey_ss58, + "identity": identity_str, + } + + if json_output: + continue + + status_text = ( + f"[{COLOR_PALETTE['STAKE']['STAKE_ALPHA']}]Custom[/{COLOR_PALETTE['STAKE']['STAKE_ALPHA']}]" + if is_custom + else f"[{COLOR_PALETTE['GENERAL']['HINT']}]Default[/{COLOR_PALETTE['GENERAL']['HINT']}]" + ) + + rows.append( + ( + str(netuid), + subnet_name, + status_text, + hotkey_ss58, + identity_str or "", + ) + ) + + if json_output: + json_console.print(json.dumps(data_output)) + return data_output + + table = Table( + title=( + f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Auto Stake Destinations" + f" for [bold]{coldkey_display}[/bold]\n" + f"Network: {subtensor.network}\n" + f"Coldkey: {coldkey_ss58}\n" + f"[/{COLOR_PALETTE['GENERAL']['HEADER']}]" + ), + show_edge=False, + header_style="bold white", + border_style="bright_black", + style="bold", + title_justify="center", + show_lines=False, + pad_edge=True, + box=box.SIMPLE_HEAD, + ) + + table.add_column( + "Netuid", style=COLOR_PALETTE["GENERAL"]["SYMBOL"], justify="center" + ) + table.add_column("Subnet", style="cyan", justify="left") + table.add_column("Status", style="white", justify="center") + table.add_column( + "Destination Hotkey", style=COLOR_PALETTE["GENERAL"]["HOTKEY"], justify="center" + ) + table.add_column( + "Identity", style=COLOR_PALETTE["GENERAL"]["SUBHEADING"], justify="left" + ) + + for row in rows: + table.add_row(*row) + + console.print(table) + console.print( + f"\n[{COLOR_PALETTE['GENERAL']['SUBHEADING']}]Total subnets:[/] {len(subnet_map)} " + f"[{COLOR_PALETTE['GENERAL']['SUBHEADING']}]Custom destinations:[/] {len(auto_destinations)}" + ) + + return None From 8b40ff2e01e7490758a70ae1fa9298a4d04469b7 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 19 Sep 2025 16:51:07 -0700 Subject: [PATCH 05/16] set_auto_stake_destination --- .../src/commands/stake/auto_staking.py | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/bittensor_cli/src/commands/stake/auto_staking.py b/bittensor_cli/src/commands/stake/auto_staking.py index 82c38a33d..e3f082434 100644 --- a/bittensor_cli/src/commands/stake/auto_staking.py +++ b/bittensor_cli/src/commands/stake/auto_staking.py @@ -167,3 +167,127 @@ def resolve_identity(hotkey: str) -> Optional[str]: ) return None + + +async def set_auto_stake_destination( + wallet: Wallet, + subtensor: "SubtensorInterface", + netuid: int, + hotkey_ss58: str, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + prompt_user: bool = True, + json_output: bool = False, +) -> bool: + """Set the auto-stake destination hotkey for a coldkey on a subnet.""" + + if not is_valid_ss58_address(hotkey_ss58): + print_error("You entered an invalid hotkey ss58 address") + return False + + try: + chain_head = await subtensor.substrate.get_chain_head() + subnet_info, identities, delegate_identities = await asyncio.gather( + subtensor.subnet(netuid, block_hash=chain_head), + subtensor.fetch_coldkey_hotkey_identities(block_hash=chain_head), + subtensor.get_delegate_identities(block_hash=chain_head), + ) + except ValueError: + print_error(f"Subnet with netuid {netuid} does not exist") + return False + + hotkey_identity = "" + identities = identities or {} + delegate_identities = delegate_identities or {} + + hotkey_identity_entry = identities.get("hotkeys", {}).get(hotkey_ss58, {}) + if identity_data := hotkey_identity_entry.get("identity"): + hotkey_identity = ( + identity_data.get("name") or identity_data.get("display") or "" + ) + if not hotkey_identity: + delegate_info = delegate_identities.get(hotkey_ss58) + if delegate_info and getattr(delegate_info, "display", ""): + hotkey_identity = delegate_info.display + + if prompt_user and not json_output: + table = Table( + title=( + f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Confirm Auto-Stake Destination" + f"[/{COLOR_PALETTE['GENERAL']['HEADER']}]" + ), + show_edge=False, + header_style="bold white", + border_style="bright_black", + style="bold", + title_justify="center", + show_lines=False, + pad_edge=True, + box=box.SIMPLE_HEAD, + ) + table.add_column( + "Netuid", justify="center", style=COLOR_PALETTE["GENERAL"]["SYMBOL"] + ) + table.add_column("Subnet", style="cyan", justify="left") + table.add_column( + "Destination Hotkey", + style=COLOR_PALETTE["GENERAL"]["HOTKEY"], + justify="center", + ) + table.add_column( + "Identity", style=COLOR_PALETTE["GENERAL"]["SUBHEADING"], justify="left" + ) + table.add_row( + str(netuid), + get_subnet_name(subnet_info), + hotkey_ss58, + hotkey_identity or "", + ) + console.print(table) + + if not Confirm.ask("\nSet this auto-stake destination?", default=True): + return False + + if not unlock_key(wallet).success: + return False + + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_coldkey_auto_stake_hotkey", + call_params={ + "netuid": netuid, + "hotkey": hotkey_ss58, + }, + ) + + with console.status( + f":satellite: Setting auto-stake destination on [white]{subtensor.network}[/white]...", + spinner="earth", + ): + success, error_message = await subtensor.sign_and_send_extrinsic( + call, + wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if json_output: + json_console.print( + json.dumps( + { + "success": success, + "error": error_message, + "netuid": netuid, + "hotkey": hotkey_ss58, + } + ) + ) + + if success: + console.print( + f":white_heavy_check_mark: [dark_sea_green3]Auto-stake destination set for netuid {netuid}[/dark_sea_green3]" + ) + return True + + err_console.print(f":cross_mark: [red]Failed[/red]: {error_message}") + return False From dbaffe3e4c981e6672b00434e248636477e6d313 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 19 Sep 2025 16:51:35 -0700 Subject: [PATCH 06/16] cleanup --- bittensor_cli/cli.py | 2 +- bittensor_cli/src/commands/stake/auto_staking.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 961be3189..637165ad7 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -3559,7 +3559,7 @@ def get_auto_stake( ) return self._run_command( - auto_stake.show_auto_destinations( + auto_stake.show_auto_stake_destinations( wallet, self.initialize_chain(network), coldkey_ss58=coldkey_ss58, diff --git a/bittensor_cli/src/commands/stake/auto_staking.py b/bittensor_cli/src/commands/stake/auto_staking.py index e3f082434..f5e5fdb5b 100644 --- a/bittensor_cli/src/commands/stake/auto_staking.py +++ b/bittensor_cli/src/commands/stake/auto_staking.py @@ -22,7 +22,7 @@ from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface -async def show_auto_destinations( +async def show_auto_stake_destinations( wallet: Optional[Wallet], subtensor: "SubtensorInterface", coldkey_ss58: Optional[str] = None, From c705a0fe37ccce867efb8be0878dd9ec97157dc0 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Tue, 23 Sep 2025 22:43:01 +0200 Subject: [PATCH 07/16] Update to include extrinsic identifiers --- bittensor_cli/src/commands/stake/auto_staking.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bittensor_cli/src/commands/stake/auto_staking.py b/bittensor_cli/src/commands/stake/auto_staking.py index f5e5fdb5b..d697df8d4 100644 --- a/bittensor_cli/src/commands/stake/auto_staking.py +++ b/bittensor_cli/src/commands/stake/auto_staking.py @@ -16,6 +16,7 @@ print_error, err_console, unlock_key, + print_extrinsic_id, ) if TYPE_CHECKING: @@ -264,13 +265,15 @@ async def set_auto_stake_destination( f":satellite: Setting auto-stake destination on [white]{subtensor.network}[/white]...", spinner="earth", ): - success, error_message = await subtensor.sign_and_send_extrinsic( + success, error_message, ext_receipt = await subtensor.sign_and_send_extrinsic( call, wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) + ext_id = await ext_receipt.get_extrinsic_identifier() if success else None + if json_output: json_console.print( json.dumps( @@ -279,11 +282,13 @@ async def set_auto_stake_destination( "error": error_message, "netuid": netuid, "hotkey": hotkey_ss58, + "extrinsic_identifier": ext_id, } ) ) if success: + await print_extrinsic_id(ext_receipt) console.print( f":white_heavy_check_mark: [dark_sea_green3]Auto-stake destination set for netuid {netuid}[/dark_sea_green3]" ) From b84ed9e9a6b613adddd2b52add54828a6e807a00 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Tue, 30 Sep 2025 23:13:39 +0200 Subject: [PATCH 08/16] Updates wording in transfer confirm --- bittensor_cli/src/bittensor/extrinsics/transfer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bittensor_cli/src/bittensor/extrinsics/transfer.py b/bittensor_cli/src/bittensor/extrinsics/transfer.py index f71e747ff..720e8d356 100644 --- a/bittensor_cli/src/bittensor/extrinsics/transfer.py +++ b/bittensor_cli/src/bittensor/extrinsics/transfer.py @@ -182,7 +182,8 @@ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]: f"[bright_magenta]{wallet.coldkey.ss58_address}\n[/bright_magenta]" f" to: [bright_magenta]{destination}[/bright_magenta]\n for fee: [bright_cyan]{fee}[/bright_cyan]\n" f"[bright_yellow]Transferring is not the same as staking. To instead stake, use " - f"[dark_orange]btcli stake add[/dark_orange] instead[/bright_yellow]" + f"[dark_orange]btcli stake add[/dark_orange] instead[/bright_yellow].\n" + f"Proceed with transfer?" ): return False, None From 74a0514e95731e4174b0b98d7e7e5fce6fd110a4 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Tue, 30 Sep 2025 15:22:05 -0700 Subject: [PATCH 09/16] fix mechanism labelling --- bittensor_cli/src/commands/subnets/mechanisms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor_cli/src/commands/subnets/mechanisms.py b/bittensor_cli/src/commands/subnets/mechanisms.py index bf329513a..2ad5d72db 100644 --- a/bittensor_cli/src/commands/subnets/mechanisms.py +++ b/bittensor_cli/src/commands/subnets/mechanisms.py @@ -247,9 +247,9 @@ async def set_emission_split( (current_value / total_existing) * 100 if total_existing else 0 ) label = ( - "[blue]Main Mechanism (1)[/blue]" + "[blue]Main Mechanism (0)[/blue]" if idx == 0 - else f"[blue]Mechanism {idx + 1}[/blue]" + else f"[blue]Mechanism {idx}[/blue]" ) response = Prompt.ask( ( From 4bd8bd374ed110dabf9805f78ca4a907b53e6050 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Tue, 30 Sep 2025 15:22:14 -0700 Subject: [PATCH 10/16] update cmd and create alias --- bittensor_cli/cli.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 7fc4fbde8..5fcaa9785 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -979,7 +979,7 @@ def __init__(self): "emissions", rich_help_panel=HELP_PANELS["MECHANISMS"]["EMISSION"] )(self.mechanism_emission_get) self.subnet_mechanisms_app.command( - "emissions-split", rich_help_panel=HELP_PANELS["MECHANISMS"]["EMISSION"] + "split-emissions", rich_help_panel=HELP_PANELS["MECHANISMS"]["EMISSION"] )(self.mechanism_emission_set) # sudo commands @@ -1113,6 +1113,9 @@ def __init__(self): self.subnets_app.command("set_identity", hidden=True)(self.subnets_set_identity) self.subnets_app.command("get_identity", hidden=True)(self.subnets_get_identity) self.subnets_app.command("check_start", hidden=True)(self.subnets_check_start) + self.subnet_mechanisms_app.command("emissions-split", hidden=True)( + self.mechanism_emission_set + ) # Sudo self.sudo_app.command("senate_vote", hidden=True)(self.sudo_senate_vote) From cfeed2714b90f19701d006dc1d704093f8230ae0 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Mon, 6 Oct 2025 20:33:08 +0200 Subject: [PATCH 11/16] Updates wording on origin/destination hotkey in `st move` --- bittensor_cli/cli.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 5fcaa9785..8c6612708 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -4321,7 +4321,19 @@ def stake_move( network: Optional[list[str]] = Options.network, wallet_name: Optional[str] = Options.wallet_name, wallet_path: Optional[str] = Options.wallet_path, - wallet_hotkey: Optional[str] = Options.wallet_hotkey_ss58, + wallet_hotkey: Optional[str] = typer.Option( + None, + "--from", + "--hotkey", + "--hotkey-ss58", + "-H", + "--wallet_hotkey", + "--wallet_hotkey_ss58", + "--wallet-hotkey", + "--wallet-hotkey-ss58", + "--wallet.hotkey", + help="Validator hotkey or SS58 where the stake is currently located.", + ), origin_netuid: Optional[int] = typer.Option( None, "--origin-netuid", help="Origin netuid" ), @@ -4329,7 +4341,12 @@ def stake_move( None, "--dest-netuid", help="Destination netuid" ), destination_hotkey: Optional[str] = typer.Option( - None, "--dest-ss58", "--dest", help="Destination hotkey", prompt=False + None, + "--to", + "--dest-ss58", + "--dest", + help="Destination validator hotkey SS58", + prompt=False, ), amount: float = typer.Option( None, From 9842fd95efe8e0b1e2b2f52bb839cb97f13dd6a9 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Mon, 6 Oct 2025 21:18:59 +0200 Subject: [PATCH 12/16] Ensures no error at the end of `btcli --commands` --- bittensor_cli/cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 5fcaa9785..138d26d6a 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -617,6 +617,7 @@ def commands_callback(value: bool): if value: cli = CLIManager() console.print(cli.generate_command_tree()) + raise typer.Exit() def debug_callback(value: bool): From b1f2a22cb57162fef602aad455521e1b14afd965 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Mon, 6 Oct 2025 21:19:11 +0200 Subject: [PATCH 13/16] Hides the associate_hotkey alias --- bittensor_cli/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 138d26d6a..a6d6bca55 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -1106,7 +1106,7 @@ def __init__(self): "get_identity", hidden=True, )(self.wallet_get_id) - self.wallet_app.command("associate_hotkey")(self.wallet_associate_hotkey) + self.wallet_app.command("associate_hotkey", hidden=True)(self.wallet_associate_hotkey) # Subnets self.subnets_app.command("burn_cost", hidden=True)(self.subnets_burn_cost) From 244087be6044a0931173e354a5b15e170b71d320 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Mon, 6 Oct 2025 21:19:54 +0200 Subject: [PATCH 14/16] Ruff --- bittensor_cli/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index a6d6bca55..a009c9f04 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -1106,7 +1106,9 @@ def __init__(self): "get_identity", hidden=True, )(self.wallet_get_id) - self.wallet_app.command("associate_hotkey", hidden=True)(self.wallet_associate_hotkey) + self.wallet_app.command("associate_hotkey", hidden=True)( + self.wallet_associate_hotkey + ) # Subnets self.subnets_app.command("burn_cost", hidden=True)(self.subnets_burn_cost) From e4174e2d7752736f569f848bd2e2dc9fd70585b9 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Mon, 6 Oct 2025 22:46:19 +0200 Subject: [PATCH 15/16] Indent --- bittensor_cli/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 4d2554203..718c2f7ff 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -618,7 +618,7 @@ def commands_callback(value: bool): if value: cli = CLIManager() console.print(cli.generate_command_tree()) - raise typer.Exit() + raise typer.Exit() def debug_callback(value: bool): From dd4c9b47d8aac516077e516903f6dd5af45e8caf Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Wed, 8 Oct 2025 10:10:11 -0700 Subject: [PATCH 16/16] bumps version and changelog --- CHANGELOG.md | 11 +++++++++++ pyproject.toml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95aae0d8e..ca36a9767 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 9.13.0 /2025-10-09 + +## What's Changed +* Updates wording in transfer confirm by @thewhaleking in https://github.com/opentensor/btcli/pull/642 +* Update/split emissions by @ibraheem-abe in https://github.com/opentensor/btcli/pull/643 +* Feat/auto staking by @ibraheem-abe in https://github.com/opentensor/btcli/pull/632 +* Updates wording on origin/destination hotkey in `st move` by @thewhaleking in https://github.com/opentensor/btcli/pull/647 +* commands list fixes by @thewhaleking in https://github.com/opentensor/btcli/pull/648 + +**Full Changelog**: https://github.com/opentensor/btcli/compare/v9.12.0...v9.13.0 + ## 9.12.0 /2025-09-25 * Removes warning icon in transfer by @ibraheem-abe in https://github.com/opentensor/btcli/pull/634 * Add Extrinsic Identifier Output by @thewhaleking in https://github.com/opentensor/btcli/pull/633 diff --git a/pyproject.toml b/pyproject.toml index 234acba94..d7e6150f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "bittensor-cli" -version = "9.12.0" +version = "9.13.0" description = "Bittensor CLI" readme = "README.md" authors = [