Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c209db7
Adds save func, uses html, adds wry option, adds save dir
ibraheem-abe Mar 4, 2025
58dd47d
fix: int() argument must be a string, a bytes-like object or a real n…
0xxfu Mar 5, 2025
c1625e4
Removes all the `raise typer.Exit()` calls throughout the commands, a…
thewhaleking Mar 5, 2025
4d41e84
Merge pull request #352 from 0xxfu/fix-get_stake_for_coldkey_and_hotk…
ibraheem-abe Mar 5, 2025
2cd2c13
Update bittensor_cli/cli.py
ibraheem-abe Mar 5, 2025
8039c78
[WIP] Changes to pyproject.toml
thewhaleking Mar 5, 2025
acc2432
Fixed the entrypoint.
thewhaleking Mar 5, 2025
e9496a5
Improves stake transfer, adds interactive selection of delegates
ibraheem-abe Mar 5, 2025
ebcf328
Merge pull request #357 from opentensor/feat/thewhaleking/change-to-p…
thewhaleking Mar 5, 2025
2e074ab
Merge pull request #350 from opentensor/feat/save-dashboard_and_browser
ibraheem-abe Mar 5, 2025
2206e35
Removes hidden flags when unstaking all
ibraheem-abe Mar 5, 2025
7189539
Merge pull request #358 from opentensor/fix/stake-transfer-selection
ibraheem-abe Mar 5, 2025
a782b41
Merge branch 'staging' into feat/thewhaleking/consistent-returns
thewhaleking Mar 6, 2025
d2b73c1
Merge pull request #359 from opentensor/fix/remove-hidden-flags-st-re…
ibraheem-abe Mar 6, 2025
6717c4e
Merge pull request #353 from opentensor/feat/thewhaleking/consistent-…
thewhaleking Mar 6, 2025
0327d92
Checks for tx fee inter-subnet movement
ibraheem-abe Mar 6, 2025
dbb0570
Adds verbosity handler to stake_move
ibraheem-abe Mar 6, 2025
a3ba80b
Adds help for hotkey ss58 + tao_check
ibraheem-abe Mar 6, 2025
1fe850c
Update bittensor_cli/cli.py
ibraheem-abe Mar 6, 2025
5ffa39e
Updates help
ibraheem-abe Mar 6, 2025
f825c61
Merge pull request #361 from opentensor/fix/add-transaction-fee-check
ibraheem-abe Mar 6, 2025
227c322
Merge branch 'main' into backmerge/main-staging-910
ibraheem-abe Mar 6, 2025
f461f90
Merge pull request #362 from opentensor/backmerge/main-staging-910
ibraheem-abe Mar 6, 2025
c15e19e
Bumps version and changelog
ibraheem-abe Mar 6, 2025
f2f8089
Bumps async substrate + removes artifacts
ibraheem-abe Mar 6, 2025
dea9335
Merge pull request #364 from opentensor/update/remove-requirements-txt
ibraheem-abe Mar 6, 2025
5b150c7
Merge branch 'staging' into release/9.1.1
ibraheem-abe Mar 6, 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
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## 9.1.1 /2025-03-06

## What's Changed
* fix: int() argument must be a string, a bytes-like object or a real n… by @0xxfu in https://github.com/opentensor/btcli/pull/352
* Change to pyproject toml by @thewhaleking in https://github.com/opentensor/btcli/pull/357
* Feat: Dashboard improvements by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/350
* Improves stake transfer, adds interactive selection of delegates by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/358
* Removes hidden flags for unstaking all by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/359
* Removes `typer.Exit` exceptions in commands by @thewhaleking in https://github.com/opentensor/btcli/pull/353
* Add transaction fee check inter-subnet movement by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/361
* Backmerge main to staging 910 by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/362

## New Contributors
* @0xxfu made their first contribution in https://github.com/opentensor/btcli/pull/352

**Full Changelog**: https://github.com/opentensor/btcli/compare/v9.1.0...v9.1.1

## 9.1.0 /2025-03-01

