Skip to content

Commit f621c13

Browse files
committed
Merge branch 'staging' into fix/thewhaleking/btcli-swap-status
2 parents cb18fba + dd0ce41 commit f621c13

19 files changed

+412
-108
lines changed

CHANGELOG.md

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

3+
## 9.7.0/2025-06-16
4+
5+
## What's Changed
6+
* Add `SKIP_PULL` variable for conftest.py by @basfroman in https://github.com/opentensor/btcli/pull/502
7+
* Feat: Adds netuid support in swap_hotkeys by @ibraheem-abe in https://github.com/opentensor/btcli/pull/505
8+
9+
**Full Changelog**: https://github.com/opentensor/btcli/compare/v9.6.0...v9.7.0
10+
11+
## 9.6.0/2025-06-12
12+
13+
## What's Changed
14+
* Allows for staking to multiple netuids in one btcli command by @thewhaleking in https://github.com/opentensor/btcli/pull/481
15+
* improve stake add json output by @thewhaleking in https://github.com/opentensor/btcli/pull/482
16+
* Apply bittensor error formatting to btcli by @thewhaleking in https://github.com/opentensor/btcli/pull/483
17+
* Add Yuma3 Enabled for Sudo Set/Get by @thewhaleking in https://github.com/opentensor/btcli/pull/487
18+
* Adds `alpha_sigmoid_steepness` call for hyperparams set/get by @thewhaleking in https://github.com/opentensor/btcli/pull/488
19+
* unstaking test fix by @thewhaleking in https://github.com/opentensor/btcli/pull/489
20+
* Merge issue: 488 by @thewhaleking in https://github.com/opentensor/btcli/pull/490
21+
* subnets check-start formatting blocks by @thewhaleking in https://github.com/opentensor/btcli/pull/491
22+
* Str vs Tuple by @thewhaleking in https://github.com/opentensor/btcli/pull/492
23+
* Add Homebrew Install to README by @thewhaleking in https://github.com/opentensor/btcli/pull/493
24+
* Update staking test for new subtensor by @thewhaleking in https://github.com/opentensor/btcli/pull/494
25+
26+
27+
**Full Changelog**: https://github.com/opentensor/btcli/compare/v9.5.1...v9.6.0
28+
329
## 9.5.1 /2025-06-02
30+
431
## What's Changed
532
* Declare templates in MANIFEST and include package data by @thewhaleking in https://github.com/opentensor/btcli/pull/477
633

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: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import traceback
1212
import warnings
1313
from pathlib import Path
14-
from typing import Coroutine, Optional, Union
14+
from typing import Coroutine, Optional
1515
from dataclasses import fields
1616

1717
import rich
@@ -929,11 +929,13 @@ def __init__(self):
929929
)(self.view_dashboard)
930930

931931
# Sub command aliases
932-
# Weights
932+
# Wallet
933933
self.wallet_app.command(
934934
"swap_hotkey",
935935
hidden=True,
936936
)(self.wallet_swap_hotkey)
937+
self.wallet_app.command("swap_coldkey", hidden=True)(self.wallet_swap_coldkey)
938+
self.wallet_app.command("swap_check", hidden=True)(self.wallet_check_ck_swap)
937939
self.wallet_app.command(
938940
"regen_coldkey",
939941
hidden=True,
@@ -962,10 +964,14 @@ def __init__(self):
962964
"get_identity",
963965
hidden=True,
964966
)(self.wallet_get_id)
967+
self.wallet_app.command("associate_hotkey")(self.wallet_associate_hotkey)
965968

966969
# Subnets
967970
self.subnets_app.command("burn_cost", hidden=True)(self.subnets_burn_cost)
968971
self.subnets_app.command("pow_register", hidden=True)(self.subnets_pow_register)
972+
self.subnets_app.command("set_identity", hidden=True)(self.subnets_set_identity)
973+
self.subnets_app.command("get_identity", hidden=True)(self.subnets_get_identity)
974+
self.subnets_app.command("check_start", hidden=True)(self.subnets_check_start)
969975

