From 26b0e8a9b5793f5dc8896721412918dc5d104e36 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 23 Oct 2025 16:57:38 +0200 Subject: [PATCH 1/3] Adds additional warnings for move vs transfer --- bittensor_cli/cli.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 220de3d40..eae4d0fed 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -4573,9 +4573,17 @@ def stake_move( [green]$[/green] btcli stake move """ self.verbosity_handler(quiet, verbose, json_output) - console.print( - "[dim]This command moves stake from one hotkey to another hotkey while keeping the same coldkey.[/dim]" - ) + if prompt: + if not Confirm.ask( + "This transaction will [bold]move stake[/bold] to another hotkey while keeping the same " + "coldkey ownership. Do you wish to continue? ", + default=False, + ): + raise typer.Exit() + else: + console.print( + "[dim]This command moves stake from one hotkey to another hotkey while keeping the same coldkey.[/dim]" + ) if not destination_hotkey: dest_wallet_or_ss58 = Prompt.ask( "Enter the [blue]destination wallet[/blue] where destination hotkey is located or " @@ -4770,9 +4778,18 @@ def stake_transfer( [green]$[/green] btcli stake transfer --all --origin-netuid 1 --dest-netuid 2 """ self.verbosity_handler(quiet, verbose, json_output) - console.print( - "[dim]This command transfers stake from one coldkey to another while keeping the same hotkey.[/dim]" - ) + if prompt: + if not Confirm.ask( + "This transaction will [bold]transfer ownership[/bold] from one coldkey to another, in subnets " + "which have enabled it. You should ensure that the destination coldkey is " + "[bold]not a validator hotkey[/bold] before continuing. Do you wish to continue?", + default=False, + ): + raise typer.Exit() + else: + console.print( + "[dim]This command transfers stake from one coldkey to another while keeping the same hotkey.[/dim]" + ) if not dest_ss58: dest_ss58 = Prompt.ask( From c239c00f62e97d4ffc533ec11c290a4d73becc90 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 23 Oct 2025 17:18:51 +0200 Subject: [PATCH 2/3] Checks if hotkey has owner and provides confirmation --- bittensor_cli/src/bittensor/extrinsics/transfer.py | 9 +++++++++ bittensor_cli/src/bittensor/subtensor_interface.py | 12 +++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/bittensor_cli/src/bittensor/extrinsics/transfer.py b/bittensor_cli/src/bittensor/extrinsics/transfer.py index 720e8d356..13f255ff4 100644 --- a/bittensor_cli/src/bittensor/extrinsics/transfer.py +++ b/bittensor_cli/src/bittensor/extrinsics/transfer.py @@ -175,6 +175,15 @@ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]: # Ask before moving on. if prompt: + hk_owner = await subtensor.get_hotkey_owner(destination, check_exists=False) + if hk_owner and hk_owner != destination: + if not Confirm.ask( + f"The destination appears to be a hotkey, owned by [bright_magenta]{hk_owner}[/bright_magenta]. " + f"Only proceed if you are absolutely sure that [bright_magenta]{destination}[/bright_magenta] is the " + f"correct destination.", + default=False, + ): + return False, None if not Confirm.ask( "Do you want to transfer:[bold white]\n" f" amount: [bright_cyan]{amount if not transfer_all else account_balance}[/bright_cyan]\n" diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index a0d6dd6ee..2ef90d284 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -1123,6 +1123,7 @@ async def get_hotkey_owner( self, hotkey_ss58: str, block_hash: Optional[str] = None, + check_exists: bool = True, ) -> Optional[str]: val = await self.query( module="SubtensorModule", @@ -1130,10 +1131,15 @@ async def get_hotkey_owner( params=[hotkey_ss58], block_hash=block_hash, ) - if val: - exists = await self.does_hotkey_exist(hotkey_ss58, block_hash=block_hash) + if check_exists: + if val: + exists = await self.does_hotkey_exist( + hotkey_ss58, block_hash=block_hash + ) + else: + exists = False else: - exists = False + exists = True hotkey_owner = val if exists else None return hotkey_owner From ab41c23f2fd7e49ddd9e895386abb7ef2293ec50 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 23 Oct 2025 17:21:45 +0200 Subject: [PATCH 3/3] Moves the unlock wallet fn after the confirmations/balance check --- bittensor_cli/src/bittensor/extrinsics/transfer.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bittensor_cli/src/bittensor/extrinsics/transfer.py b/bittensor_cli/src/bittensor/extrinsics/transfer.py index 13f255ff4..6886fb41a 100644 --- a/bittensor_cli/src/bittensor/extrinsics/transfer.py +++ b/bittensor_cli/src/bittensor/extrinsics/transfer.py @@ -116,9 +116,6 @@ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]: ) return False, None console.print(f"[dark_orange]Initiating transfer on network: {subtensor.network}") - # Unlock wallet coldkey. - if not unlock_key(wallet).success: - return False, None call_params: dict[str, Optional[Union[str, int]]] = {"dest": destination} if transfer_all: @@ -188,7 +185,7 @@ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]: "Do you want to transfer:[bold white]\n" f" amount: [bright_cyan]{amount if not transfer_all else account_balance}[/bright_cyan]\n" f" from: [light_goldenrod2]{wallet.name}[/light_goldenrod2] : " - f"[bright_magenta]{wallet.coldkey.ss58_address}\n[/bright_magenta]" + f"[bright_magenta]{wallet.coldkeypub.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].\n" @@ -196,6 +193,10 @@ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]: ): return False, None + # Unlock wallet coldkey. + if not unlock_key(wallet).success: + return False, None + with console.status(":satellite: Transferring...", spinner="earth"): success, block_hash, err_msg, ext_receipt = await do_transfer()