Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
be50955
Removes double param for `--cache` in `config set`
thewhaleking Aug 7, 2025
f7490ca
Merge pull request #579 from opentensor/fix/thewhaleking/double-params
thewhaleking Aug 7, 2025
fdf0ad1
Updates the previously not-root-only hyperparams to root sudo only
thewhaleking Aug 7, 2025
099f8d8
Test
thewhaleking Aug 7, 2025
e44d6e1
Update return types for better json output
thewhaleking Aug 7, 2025
217ead5
Ruff
thewhaleking Aug 7, 2025
ac40408
Didn't specify the wallet in the test
thewhaleking Aug 7, 2025
7bdf47d
Better error formatting
thewhaleking Aug 7, 2025
48f2a31
Merge pull request #580 from opentensor/fix/thewhaleking/change-root-…
thewhaleking Aug 7, 2025
a533d1d
Merge pull request #581 from opentensor/fix/thewhaleking/err-formatting
thewhaleking Aug 7, 2025
1ce3344
Better handles situations in which users may type something besides a…
thewhaleking Aug 8, 2025
498a1f7
Merge pull request #582 from opentensor/fix/thewhaleking/handle-int-v…
thewhaleking Aug 8, 2025
9ed7cda
Adds `moving_price` attr to DynamicInfo
thewhaleking Aug 11, 2025
41f4bfe
Catch additional exceptions for generating the hotkeypub
thewhaleking Aug 12, 2025
9c70d4a
Ensure that overwrite arg is correctly passed when creating wallet.
thewhaleking Aug 12, 2025
7e2f0d5
Merge pull request #585 from opentensor/fix/thewhaleking/wallet-fixes
thewhaleking Aug 12, 2025
339d7e1
Merge pull request #583 from opentensor/feat/thewhaleking/add-moving-…
thewhaleking Aug 12, 2025
908fdb0
Changelog + version
thewhaleking Aug 12, 2025
db8387a
Merge pull request #586 from opentensor/changelog/9.10.1
thewhaleking Aug 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 9.10.1 /2025-08-12
* Removes double param for `--cache` in `config set` by @thewhaleking in https://github.com/opentensor/btcli/pull/579
* change root only sudo hyperparams by @thewhaleking in https://github.com/opentensor/btcli/pull/580
* Better error formatting by @thewhaleking in https://github.com/opentensor/btcli/pull/581
* Handle optional netuid better by @thewhaleking in https://github.com/opentensor/btcli/pull/582
* wallet fixes by @thewhaleking in https://github.com/opentensor/btcli/pull/585
* Adds `moving_price` attr to DynamicInfo by @thewhaleking in https://github.com/opentensor/btcli/pull/583

**Full Changelog**: https://github.com/opentensor/btcli/compare/v9.10.0...v9.10.1

## 9.10.0 /2025-08-06
* Sets default interval hours for subnets price to 4, bc of rate limiting. by @thewhaleking in https://github.com/opentensor/btcli/pull/568
* Subnets Price --current + improvements by @thewhaleking in https://github.com/opentensor/btcli/pull/569
Expand Down
13 changes: 9 additions & 4 deletions bittensor_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,10 +410,15 @@ def get_optional_netuid(netuid: Optional[int], all_netuids: bool) -> Optional[in
)
if answer is None:
return None
answer = answer.strip()
if answer.lower() == "all":
return None
else:
return int(answer)
try:
return int(answer)
except ValueError:
err_console.print(f"Invalid netuid: {answer}")
return get_optional_netuid(None, False)
else:
return netuid