970976
# Sudo
971977
self.sudo_app.command("senate_vote", hidden=True)(self.sudo_senate_vote)
@@ -1897,6 +1903,8 @@ def wallet_swap_hotkey(
18971903
wallet_name: Optional[str] = Options.wallet_name,
18981904
wallet_path: Optional[str] = Options.wallet_path,
18991905
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
1906+
netuid: Optional[int] = Options.netuid_not_req,
1907+
all_netuids: bool = Options.all_netuids,
19001908
network: Optional[list[str]] = Options.network,
19011909
destination_hotkey_name: Optional[str] = typer.Argument(
19021910
None, help="Destination hotkey name."
@@ -1917,12 +1925,14 @@ def wallet_swap_hotkey(
19171925
19181926
- Make sure that your original key pair (coldkeyA, hotkeyA) is already registered.
19191927
- Make sure that you use a newly created hotkeyB in this command. A hotkeyB that is already registered cannot be used in this command.
1928+
- You can specify the netuid for which you want to swap the hotkey for. If it is not defined, the swap will be initiated for all subnets.
19201929
- Finally, note that this command requires a fee of 1 TAO for recycling and this fee is taken from your wallet (coldkeyA).
19211930
19221931
EXAMPLE
19231932
1924-
[green]$[/green] btcli wallet swap_hotkey destination_hotkey_name --wallet-name your_wallet_name --wallet-hotkey original_hotkey
1933+
[green]$[/green] btcli wallet swap_hotkey destination_hotkey_name --wallet-name your_wallet_name --wallet-hotkey original_hotkey --netuid 1
19251934
"""
1935+
netuid = get_optional_netuid(netuid, all_netuids)
19261936
self.verbosity_handler(quiet, verbose, json_output)
19271937
original_wallet = self.wallet_ask(
19281938
wallet_name,
@@ -1946,7 +1956,7 @@ def wallet_swap_hotkey(
19461956
self.initialize_chain(network)
19471957
return self._run_command(
19481958
wallets.swap_hotkey(
1949-
original_wallet, new_wallet, self.subtensor, prompt, json_output
1959+
original_wallet, new_wallet, self.subtensor, netuid, prompt, json_output
19501960
)
19511961
)
19521962

@@ -5021,6 +5031,7 @@ def subnets_create(
50215031
description: Optional[str] = typer.Option(
50225032
None, "--description", help="Description"
50235033
),
5034+
logo_url: Optional[str] = typer.Option(None, "--logo-url", help="Logo URL"),
50245035
additional_info: Optional[str] = typer.Option(
50255036
None, "--additional-info", help="Additional information"
50265037
),
@@ -5063,6 +5074,7 @@ def subnets_create(
50635074
subnet_url=subnet_url,
50645075
discord=discord,
50655076
description=description,
5077+
logo_url=logo_url,
50665078
additional=additional_info,
50675079
)
50685080
self._run_command(
@@ -5084,7 +5096,7 @@ def subnets_check_start(
50845096
This command verifies if a subnet's emission schedule can be started based on the subnet's registration block.
50855097
50865098
Example:
5087-
[green]$[/green] btcli subnets check_start --netuid 1
5099+
[green]$[/green] btcli subnets check-start --netuid 1
50885100
"""
50895101
self.verbosity_handler(quiet, verbose)
50905102
return self._run_command(
@@ -5186,6 +5198,7 @@ def subnets_set_identity(
51865198
description: Optional[str] = typer.Option(
51875199
None, "--description", help="Description"
51885200
),
5201+
logo_url: Optional[str] = typer.Option(None, "--logo-url", help="Logo URL"),
51895202
additional_info: Optional[str] = typer.Option(
51905203
None, "--additional-info", help="Additional information"
51915204
),
@@ -5237,6 +5250,7 @@ def subnets_set_identity(
52375250
subnet_url=subnet_url,
52385251
discord=discord,
52395252
description=description,
5253+
logo_url=logo_url,
52405254
additional=additional_info,
52415255
)
52425256

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: 6 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

@@ -626,6 +630,7 @@ class SubnetIdentity(InfoBase):
626630
subnet_url: str
627631
discord: str
628632
description: str
633+
logo_url: str
629634
additional: str
630635

631636
@classmethod
@@ -637,6 +642,7 @@ def _fix_decoded(cls, decoded: dict) -> "SubnetIdentity":
637642
subnet_url=bytes(decoded["subnet_url"]).decode(),
638643
discord=bytes(decoded["discord"]).decode(),
639644
description=bytes(decoded["description"]).decode(),
645+
logo_url=bytes(decoded["logo_url"]).decode(),
640646
additional=bytes(decoded["additional"]).decode(),
641647
)
642648

bittensor_cli/src/bittensor/extrinsics/registration.py

Lines changed: 56 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,7 +1611,8 @@ def _update_curr_block(
16111611
"""
16121612
Update the current block data with the provided block information and difficulty.
16131613
1614-
This function updates the current block and its difficulty in a thread-safe manner. It sets the current block
1614+
This function updates the current block
1615+
and its difficulty in a thread-safe manner. It sets the current block
16151616
number, hashes the block with the hotkey, updates the current block bytes, and packs the difficulty.
16161617
16171618
:param curr_diff: Shared array to store the current difficulty.
@@ -1745,6 +1746,7 @@ async def swap_hotkey_extrinsic(
17451746
subtensor: "SubtensorInterface",
17461747
wallet: Wallet,
17471748
new_wallet: Wallet,
1749+
netuid: Optional[int] = None,
17481750
prompt: bool = False,
17491751
) -> bool:
17501752
"""
@@ -1756,43 +1758,81 @@ async def swap_hotkey_extrinsic(
17561758
netuids_registered = await subtensor.get_netuids_for_hotkey(
17571759
wallet.hotkey.ss58_address, block_hash=block_hash
17581760
)
1759-
if not len(netuids_registered) > 0:
1761+
netuids_registered_new_hotkey = await subtensor.get_netuids_for_hotkey(
1762+
new_wallet.hotkey.ss58_address, block_hash=block_hash
1763+
)
1764+
1765+
if netuid is not None and netuid not in netuids_registered:
1766+
err_console.print(
1767+
f":cross_mark: [red]Failed[/red]: Original hotkey {wallet.hotkey.ss58_address} is not registered on subnet {netuid}"
1768+
)
1769+
return False
1770+
1771+
elif not len(netuids_registered) > 0:
17601772
err_console.print(
1761-
f"Destination hotkey [dark_orange]{new_wallet.hotkey.ss58_address}[/dark_orange] is not registered. "
1773+
f"Original hotkey [dark_orange]{wallet.hotkey.ss58_address}[/dark_orange] is not registered on any subnet. "
17621774
f"Please register and try again"
17631775
)
17641776
return False
17651777

1778+
if netuid is not None:
1779+
if netuid in netuids_registered_new_hotkey:
1780+
err_console.print(
1781+
f":cross_mark: [red]Failed[/red]: New hotkey {new_wallet.hotkey.ss58_address} "
1782+
f"is already registered on subnet {netuid}"
1783+
)
1784+
return False
1785+
else:
1786+
if len(netuids_registered_new_hotkey) > 0:
1787+
err_console.print(
1788+
f":cross_mark: [red]Failed[/red]: New hotkey {new_wallet.hotkey.ss58_address} "
1789+
f"is already registered on subnet(s) {netuids_registered_new_hotkey}"
1790+
)
1791+
return False
1792+
17661793
if not unlock_key(wallet).success:
17671794
return False
17681795

17691796
if prompt:
17701797
# Prompt user for confirmation.
1771-
if not Confirm.ask(
1772-
f"Do you want to swap [dark_orange]{wallet.name}[/dark_orange] hotkey \n\t"
1773-
f"[dark_orange]{wallet.hotkey.ss58_address}[/dark_orange] with hotkey \n\t"
1774-
f"[dark_orange]{new_wallet.hotkey.ss58_address}[/dark_orange]\n"
1775-
"This operation will cost [bold cyan]1 TAO t (recycled)[/bold cyan]"
1776-
):
1798+
if netuid is not None:
1799+
confirm_message = (
1800+
f"Do you want to swap [dark_orange]{wallet.name}[/dark_orange] hotkey \n\t"
1801+
f"[dark_orange]{wallet.hotkey.ss58_address} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
1802+
f"[dark_orange]{new_wallet.hotkey.ss58_address} ({new_wallet.hotkey_str})[/dark_orange] on subnet {netuid}\n"
1803+
"This operation will cost [bold cyan]1 TAO (recycled)[/bold cyan]"
1804+
)
1805+
else:
1806+
confirm_message = (
1807+
f"Do you want to swap [dark_orange]{wallet.name}[/dark_orange] hotkey \n\t"
1808+
f"[dark_orange]{wallet.hotkey.ss58_address} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
1809+
f"[dark_orange]{new_wallet.hotkey.ss58_address} ({new_wallet.hotkey_str})[/dark_orange] on all subnets\n"
1810+
"This operation will cost [bold cyan]1 TAO (recycled)[/bold cyan]"
1811+
)
1812+
1813+
if not Confirm.ask(confirm_message):
17771814
return False
17781815
print_verbose(
1779-
f"Swapping {wallet.name}'s hotkey ({wallet.hotkey.ss58_address}) with "
1780-
f"{new_wallet.name}s hotkey ({new_wallet.hotkey.ss58_address})"
1816+
f"Swapping {wallet.name}'s hotkey ({wallet.hotkey.ss58_address} - {wallet.hotkey_str}) with "
1817+
f"{new_wallet.name}'s hotkey ({new_wallet.hotkey.ss58_address} - {new_wallet.hotkey_str})"
17811818
)
17821819
with console.status(":satellite: Swapping hotkeys...", spinner="aesthetic"):
1820+
call_params = {
1821+
"hotkey": wallet.hotkey.ss58_address,
1822+
"new_hotkey": new_wallet.hotkey.ss58_address,
1823+
"netuid": netuid,
1824+
}
1825+
17831826
call = await subtensor.substrate.compose_call(
17841827
call_module="SubtensorModule",
17851828
call_function="swap_hotkey",
1786-
call_params={
1787-
"hotkey": wallet.hotkey.ss58_address,
1788-
"new_hotkey": new_wallet.hotkey.ss58_address,
1789-
},
1829+
call_params=call_params,
17901830
)
17911831
success, err_msg = await subtensor.sign_and_send_extrinsic(call, wallet)
17921832

17931833
if success:
17941834
console.print(
1795-
f"Hotkey {wallet.hotkey} swapped for new hotkey: {new_wallet.hotkey}"
1835+
f"Hotkey {wallet.hotkey.ss58_address} ({wallet.hotkey_str}) swapped for new hotkey: {new_wallet.hotkey.ss58_address} ({new_wallet.hotkey_str})"
17961836
)
17971837
return True
17981838
else:

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)