Skip to content

Commit 81e8c48

Browse files
authored
Merge pull request #499 from opentensor/release/9.6.0
Release/9.6.0
2 parents b593638 + 216f288 commit 81e8c48

File tree

14 files changed

+429
-151
lines changed

14 files changed

+429
-151
lines changed

CHANGELOG.md

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

3+
## 9.6.0/2025-06-12
4+
5+
## What's Changed
6+
* Allows for staking to multiple netuids in one btcli command by @thewhaleking in https://github.com/opentensor/btcli/pull/481
7+
* improve stake add json output by @thewhaleking in https://github.com/opentensor/btcli/pull/482
8+
* Apply bittensor error formatting to btcli by @thewhaleking in https://github.com/opentensor/btcli/pull/483
9+
* Add Yuma3 Enabled for Sudo Set/Get by @thewhaleking in https://github.com/opentensor/btcli/pull/487
10+
* Adds `alpha_sigmoid_steepness` call for hyperparams set/get by @thewhaleking in https://github.com/opentensor/btcli/pull/488
11+
* unstaking test fix by @thewhaleking in https://github.com/opentensor/btcli/pull/489
12+
* Merge issue: 488 by @thewhaleking in https://github.com/opentensor/btcli/pull/490
13+
* subnets check-start formatting blocks by @thewhaleking in https://github.com/opentensor/btcli/pull/491
14+
* Str vs Tuple by @thewhaleking in https://github.com/opentensor/btcli/pull/492
15+
* Add Homebrew Install to README by @thewhaleking in https://github.com/opentensor/btcli/pull/493
16+
* Update staking test for new subtensor by @thewhaleking in https://github.com/opentensor/btcli/pull/494
17+
18+
19+
**Full Changelog**: https://github.com/opentensor/btcli/compare/v9.5.1...v9.6.0
20+
321
## 9.5.1 /2025-06-02
22+
423
## What's Changed
524
* Declare templates in MANIFEST and include package data by @thewhaleking in https://github.com/opentensor/btcli/pull/477
625

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ Installation steps are described below. For a full documentation on how to use `
3838

3939
## Install on macOS and Linux
4040

41-
You can install `btcli` on your local machine directly from source, or from PyPI. **Make sure you verify your installation after you install**:
41+
You can install `btcli` on your local machine directly from source, PyPI, or Homebrew. **Make sure you verify your installation after you install**:
4242

4343

44-
### Install from PyPI
44+
### Install from [PyPI](https://pypi.org/project/bittensor/)
4545

4646
Run
4747
```
@@ -53,6 +53,12 @@ Alternatively, if you prefer to use [uv](https://pypi.org/project/uv/):
5353
uv pip install bittensor-cli
5454
```
5555

56+
### Install from [Homebrew](https://formulae.brew.sh/formula/btcli#default)
57+
58+
```shell
59+
brew install btcli
60+
```
61+
5662
### Install from source
5763

5864
1. Create and activate a virtual environment.

bittensor_cli/cli.py

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python3
22
import asyncio
33
import curses
4+
import copy
45
import importlib
56
import json
67
import os.path
@@ -10,7 +11,7 @@
1011
import traceback
1112
import warnings
1213
from pathlib import Path
13-
from typing import Coroutine, Optional
14+
from typing import Coroutine, Optional, Union
1415
from dataclasses import fields
1516

1617
import rich
@@ -89,6 +90,23 @@ class Options:
8990
Re-usable typer args
9091
"""
9192

93+
@classmethod
94+
def edit_help(cls, option_name: str, help_text: str):
95+
"""
96+
Edits the `help` attribute of a copied given Typer option in this class, returning
97+
the modified Typer option.
98+
99+
Args:
100+
option_name: the name of the option (e.g. "wallet_name")
101+
help_text: New help text to be used (e.g. "Wallet's name")
102+
103+
Returns:
104+
Modified Typer Option with new help text.
105+
"""
106+
copied_attr = copy.copy(getattr(cls, option_name))
107+
setattr(copied_attr, "help", help_text)
108+
return copied_attr
109+
92110
wallet_name = typer.Option(
93111
None,
94112
"--wallet-name",
@@ -3202,7 +3220,11 @@ def stake_add(
32023220
help="When set, this command stakes to all hotkeys associated with the wallet. Do not use if specifying "
32033221
"hotkeys in `--include-hotkeys`.",
32043222
),
3205-
netuid: Optional[int] = Options.netuid_not_req,
3223+
netuids: Optional[str] = Options.edit_help(
3224+
"netuids",
3225+
"Netuid(s) to for which to add stake. Specify multiple netuids by separating with a comma, e.g."
3226+
"`btcli st add -n 1,2,3",
3227+
),
32063228
all_netuids: bool = Options.all_netuids,
32073229
wallet_name: str = Options.wallet_name,
32083230
wallet_path: str = Options.wallet_path,
@@ -3242,52 +3264,76 @@ def stake_add(
32423264
6. Stake all balance to a subnet:
32433265
[green]$[/green] btcli stake add --all --netuid 3
32443266
3267+
7. Stake the same amount to multiple subnets:
3268+
[green]$[/green] btcli stake add --amount 100 --netuids 4,5,6
3269+
32453270
[bold]Safe Staking Parameters:[/bold]
32463271
• [blue]--safe[/blue]: Enables rate tolerance checks
32473272
• [blue]--tolerance[/blue]: Maximum % rate change allowed (0.05 = 5%)
32483273
• [blue]--partial[/blue]: Complete partial stake if rates exceed tolerance
32493274
32503275
"""
3276+
netuids = netuids or []
32513277
self.verbosity_handler(quiet, verbose, json_output)
32523278
safe_staking = self.ask_safe_staking(safe_staking)
32533279
if safe_staking:
32543280
rate_tolerance = self.ask_rate_tolerance(rate_tolerance)
32553281
allow_partial_stake = self.ask_partial_stake(allow_partial_stake)
32563282
console.print("\n")
3257-
netuid = get_optional_netuid(netuid, all_netuids)
3283+
3284+
if netuids:
3285+
netuids = parse_to_list(
3286+
netuids, int, "Netuids must be ints separated by commas", False
3287+
)
3288+
else:
3289+
netuid_ = get_optional_netuid(None, all_netuids)
3290+
netuids = [netuid_] if netuid_ else None
3291+
if netuids:
3292+
for netuid_ in netuids:
3293+
# ensure no negative netuids make it into our list
3294+
validate_netuid(netuid_)
32583295

32593296
if stake_all and amount:
32603297
print_error(
32613298
"Cannot specify an amount and 'stake-all'. Choose one or the other."
32623299
)
3263-
raise typer.Exit()
3300+
return
32643301

32653302
if stake_all and not amount:
32663303
if not Confirm.ask("Stake all the available TAO tokens?", default=False):
3267-
raise typer.Exit()
3304+
return
3305+
3306+
if (
3307+
stake_all
3308+
and (isinstance(netuids, list) and len(netuids) > 1)
3309+
or (netuids is None)
3310+
):
3311+
print_error("Cannot stake all to multiple subnets.")
3312+
return
32683313

32693314
if all_hotkeys and include_hotkeys:
32703315
print_error(
32713316
"You have specified hotkeys to include and also the `--all-hotkeys` flag. The flag"
32723317
"should only be used standalone (to use all hotkeys) or with `--exclude-hotkeys`."
32733318
)
3274-
raise typer.Exit()
3319+
return
32753320

32763321
if include_hotkeys and exclude_hotkeys:
32773322
print_error(
32783323
"You have specified options for both including and excluding hotkeys. Select one or the other."
32793324
)
3280-
raise typer.Exit()
3325+
return
32813326

32823327
if not wallet_hotkey and not all_hotkeys and not include_hotkeys:
32833328
if not wallet_name:
32843329
wallet_name = Prompt.ask(
32853330
"Enter the [blue]wallet name[/blue]",
32863331
default=self.config.get("wallet_name") or defaults.wallet.name,
32873332
)
3288-
if netuid is not None:
3333+
if netuids is not None:
32893334
hotkey_or_ss58 = Prompt.ask(
3290-
"Enter the [blue]wallet hotkey[/blue] name or [blue]ss58 address[/blue] to stake to [dim](or Press Enter to view delegates)[/dim]",
3335+
"Enter the [blue]wallet hotkey[/blue] name or [blue]ss58 address[/blue] to stake to [dim]"
3336+
"(or Press Enter to view delegates)[/dim]",
32913337
)
32923338
else:
32933339
hotkey_or_ss58 = Prompt.ask(
@@ -3299,10 +3345,18 @@ def stake_add(
32993345
wallet = self.wallet_ask(
33003346
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
33013347
)
3348+
if len(netuids) > 1:
3349+
netuid_ = IntPrompt.ask(
3350+
"Enter the netuid for which to show delegates",
3351+
choices=[str(x) for x in netuids],
3352+
)
3353+
else:
3354+
netuid_ = netuids[0]
3355+
33023356
selected_hotkey = self._run_command(
33033357
subnets.show(
33043358
subtensor=self.initialize_chain(network),
3305-
netuid=netuid,
3359+
netuid=netuid_,
33063360
sort=False,
33073361
max_rows=12,
33083362
prompt=False,
@@ -3312,7 +3366,7 @@ def stake_add(
33123366
)
33133367
if not selected_hotkey:
33143368
print_error("No delegate selected. Exiting.")
3315-
raise typer.Exit()
3369+
return
33163370
include_hotkeys = selected_hotkey
33173371
elif is_valid_ss58_address(hotkey_or_ss58):
33183372
wallet = self.wallet_ask(
@@ -3373,8 +3427,8 @@ def stake_add(
33733427
)
33743428
if free_balance == Balance.from_tao(0):
33753429
print_error("You dont have any balance to stake.")
3376-
raise typer.Exit()
3377-
if netuid is not None:
3430+
return
3431+
if netuids:
33783432
amount = FloatPrompt.ask(
33793433
f"Amount to [{COLORS.G.SUBHEAD_MAIN}]stake (TAO τ)"
33803434
)
@@ -3396,7 +3450,7 @@ def stake_add(
33963450
add_stake.stake_add(
33973451
wallet,
33983452
self.initialize_chain(network),
3399-
netuid,
3453+
netuids,
34003454
stake_all,
34013455
amount,
34023456
prompt,
@@ -4796,12 +4850,9 @@ def subnets_list(
47964850
def subnets_price(
47974851
self,
47984852
network: Optional[list[str]] = Options.network,
4799-
netuids: str = typer.Option(
4800-
None,
4801-
"--netuids",
4802-
"--netuid",
4803-
"-n",
4804-
help="Netuid(s) to show the price for.",
4853+
netuids: str = Options.edit_help(
4854+
"netuids",
4855+
"Netuids to show the price for. Separate multiple netuids with a comma, for example: `-n 0,1,2`.",
48054856
),
48064857
interval_hours: int = typer.Option(
48074858
24,

bittensor_cli/src/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,8 @@ class WalletValidationTypes(Enum):
658658
"sudo_set_network_pow_registration_allowed",
659659
False,
660660
),
661+
"yuma3_enabled": ("sudo_set_yuma3_enabled", False),
662+
"alpha_sigmoid_steepness": ("sudo_set_alpha_sigmoid_steepness", True),
661663
}
662664

663665
# Help Panels for cli help

bittensor_cli/src/bittensor/chain_data.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ class SubnetHyperparameters(InfoBase):
177177
alpha_high: int
178178
alpha_low: int
179179
liquid_alpha_enabled: bool
180+
yuma3_enabled: bool
181+
alpha_sigmoid_steepness: int
180182

181183
@classmethod
182184
def _fix_decoded(
@@ -210,6 +212,8 @@ def _fix_decoded(
210212
alpha_high=decoded.get("alpha_high"),
211213
alpha_low=decoded.get("alpha_low"),
212214
liquid_alpha_enabled=decoded.get("liquid_alpha_enabled"),
215+
yuma3_enabled=decoded.get("yuma3_enabled"),
216+
alpha_sigmoid_steepness=decoded.get("alpha_sigmoid_steepness"),
213217
)
214218

215219

bittensor_cli/src/bittensor/subtensor_interface.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,14 +1128,22 @@ async def get_subnet_hyperparameters(
11281128
Understanding the hyperparameters is crucial for comprehending how subnets are configured and
11291129
managed, and how they interact with the network's consensus and incentive mechanisms.
11301130
"""
1131-
result = await self.query_runtime_api(
1132-
runtime_api="SubnetInfoRuntimeApi",
1133-
method="get_subnet_hyperparams",
1134-
params=[netuid],
1135-
block_hash=block_hash,
1131+
main_result, yuma3_result, sigmoid_steepness = await asyncio.gather(
1132+
self.query_runtime_api(
1133+
runtime_api="SubnetInfoRuntimeApi",
1134+
method="get_subnet_hyperparams",
1135+
params=[netuid],
1136+
block_hash=block_hash,
1137+
),
1138+
self.query("SubtensorModule", "Yuma3On", [netuid]),
1139+
self.query("SubtensorModule", "AlphaSigmoidSteepness", [netuid]),
11361140
)
1137-
1138-
if not result:
1141+
result = {
1142+
**main_result,
1143+
**{"yuma3_enabled": yuma3_result},
1144+
**{"alpha_sigmoid_steepness": sigmoid_steepness},
1145+
}
1146+
if not main_result:
11391147
return []
11401148

11411149
return SubnetHyperparameters.from_any(result)

0 commit comments

Comments
 (0)