Skip to content

Commit e926be8

Browse files
authored
Merge pull request #651 from opentensor/release/9.13.0
Release/9.13.0
2 parents d39cd80 + fa5f3d3 commit e926be8

File tree

7 files changed

+503
-8
lines changed

7 files changed

+503
-8
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Changelog
22

3+
## 9.13.0 /2025-10-09
4+
5+
## What's Changed
6+
* Updates wording in transfer confirm by @thewhaleking in https://github.com/opentensor/btcli/pull/642
7+
* Update/split emissions by @ibraheem-abe in https://github.com/opentensor/btcli/pull/643
8+
* Feat/auto staking by @ibraheem-abe in https://github.com/opentensor/btcli/pull/632
9+
* Updates wording on origin/destination hotkey in `st move` by @thewhaleking in https://github.com/opentensor/btcli/pull/647
10+
* commands list fixes by @thewhaleking in https://github.com/opentensor/btcli/pull/648
11+
12+
**Full Changelog**: https://github.com/opentensor/btcli/compare/v9.12.0...v9.13.0
13+
314
## 9.12.0 /2025-09-25
415
* Removes warning icon in transfer by @ibraheem-abe in https://github.com/opentensor/btcli/pull/634
516
* Add Extrinsic Identifier Output by @thewhaleking in https://github.com/opentensor/btcli/pull/633

bittensor_cli/cli.py

Lines changed: 165 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
prompt_position_id,
7373
)
7474
from bittensor_cli.src.commands.stake import (
75+
auto_staking as auto_stake,
7576
children_hotkeys,
7677
list as list_stake,
7778
move as move_stake,
@@ -617,6 +618,7 @@ def commands_callback(value: bool):
617618
if value:
618619
cli = CLIManager()
619620
console.print(cli.generate_command_tree())
621+
raise typer.Exit()
620622

621623

622624
def debug_callback(value: bool):
@@ -935,6 +937,12 @@ def __init__(self):
935937
self.stake_app.command(
936938
"add", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
937939
)(self.stake_add)
940+
self.stake_app.command(
941+
"auto", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
942+
)(self.get_auto_stake)
943+
self.stake_app.command(
944+
"set-auto", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
945+
)(self.set_auto_stake)
938946
self.stake_app.command(
939947
"remove", rich_help_panel=HELP_PANELS["STAKE"]["STAKE_MGMT"]
940948
)(self.stake_remove)
@@ -979,7 +987,7 @@ def __init__(self):
979987
"emissions", rich_help_panel=HELP_PANELS["MECHANISMS"]["EMISSION"]
980988
)(self.mechanism_emission_get)
981989
self.subnet_mechanisms_app.command(
982-
"emissions-split", rich_help_panel=HELP_PANELS["MECHANISMS"]["EMISSION"]
990+
"split-emissions", rich_help_panel=HELP_PANELS["MECHANISMS"]["EMISSION"]
983991
)(self.mechanism_emission_set)
984992

985993
# sudo commands
@@ -1105,14 +1113,19 @@ def __init__(self):
11051113
"get_identity",
11061114
hidden=True,
11071115
)(self.wallet_get_id)
1108-
self.wallet_app.command("associate_hotkey")(self.wallet_associate_hotkey)
1116+
self.wallet_app.command("associate_hotkey", hidden=True)(
1117+
self.wallet_associate_hotkey
1118+
)
11091119

11101120
# Subnets
11111121
self.subnets_app.command("burn_cost", hidden=True)(self.subnets_burn_cost)
11121122
self.subnets_app.command("pow_register", hidden=True)(self.subnets_pow_register)
11131123
self.subnets_app.command("set_identity", hidden=True)(self.subnets_set_identity)
11141124
self.subnets_app.command("get_identity", hidden=True)(self.subnets_get_identity)
11151125
self.subnets_app.command("check_start", hidden=True)(self.subnets_check_start)
1126+
self.subnet_mechanisms_app.command("emissions-split", hidden=True)(
1127+
self.mechanism_emission_set
1128+
)
11161129

11171130
# Sudo
11181131
self.sudo_app.command("senate_vote", hidden=True)(self.sudo_senate_vote)
@@ -3593,6 +3606,137 @@ def wallet_swap_coldkey(
35933606
)
35943607
)
35953608