## What's Changed
Expand Down
153 changes: 115 additions & 38 deletions bittensor_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ class Options:
"--wallet.hotkey",
help="Hotkey of the wallet",
)
wallet_hotkey_ss58 = typer.Option(
None,
"--hotkey",
"--hotkey-ss58",
"-H",
"--wallet_hotkey",
"--wallet_hotkey_ss58",
"--wallet-hotkey",
"--wallet-hotkey-ss58",
"--wallet.hotkey",
help="Hotkey name or SS58 address of the hotkey",
)
mnemonic = typer.Option(
None,
help='Mnemonic used to regenerate your key. For example: "horse cart dog ..."',
Expand Down Expand Up @@ -246,6 +258,15 @@ class Options:
"--allow-partial/--not-partial",
help="Enable or disable partial stake mode (default: disabled).",
)
dashboard_path = typer.Option(
None,
"--dashboard-path",
"--dashboard_path",
"--dash_path",
"--dash.path",
"--dashboard.path",
help="Path to save the dashboard HTML file. For example: `~/.bittensor/dashboard`.",
)


def list_prompt(init_var: list, list_type: type, help_text: str) -> list:
Expand Down Expand Up @@ -525,6 +546,7 @@ def __init__(self):
"rate_tolerance": None,
"safe_staking": True,
"allow_partial_stake": False,
"dashboard_path": None,
# Commenting this out as this needs to get updated
# "metagraph_cols": {
# "UID": True,
Expand Down Expand Up @@ -1119,6 +1141,7 @@ def set_config(
"--partial/--no-partial",
"--allow/--not-allow",
),
dashboard_path: Optional[str] = Options.dashboard_path,
):
"""
Sets or updates configuration values in the BTCLI config file.
Expand Down Expand Up @@ -1148,6 +1171,7 @@ def set_config(
"rate_tolerance": rate_tolerance,
"safe_staking": safe_staking,
"allow_partial_stake": allow_partial_stake,
"dashboard_path": dashboard_path,
}
bools = ["use_cache", "safe_staking", "allow_partial_stake"]
if all(v is None for v in args.values()):
Expand Down Expand Up @@ -1257,6 +1281,7 @@ def del_config(
"--allow/--not-allow",
),
all_items: bool = typer.Option(False, "--all"),
dashboard_path: Optional[str] = Options.dashboard_path,
):
"""
Clears the fields in the config file and sets them to 'None'.
Expand Down Expand Up @@ -1288,6 +1313,7 @@ def del_config(
"rate_tolerance": rate_tolerance,
"safe_staking": safe_staking,
"allow_partial_stake": allow_partial_stake,
"dashboard_path": dashboard_path,
}