Expand Down Expand Up @@ -1264,7 +1269,7 @@ def set_config(
use_cache: Optional[bool] = typer.Option(
None,
"--cache/--no-cache",
"--cache/--no_cache",
" /--no_cache",
help="Disable caching of some commands. This will disable the `--reuse-last` and `--html` flags on "
"commands such as `subnets metagraph`, `stake show` and `subnets list`.",
),
Expand Down Expand Up @@ -4816,7 +4821,7 @@ def sudo_set(
wallet = self.wallet_ask(
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
)
result = self._run_command(
result, err_msg = self._run_command(
sudo.sudo_set_hyperparameter(
wallet,
self.initialize_chain(network),
Expand All @@ -4828,7 +4833,7 @@ def sudo_set(
)
)
if json_output:
json_console.print(json.dumps({"success": result}))
json_console.print(json.dumps({"success": result, "err_msg": err_msg}))
return result

def sudo_get(
Expand Down
8 changes: 4 additions & 4 deletions bittensor_cli/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,10 +631,10 @@ class WalletValidationTypes(Enum):
"min_allowed_weights": ("sudo_set_min_allowed_weights", False),
"max_weights_limit": ("sudo_set_max_weight_limit", False),
"tempo": ("sudo_set_tempo", True),
"min_difficulty": ("sudo_set_min_difficulty", False),
"min_difficulty": ("sudo_set_min_difficulty", True),
"max_difficulty": ("sudo_set_max_difficulty", False),
"weights_version": ("sudo_set_weights_version_key", False),
"weights_rate_limit": ("sudo_set_weights_set_rate_limit", False),
"weights_rate_limit": ("sudo_set_weights_set_rate_limit", True),
"adjustment_interval": ("sudo_set_adjustment_interval", True),
"activity_cutoff": ("sudo_set_activity_cutoff", False),
"target_regs_per_interval": ("sudo_set_target_registrations_per_interval", True),
Expand All @@ -645,15 +645,15 @@ class WalletValidationTypes(Enum):
"serving_rate_limit": ("sudo_set_serving_rate_limit", False),
"max_validators": ("sudo_set_max_allowed_validators", True),
"adjustment_alpha": ("sudo_set_adjustment_alpha", False),
"difficulty": ("sudo_set_difficulty", False),
"difficulty": ("sudo_set_difficulty", True),
"commit_reveal_period": (
"sudo_set_commit_reveal_weights_interval",
False,
),
"commit_reveal_weights_enabled": ("sudo_set_commit_reveal_weights_enabled", False),
"alpha_values": ("sudo_set_alpha_values", False),
"liquid_alpha_enabled": ("sudo_set_liquid_alpha_enabled", False),
"registration_allowed": ("sudo_set_network_registration_allowed", False),
"registration_allowed": ("sudo_set_network_registration_allowed", True),
"network_pow_registration_allowed": (
"sudo_set_network_pow_registration_allowed",
False,
Expand Down
2 changes: 2 additions & 0 deletions bittensor_cli/src/bittensor/chain_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,7 @@ class DynamicInfo(InfoBase):
network_registered_at: int
subnet_identity: Optional[SubnetIdentity]
subnet_volume: Balance
moving_price: float

@classmethod
def _fix_decoded(cls, decoded: Any) -> "DynamicInfo":
Expand Down Expand Up @@ -786,6 +787,7 @@ def _fix_decoded(cls, decoded: Any) -> "DynamicInfo":
network_registered_at=int(decoded.get("network_registered_at")),
subnet_identity=subnet_identity,
subnet_volume=subnet_volume,
moving_price=fixed_to_float(decoded["moving_price"], 32),
)

def tao_to_alpha(self, tao: Balance) -> Balance:
Expand Down
6 changes: 4 additions & 2 deletions bittensor_cli/src/bittensor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,9 @@ def format_error_message(error_message: Union[dict, Exception]) -> str:
err_type = error_message.get("type", err_type)
err_name = error_message.get("name", err_name)
err_docs = error_message.get("docs", [err_description])
err_description = " ".join(err_docs)
err_description = (
" ".join(err_docs) if not isinstance(err_docs, str) else err_docs
)
err_description += (
f" | Please consult {BT_DOCS_LINK}/errors/subtensor#{err_name.lower()}"
)
Expand Down Expand Up @@ -1441,5 +1443,5 @@ def get_hotkey_pub_ss58(wallet: Wallet) -> str:
"""
try:
return wallet.hotkeypub.ss58_address
except KeyFileError:
except (KeyFileError, AttributeError):
return wallet.hotkey.ss58_address
44 changes: 23 additions & 21 deletions bittensor_cli/src/commands/sudo.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ async def set_hyperparameter_extrinsic(
wait_for_inclusion: bool = False,
wait_for_finalization: bool = True,
prompt: bool = True,
) -> bool:
) -> tuple[bool, str]:
"""Sets a hyperparameter for a specific subnetwork.

:param subtensor: initialized SubtensorInterface object
Expand All @@ -200,13 +200,14 @@ async def set_hyperparameter_extrinsic(
params=[netuid],
)
if subnet_owner != wallet.coldkeypub.ss58_address:
err_console.print(
err_msg = (
":cross_mark: [red]This wallet doesn't own the specified subnet.[/red]"
)
return False
err_console.print(err_msg)
return False, err_msg

if not unlock_key(wallet).success:
return False
if not (ulw := unlock_key(wallet)).success:
return False, ulw.message

arbitrary_extrinsic = False

Expand All @@ -218,15 +219,14 @@ async def set_hyperparameter_extrinsic(
)
extrinsic = parameter
if not arbitrary_extrinsic:
err_console.print(
":cross_mark: [red]Invalid hyperparameter specified.[/red]"
)
return False
err_msg = ":cross_mark: [red]Invalid hyperparameter specified.[/red]"
err_console.print(err_msg)
return False, err_msg
if sudo_ and prompt:
if not Confirm.ask(
"This hyperparam is only settable by root sudo users. If you are not, this will fail. Please confirm"
):
return False
return False, "This hyperparam is only settable by root sudo users"

substrate = subtensor.substrate
msg_value = value if not arbitrary_extrinsic else call_params
Expand Down Expand Up @@ -254,10 +254,11 @@ async def set_hyperparameter_extrinsic(
]

if len(value) < len(non_netuid_fields):
err_console.print(
err_msg = (
"Not enough values provided in the list for all parameters"
)
return False
err_console.print(err_msg)
return False, err_msg

call_params.update(
{name: val for name, val in zip(non_netuid_fields, value)}
Expand Down Expand Up @@ -290,20 +291,20 @@ async def set_hyperparameter_extrinsic(
)
if not success:
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
return False
return False, err_msg
elif arbitrary_extrinsic:
console.print(
f":white_heavy_check_mark: "
f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]"
)
return True
return True, ""
# Successful registration, final check for membership
else:
console.print(
f":white_heavy_check_mark: "
f"[dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]"
)
return True
return True, ""


async def _get_senate_members(
Expand Down Expand Up @@ -619,25 +620,26 @@ async def sudo_set_hyperparameter(
param_value: Optional[str],
prompt: bool,
json_output: bool,
):
) -> tuple[bool, str]:
"""Set subnet hyperparameters."""
is_allowed_value, value = allowed_value(param_name, param_value)
if not is_allowed_value:
err_console.print(
err_msg = (
f"Hyperparameter [dark_orange]{param_name}[/dark_orange] value is not within bounds. "
f"Value is {param_value} but must be {value}"
)
return False
success = await set_hyperparameter_extrinsic(
err_console.print(err_msg)
return False, err_msg
success, err_msg = await set_hyperparameter_extrinsic(
subtensor, wallet, netuid, param_name, value, prompt=prompt
)
if json_output:
return success
return success, err_msg
if success:
console.print("\n")
print_verbose("Fetching hyperparameters")
await get_hyperparameters(subtensor, netuid=netuid)
return success
return success, err_msg


async def get_hyperparameters(
Expand Down
10 changes: 6 additions & 4 deletions bittensor_cli/src/commands/wallets.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,13 +452,15 @@ async def wallet_create(
"error": "",
"data": None,
}

if uri:
try:
keypair = Keypair.create_from_uri(uri)
wallet.set_coldkey(keypair=keypair, encrypt=False, overwrite=False)
wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=False)
wallet.set_hotkey(keypair=keypair, encrypt=False, overwrite=False)
wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=False)
wallet.set_coldkey(keypair=keypair, encrypt=False, overwrite=overwrite)
wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=overwrite)
wallet.set_hotkey(keypair=keypair, encrypt=False, overwrite=overwrite)
wallet.set_hotkeypub(keypair=keypair, encrypt=False, overwrite=overwrite)
wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=overwrite)
output_dict["success"] = True
output_dict["data"] = {
"name": wallet.name,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "bittensor-cli"
version = "9.10.0"
version = "9.10.1"
description = "Bittensor CLI"
readme = "README.md"
authors = [
Expand Down
116 changes: 116 additions & 0 deletions tests/e2e_tests/test_hyperparams_setting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import json

from bittensor_cli.src import HYPERPARAMS

"""
Verify commands:

* btcli subnets create
* btcli sudo set
* btcli sudo get
"""


def test_hyperparams_setting(local_chain, wallet_setup):
netuid = 2
wallet_path_alice = "//Alice"
# Create wallet for Alice
keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup(
wallet_path_alice
)

# Register a subnet with sudo as Alice
result = exec_command_alice(
command="subnets",
sub_command="create",
extra_args=[
"--wallet-path",
wallet_path_alice,
"--network",
"ws://127.0.0.1:9945",
"--wallet-name",
wallet_alice.name,
"--wallet-hotkey",
wallet_alice.hotkey_str,
"--subnet-name",
"Test Subnet",
"--repo",
"https://github.com/username/repo",
"--contact",
"alice@opentensor.dev",
"--url",
"https://testsubnet.com",
"--discord",
"alice#1234",
"--description",
"A test subnet for e2e testing",
"--additional-info",
"Created by Alice",
"--logo-url",
"https://testsubnet.com/logo.png",
"--no-prompt",
"--json-output",
],
)
result_output = json.loads(result.stdout)
assert result_output["success"] is True
assert result_output["netuid"] == netuid
print(result_output)

# Fetch the hyperparameters of the subnet
hyperparams = exec_command_alice(
command="sudo",
sub_command="get",
extra_args=[
"--network",
"ws://127.0.0.1:9945",
"--netuid",
netuid,
"--json-out",
],
)

# Parse all hyperparameters and single out max_burn in TAO
all_hyperparams = json.loads(hyperparams.stdout)
hp = {}
for hyperparam in all_hyperparams:
hp[hyperparam["hyperparameter"]] = hyperparam["value"]
for key, (_, sudo_only) in HYPERPARAMS.items():
if key in hp.keys() and not sudo_only:
if isinstance(hp[key], bool):
new_val = not hp[key]
elif isinstance(hp[key], int):
if hp[key] < 100:
new_val = hp[key] + 1
else:
new_val = hp[key] - 1
else:
raise ValueError(
f"Unrecognized hyperparameter value type: {key}: {hp[key]}"
)
cmd = exec_command_alice(
command="sudo",
sub_command="set",
extra_args=[
"--wallet-path",
wallet_path_alice,
"--network",
"ws://127.0.0.1:9945",
"--wallet-name",
wallet_alice.name,
"--wallet-hotkey",
wallet_alice.hotkey_str,
"--netuid",
netuid,
"--json-out",
"--no-prompt",
"--param",
key,
"--value",
new_val,
],
)
cmd_json = json.loads(cmd.stdout)
assert cmd_json["success"] is True, (key, new_val, cmd.stdout, cmd_json)
print(f"Successfully set hyperparameter {key} to value {new_val}")
print("Successfully set hyperparameters")
Loading