3609+
def get_auto_stake(
3610+
self,
3611+
network: Optional[list[str]] = Options.network,
3612+
wallet_name: Optional[str] = Options.wallet_name,
3613+
wallet_path: Optional[str] = Options.wallet_path,
3614+
coldkey_ss58=typer.Option(
3615+
None,
3616+
"--ss58",
3617+
"--coldkey_ss58",
3618+
"--coldkey.ss58_address",
3619+
"--coldkey.ss58",
3620+
help="Coldkey address of the wallet",
3621+
),
3622+
quiet: bool = Options.quiet,
3623+
verbose: bool = Options.verbose,
3624+
json_output: bool = Options.json_output,
3625+
):
3626+
"""Display auto-stake destinations for a wallet across all subnets."""
3627+
3628+
self.verbosity_handler(quiet, verbose, json_output)
3629+
3630+
wallet = None
3631+
if coldkey_ss58:
3632+
if not is_valid_ss58_address(coldkey_ss58):
3633+
print_error("You entered an invalid ss58 address")
3634+
raise typer.Exit()
3635+
else:
3636+
if wallet_name:
3637+
coldkey_or_ss58 = wallet_name
3638+
else:
3639+
coldkey_or_ss58 = Prompt.ask(
3640+
"Enter the [blue]wallet name[/blue] or [blue]coldkey ss58 address[/blue]",
3641+
default=self.config.get("wallet_name") or defaults.wallet.name,
3642+
)
3643+
if is_valid_ss58_address(coldkey_or_ss58):
3644+
coldkey_ss58 = coldkey_or_ss58
3645+
else:
3646+
wallet_name = coldkey_or_ss58 if coldkey_or_ss58 else wallet_name
3647+
wallet = self.wallet_ask(
3648+
wallet_name,
3649+
wallet_path,
3650+
None,
3651+
ask_for=[WO.NAME, WO.PATH],
3652+
validate=WV.WALLET,
3653+
)
3654+
3655+
return self._run_command(
3656+
auto_stake.show_auto_stake_destinations(
3657+
wallet,
3658+
self.initialize_chain(network),
3659+
coldkey_ss58=coldkey_ss58,
3660+
json_output=json_output,
3661+
verbose=verbose,
3662+
)
3663+
)
3664+
3665+
def set_auto_stake(
3666+
self,
3667+
network: Optional[list[str]] = Options.network,
3668+
wallet_name: Optional[str] = Options.wallet_name,
3669+
wallet_path: Optional[str] = Options.wallet_path,
3670+
netuid: Optional[int] = Options.netuid_not_req,
3671+
quiet: bool = Options.quiet,
3672+
verbose: bool = Options.verbose,
3673+
prompt: bool = Options.prompt,
3674+
wait_for_inclusion: bool = Options.wait_for_inclusion,
3675+
wait_for_finalization: bool = Options.wait_for_finalization,
3676+
json_output: bool = Options.json_output,
3677+
):
3678+
"""Set the auto-stake destination hotkey for a coldkey."""
3679+
3680+
self.verbosity_handler(quiet, verbose, json_output)
3681+
3682+
wallet = self.wallet_ask(
3683+
wallet_name,
3684+
wallet_path,
3685+
None,
3686+
ask_for=[WO.NAME, WO.PATH],
3687+
validate=WV.WALLET,
3688+
)
3689+
3690+
if netuid is None:
3691+
netuid = IntPrompt.ask(
3692+
"Enter the [blue]netuid[/blue] to configure",
3693+
default=defaults.netuid,
3694+
)
3695+
validate_netuid(netuid)
3696+
3697+
hotkey_prompt = Prompt.ask(
3698+
"Enter the [blue]hotkey ss58 address[/blue] to auto-stake to "
3699+
"[dim](Press Enter to view delegates)[/dim]",
3700+
default="",
3701+
show_default=False,
3702+
).strip()
3703+
3704+
if not hotkey_prompt:
3705+
selected_hotkey = self._run_command(
3706+
subnets.show(
3707+
subtensor=self.initialize_chain(network),
3708+
netuid=netuid,
3709+
sort=False,
3710+
max_rows=20,
3711+
prompt=False,
3712+
delegate_selection=True,
3713+
),
3714+
exit_early=False,
3715+
)
3716+
if not selected_hotkey:
3717+
print_error("No delegate selected. Exiting.")
3718+
return
3719+
hotkey_ss58 = selected_hotkey
3720+
else:
3721+
hotkey_ss58 = hotkey_prompt
3722+
3723+
if not is_valid_ss58_address(hotkey_ss58):
3724+
print_error("You entered an invalid hotkey ss58 address")
3725+
return
3726+
3727+
return self._run_command(
3728+
auto_stake.set_auto_stake_destination(
3729+
wallet,
3730+
self.initialize_chain(network),
3731+
netuid,
3732+
hotkey_ss58,
3733+
wait_for_inclusion=wait_for_inclusion,
3734+
wait_for_finalization=wait_for_finalization,
3735+
prompt_user=prompt,
3736+
json_output=json_output,
3737+
)
3738+
)
3739+
35963740
def stake_list(
35973741
self,
35983742
network: Optional[list[str]] = Options.network,
@@ -4318,15 +4462,32 @@ def stake_move(
43184462
network: Optional[list[str]] = Options.network,
43194463
wallet_name: Optional[str] = Options.wallet_name,
43204464
wallet_path: Optional[str] = Options.wallet_path,
4321-
wallet_hotkey: Optional[str] = Options.wallet_hotkey_ss58,
4465+
wallet_hotkey: Optional[str] = typer.Option(
4466+
None,
4467+
"--from",
4468+
"--hotkey",
4469+
"--hotkey-ss58",
4470+
"-H",
4471+
"--wallet_hotkey",
4472+
"--wallet_hotkey_ss58",
4473+
"--wallet-hotkey",
4474+
"--wallet-hotkey-ss58",
4475+
"--wallet.hotkey",
4476+
help="Validator hotkey or SS58 where the stake is currently located.",
4477+
),
43224478
origin_netuid: Optional[int] = typer.Option(
43234479
None, "--origin-netuid", help="Origin netuid"
43244480
),
43254481
destination_netuid: Optional[int] = typer.Option(
43264482
None, "--dest-netuid", help="Destination netuid"
43274483
),
43284484
destination_hotkey: Optional[str] = typer.Option(
4329-
None, "--dest-ss58", "--dest", help="Destination hotkey", prompt=False
4485+
None,
4486+
"--to",
4487+
"--dest-ss58",
4488+
"--dest",
4489+
help="Destination validator hotkey SS58",
4490+
prompt=False,
43304491
),
43314492
amount: float = typer.Option(
43324493
None,

bittensor_cli/src/bittensor/extrinsics/transfer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ async def do_transfer() -> tuple[bool, str, str, AsyncExtrinsicReceipt]:
182182
f"[bright_magenta]{wallet.coldkey.ss58_address}\n[/bright_magenta]"
183183
f" to: [bright_magenta]{destination}[/bright_magenta]\n for fee: [bright_cyan]{fee}[/bright_cyan]\n"
184184
f"[bright_yellow]Transferring is not the same as staking. To instead stake, use "
185-
f"[dark_orange]btcli stake add[/dark_orange] instead[/bright_yellow]"
185+
f"[dark_orange]btcli stake add[/dark_orange] instead[/bright_yellow].\n"
186+
f"Proceed with transfer?"
186187
):
187188
return False, None
188189

bittensor_cli/src/bittensor/subtensor_interface.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,30 @@ async def get_stake_for_coldkey(
225225
stakes: list[StakeInfo] = StakeInfo.list_from_any(result)
226226
return [stake for stake in stakes if stake.stake > 0]
227227

228+
async def get_auto_stake_destinations(
229+
self,
230+
coldkey_ss58: str,
231+
block_hash: Optional[str] = None,
232+
reuse_block: bool = False,
233+
) -> dict[int, str]:
234+
"""Retrieve auto-stake destinations configured for a coldkey."""
235+
236+
query = await self.substrate.query_map(
237+
module="SubtensorModule",
238+
storage_function="AutoStakeDestination",
239+
params=[coldkey_ss58],
240+
block_hash=block_hash,
241+
reuse_block_hash=reuse_block,
242+
)
243+
244+
destinations: dict[int, str] = {}
245+
async for netuid, destination in query:
246+
hotkey_ss58 = decode_account_id(destination.value[0])
247+
if hotkey_ss58:
248+
destinations[int(netuid)] = hotkey_ss58
249+
250+
return destinations
251+
228252
async def get_stake_for_coldkey_and_hotkey(
229253
self,
230254
hotkey_ss58: str,

0 commit comments

Comments
 (0)