# If no specific argument is provided, iterate over all
Expand Down Expand Up @@ -2937,7 +2963,7 @@ def stake_add(
),
exit_early=False,
)
if selected_hotkey is None:
if not selected_hotkey:
print_error("No delegate selected. Exiting.")
raise typer.Exit()
include_hotkeys = selected_hotkey
Expand Down Expand Up @@ -3048,14 +3074,12 @@ def stake_remove(
False,
"--unstake-all",
"--all",
hidden=True,
help="When set, this command unstakes all staked TAO + Alpha from the all hotkeys.",
),
unstake_all_alpha: bool = typer.Option(
False,
"--unstake-all-alpha",
"--all-alpha",
hidden=True,
help="When set, this command unstakes all staked Alpha from the all hotkeys.",
),
amount: float = typer.Option(
Expand Down Expand Up @@ -3337,7 +3361,7 @@ def stake_move(
network: Optional[list[str]] = Options.network,
wallet_name: Optional[str] = Options.wallet_name,
wallet_path: Optional[str] = Options.wallet_path,
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
wallet_hotkey: Optional[str] = Options.wallet_hotkey_ss58,
origin_netuid: Optional[int] = typer.Option(
None, "--origin-netuid", help="Origin netuid"
),
Expand All @@ -3357,6 +3381,8 @@ def stake_move(
False, "--stake-all", "--all", help="Stake all", prompt=False
),
prompt: bool = Options.prompt,
quiet: bool = Options.quiet,
verbose: bool = Options.verbose,
):
"""
Move staked TAO between hotkeys while keeping the same coldkey ownership.
Expand All @@ -3378,6 +3404,7 @@ def stake_move(

[green]$[/green] btcli stake move
"""
self.verbosity_handler(quiet, verbose)
console.print(
"[dim]This command moves stake from one hotkey to another hotkey while keeping the same coldkey.[/dim]"
)
Expand Down Expand Up @@ -3490,7 +3517,7 @@ def stake_transfer(
network: Optional[list[str]] = Options.network,
wallet_name: Optional[str] = Options.wallet_name,
wallet_path: Optional[str] = Options.wallet_path,
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
wallet_hotkey: Optional[str] = Options.wallet_hotkey_ss58,
origin_netuid: Optional[int] = typer.Option(
None,
"--origin-netuid",
Expand All @@ -3514,6 +3541,9 @@ def stake_transfer(
"-a",
help="Amount of stake to transfer",
),
stake_all: bool = typer.Option(
False, "--stake-all", "--all", help="Stake all", prompt=False
),
prompt: bool = Options.prompt,
quiet: bool = Options.quiet,
verbose: bool = Options.verbose,
Expand All @@ -3531,6 +3561,8 @@ def stake_transfer(
- The destination subnet (--dest-netuid)
- The destination wallet/address (--dest)
- The amount to transfer (--amount)
- The origin wallet (--wallet-name)
- The origin hotkey wallet/address (--wallet-hotkey)

If no arguments are provided, an interactive selection menu will be shown.

Expand All @@ -3539,14 +3571,37 @@ def stake_transfer(
Transfer 100 TAO from subnet 1 to subnet 2:
[green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest wallet2 --amount 100

Using SS58 address:
Using Destination SS58 address:
[green]$[/green] btcli stake transfer --origin-netuid 1 --dest-netuid 2 --dest 5FrLxJsyJ5x9n2rmxFwosFraxFCKcXZDngEP9H7qjkKgHLcK --amount 100

Using Origin hotkey SS58 address (useful when transferring stake from a delegate):
[green]$[/green] btcli stake transfer --wallet-hotkey 5FrLxJsyJ5x9n2rmxFwosFraxFCKcXZDngEP9H7qjkKgHLcK --wallet-name sample_wallet

Transfer all available stake from origin hotkey:
[green]$[/green] btcli stake transfer --all --origin-netuid 1 --dest-netuid 2
"""
console.print(
"[dim]This command transfers stake from one coldkey to another while keeping the same hotkey.[/dim]"
)
self.verbosity_handler(quiet, verbose)

if not dest_ss58:
dest_ss58 = Prompt.ask(
"Enter the [blue]destination wallet name[/blue] or [blue]coldkey SS58 address[/blue]"
)

if is_valid_ss58_address(dest_ss58):
dest_ss58 = dest_ss58
else:
dest_wallet = self.wallet_ask(
dest_ss58,
wallet_path,
None,
ask_for=[WO.NAME, WO.PATH],
validate=WV.WALLET,
)
dest_ss58 = dest_wallet.coldkeypub.ss58_address

if not wallet_name:
wallet_name = Prompt.ask(
"Enter the [blue]origin wallet name[/blue]",
Expand All @@ -3556,13 +3611,16 @@ def stake_transfer(
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME]
)

interactive_selection = False
if not wallet_hotkey:
origin_hotkey = Prompt.ask(
"Enter the [blue]origin hotkey[/blue] name or "
"[blue]ss58 address[/blue] where the stake will be moved from",
default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
"Enter the [blue]origin hotkey[/blue] name or ss58 address [bold](stake will be transferred FROM here)[/bold] "
"[dim](or press Enter to select from existing stakes)[/dim]"
)
if is_valid_ss58_address(origin_hotkey):
if origin_hotkey == "":
interactive_selection = True

elif is_valid_ss58_address(origin_hotkey):
origin_hotkey = origin_hotkey
else:
wallet = self.wallet_ask(
Expand All @@ -3586,33 +3644,11 @@ def stake_transfer(
)
origin_hotkey = wallet.hotkey.ss58_address

if not dest_ss58:
dest_ss58 = Prompt.ask(
"Enter the [blue]destination wallet name[/blue] or [blue]coldkey SS58 address[/blue]"
)

if is_valid_ss58_address(dest_ss58):
dest_ss58 = dest_ss58
else:
dest_wallet = self.wallet_ask(
dest_ss58,
wallet_path,
None,
ask_for=[WO.NAME, WO.PATH],
validate=WV.WALLET,
)
dest_ss58 = dest_wallet.coldkeypub.ss58_address

interactive_selection = False
if origin_netuid is None and dest_netuid is None and not amount:
interactive_selection = True
else:
if not interactive_selection:
if origin_netuid is None:
origin_netuid = IntPrompt.ask(
"Enter the [blue]origin subnet[/blue] (netuid)"
)
if not amount:
amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to transfer")

if dest_netuid is None:
dest_netuid = IntPrompt.ask(
Expand All @@ -3629,6 +3665,7 @@ def stake_transfer(
dest_coldkey_ss58=dest_ss58,
amount=amount,
interactive_selection=interactive_selection,
stake_all=stake_all,
prompt=prompt,
)
)
Expand Down Expand Up @@ -5091,20 +5128,60 @@ def view_dashboard(
wallet_name: str = Options.wallet_name,
wallet_path: str = Options.wallet_path,
wallet_hotkey: str = Options.wallet_hotkey,
coldkey_ss58: Optional[str] = typer.Option(
None,
"--coldkey-ss58",
"--ss58",
help="Coldkey SS58 address to view dashboard for",
),
use_wry: bool = typer.Option(
False, "--use-wry", help="Use PyWry instead of browser window"
),
save_file: bool = typer.Option(
False, "--save-file", "--save", help="Save the dashboard HTML file"
),
dashboard_path: Optional[str] = Options.dashboard_path,
quiet: bool = Options.quiet,
verbose: bool = Options.verbose,
):
"""
Display html dashboard with subnets list, stake, and neuron information.
"""
self.verbosity_handler(quiet, verbose)
if is_linux():
if use_wry and is_linux():
print_linux_dependency_message()
wallet = self.wallet_ask(
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
)

if use_wry and save_file:
print_error("Cannot save file when using PyWry.")
raise typer.Exit()

if save_file:
if not dashboard_path:
dashboard_path = Prompt.ask(
"Enter the [blue]path[/blue] where the dashboard HTML file will be saved",
default=self.config.get("dashboard_path")
or defaults.dashboard.path,
)

if coldkey_ss58:
if not is_valid_ss58_address(coldkey_ss58):
print_error(f"Invalid SS58 address: {coldkey_ss58}")
raise typer.Exit()
wallet = None
else:
wallet = self.wallet_ask(
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
)

return self._run_command(
view.display_network_dashboard(wallet, self.initialize_chain(network))
view.display_network_dashboard(
wallet=wallet,
subtensor=self.initialize_chain(network),
use_wry=use_wry,
save_file=save_file,
dashboard_path=dashboard_path,
coldkey_ss58=coldkey_ss58,
)
)

@staticmethod
Expand Down
3 changes: 3 additions & 0 deletions bittensor_cli/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ class logging:
record_log = False
logging_dir = "~/.bittensor/miners"

class dashboard:
path = "~/.bittensor/dashboard/"


defaults = Defaults

Expand Down
2 changes: 1 addition & 1 deletion bittensor_cli/src/bittensor/subtensor_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ async def get_stake_for_coldkey_and_hotkey_on_netuid(
if _result is None:
return Balance(0).set_unit(netuid)
else:
return Balance.from_rao(_result).set_unit(int(netuid))
return Balance.from_rao(fixed_to_float(_result)).set_unit(int(netuid))

async def get_metagraph_info(
self, netuid: int, block_hash: Optional[str] = None
Expand Down
18 changes: 17 additions & 1 deletion bittensor_cli/src/bittensor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,33 @@ def __init__(self, hotkey_ss58=None):
self.ss58_address = hotkey_ss58


class _Coldkeypub:
def __init__(self, coldkey_ss58=None):
self.ss58_address = coldkey_ss58


class WalletLike:
def __init__(self, name=None, hotkey_ss58=None, hotkey_str=None):
def __init__(
self,
name=None,
hotkey_ss58=None,
hotkey_str=None,
coldkeypub_ss58=None,
):
self.name = name
self.hotkey_ss58 = hotkey_ss58
self.hotkey_str = hotkey_str
self._hotkey = _Hotkey(hotkey_ss58)
self._coldkeypub = _Coldkeypub(coldkeypub_ss58)

@property
def hotkey(self):
return self._hotkey

@property
def coldkeypub(self):
return self._coldkeypub


def print_console(message: str, colour: str, title: str, console: Console):
console.print(
Expand Down
Loading
Loading