diff --git a/bittensor_cli/src/bittensor/chain_data.py b/bittensor_cli/src/bittensor/chain_data.py index 628821ae..ddbbc724 100644 --- a/bittensor_cli/src/bittensor/chain_data.py +++ b/bittensor_cli/src/bittensor/chain_data.py @@ -745,7 +745,9 @@ def tao_to_alpha(self, tao: Balance) -> Balance: def alpha_to_tao(self, alpha: Balance) -> Balance: return Balance.from_tao(alpha.tao * self.price.tao) - def tao_to_alpha_with_slippage(self, tao: Balance) -> tuple[Balance, Balance]: + def tao_to_alpha_with_slippage( + self, tao: Balance + ) -> tuple[Balance, Balance, float]: """ Returns an estimate of how much Alpha would a staker receive if they stake their tao using the current pool state. Args: diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index 9e8692e8..dc060697 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -207,7 +207,7 @@ async def get_stake_for_coldkey( if result is None: return [] - stakes = StakeInfo.list_from_any(result) + stakes: list[StakeInfo] = StakeInfo.list_from_any(result) return [stake for stake in stakes if stake.stake > 0] async def get_stake_for_coldkey_and_hotkey( @@ -352,14 +352,12 @@ async def get_total_stake_for_coldkey( self, *ss58_addresses, block_hash: Optional[str] = None, - reuse_block: bool = False, ) -> dict[str, tuple[Balance, Balance]]: """ Returns the total stake held on a coldkey. :param ss58_addresses: The SS58 address(es) of the coldkey(s) :param block_hash: The hash of the block number to retrieve the stake from. - :param reuse_block: Whether to reuse the last-used block hash when retrieving info. :return: {address: Balance objects} """ diff --git a/bittensor_cli/src/bittensor/utils.py b/bittensor_cli/src/bittensor/utils.py index 35e3394a..318847c8 100644 --- a/bittensor_cli/src/bittensor/utils.py +++ b/bittensor_cli/src/bittensor/utils.py @@ -5,7 +5,6 @@ import sqlite3 import platform import webbrowser -import sys from pathlib import Path from typing import TYPE_CHECKING, Any, Collection, Optional, Union, Callable from urllib.parse import urlparse @@ -73,8 +72,8 @@ def coldkeypub(self): return self._coldkeypub -def print_console(message: str, colour: str, title: str, console: Console): - console.print( +def print_console(message: str, colour: str, title: str, console_: Console): + console_.print( f"[bold {colour}][{title}]:[/bold {colour}] [{colour}]{message}[/{colour}]\n" ) diff --git a/bittensor_cli/src/commands/stake/__init__.py b/bittensor_cli/src/commands/stake/__init__.py index d1dceeab..e69de29b 100644 --- a/bittensor_cli/src/commands/stake/__init__.py +++ b/bittensor_cli/src/commands/stake/__init__.py @@ -1,154 +0,0 @@ -from typing import Optional, TYPE_CHECKING - -import rich.prompt -from rich.table import Table - -from bittensor_cli.src.bittensor.chain_data import DelegateInfoLite -from bittensor_cli.src.bittensor.utils import console - -if TYPE_CHECKING: - from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface - - -async def select_delegate(subtensor: "SubtensorInterface", netuid: int): - # Get a list of delegates and sort them by total stake in descending order - delegates: list[DelegateInfoLite] = ( - await subtensor.get_delegates_by_netuid_light(netuid) - ).sort(key=lambda x: x.total_stake, reverse=True) - - # Get registered delegates details. - registered_delegate_info = await subtensor.get_delegate_identities() - - # Create a table to display delegate information - table = Table( - show_header=True, - header_style="bold", - border_style="rgb(7,54,66)", - style="rgb(0,43,54)", - ) - - # Add columns to the table with specific styles - table.add_column("Index", style="rgb(253,246,227)", no_wrap=True) - table.add_column("Delegate Name", no_wrap=True) - table.add_column("Hotkey SS58", style="rgb(211,54,130)", no_wrap=True) - table.add_column("Owner SS58", style="rgb(133,153,0)", no_wrap=True) - table.add_column("Take", style="rgb(181,137,0)", no_wrap=True) - table.add_column( - "Total Stake", style="rgb(38,139,210)", no_wrap=True, justify="right" - ) - table.add_column( - "Owner Stake", style="rgb(220,50,47)", no_wrap=True, justify="right" - ) - # table.add_column("Return per 1000", style="rgb(108,113,196)", no_wrap=True, justify="right") - # table.add_column("Total Daily Return", style="rgb(42,161,152)", no_wrap=True, justify="right") - - # List to store visible delegates - visible_delegates = [] - - def get_user_input() -> str: - return rich.prompt.Prompt.ask( - 'Press Enter to scroll, enter a number (1-N) to select, or type "h" for help: ', - choices=["", "h"] + [str(x) for x in range(1, len(delegates) - 1)], - show_choices=True, - ) - - # TODO: Add pagination to handle large number of delegates more efficiently - # Iterate through delegates and display their information - - def loop_selections() -> Optional[int]: - idx = 0 - selected_idx = None - while idx < len(delegates): - if idx < len(delegates): - delegate = delegates[idx] - - # Add delegate to visible list - visible_delegates.append(delegate) - - # Add a row to the table with delegate information - table.add_row( - str(idx), - registered_delegate_info[delegate.hotkey_ss58].name - if delegate.hotkey_ss58 in registered_delegate_info - else "", - delegate.hotkey_ss58[:5] - + "..." - + delegate.hotkey_ss58[-5:], # Show truncated hotkey - delegate.owner_ss58[:5] - + "..." - + delegate.owner_ss58[-5:], # Show truncated owner address - f"{delegate.take:.6f}", - f"τ{delegate.total_stake.tao:,.4f}", - f"τ{delegate.owner_stake.tao:,.4f}", - # f"τ{delegate.return_per_1000.tao:,.4f}", - # f"τ{delegate.total_daily_return.tao:,.4f}", - ) - - # Clear console and print updated table - console.clear() - console.print(table) - - # Prompt user for input - user_input: str = get_user_input() - - # Add a help option to display information about each column - if user_input == "h": - console.print("\nColumn Information:") - console.print( - "[rgb(253,246,227)]Index:[/rgb(253,246,227)] Position in the list of delegates" - ) - console.print( - "[rgb(211,54,130)]Hotkey SS58:[/rgb(211,54,130)] Truncated public key of the delegate's hotkey" - ) - console.print( - "[rgb(133,153,0)]Owner SS58:[/rgb(133,153,0)] Truncated public key of the delegate's owner" - ) - console.print( - "[rgb(181,137,0)]Take:[/rgb(181,137,0)] Percentage of rewards the delegate takes" - ) - console.print( - "[rgb(38,139,210)]Total Stake:[/rgb(38,139,210)] Total amount staked to this delegate" - ) - console.print( - "[rgb(220,50,47)]Owner Stake:[/rgb(220,50,47)] Amount staked by the delegate owner" - ) - console.print( - "[rgb(108,113,196)]Return per 1000:[/rgb(108,113,196)] Estimated return for 1000 Tao staked" - ) - console.print( - "[rgb(42,161,152)]Total Daily Return:[/rgb(42,161,152)] Estimated total daily return for all stake" - ) - user_input = get_user_input() - - # If user presses Enter, continue to next delegate - if user_input and user_input != "h": - selected_idx = int(user_input) - break - - if idx < len(delegates): - idx += 1 - - return selected_idx - - # TODO( const ): uncomment for check - # Add a confirmation step before returning the selected delegate - # console.print(f"\nSelected delegate: [rgb(211,54,130)]{visible_delegates[selected_idx].hotkey_ss58}[/rgb(211,54,130)]") - # console.print(f"Take: [rgb(181,137,0)]{visible_delegates[selected_idx].take:.6f}[/rgb(181,137,0)]") - # console.print(f"Total Stake: [rgb(38,139,210)]{visible_delegates[selected_idx].total_stake}[/rgb(38,139,210)]") - - # confirmation = Prompt.ask("Do you want to proceed with this delegate? (y/n)") - # if confirmation.lower() != 'yes' and confirmation.lower() != 'y': - # return select_delegate( subtensor, netuid ) - - # Return the selected delegate - while True: - selected_idx_ = loop_selections() - if selected_idx_ is None: - if not rich.prompt.Confirm.ask( - "You've reached the end of the list. You must make a selection. Loop through again?" - ): - raise IndexError - else: - continue - else: - return delegates[selected_idx_] diff --git a/bittensor_cli/src/commands/stake/add.py b/bittensor_cli/src/commands/stake/add.py index 6932a229..6d156f52 100644 --- a/bittensor_cli/src/commands/stake/add.py +++ b/bittensor_cli/src/commands/stake/add.py @@ -46,9 +46,7 @@ async def stake_add( netuid: the netuid to stake to (None indicates all subnets) stake_all: whether to stake all available balance amount: specified amount of balance to stake - delegate: whether to delegate stake, currently unused prompt: whether to prompt the user - max_stake: maximum amount to stake (used in combination with stake_all), currently unused all_hotkeys: whether to stake all hotkeys include_hotkeys: list of hotkeys to include in staking process (if not specifying `--all`) exclude_hotkeys: list of hotkeys to exclude in staking (if specifying `--all`) @@ -61,18 +59,16 @@ async def stake_add( """ async def safe_stake_extrinsic( - netuid: int, - amount: Balance, + netuid_: int, + amount_: Balance, current_stake: Balance, - hotkey_ss58: str, + hotkey_ss58_: str, price_limit: Balance, - wallet: Wallet, - subtensor: "SubtensorInterface", status=None, ) -> None: err_out = partial(print_error, status=status) failure_prelude = ( - f":cross_mark: [red]Failed[/red] to stake {amount} on Netuid {netuid}" + f":cross_mark: [red]Failed[/red] to stake {amount_} on Netuid {netuid_}" ) current_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address) next_nonce = await subtensor.substrate.get_account_next_index( @@ -82,9 +78,9 @@ async def safe_stake_extrinsic( call_module="SubtensorModule", call_function="add_stake_limit", call_params={ - "hotkey": hotkey_ss58, - "netuid": netuid, - "amount_staked": amount.rao, + "hotkey": hotkey_ss58_, + "netuid": netuid_, + "amount_staked": amount_.rao, "limit_price": price_limit, "allow_partial": allow_partial_stake, }, @@ -119,30 +115,30 @@ async def safe_stake_extrinsic( new_balance, new_stake = await asyncio.gather( subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash), subtensor.get_stake( - hotkey_ss58=hotkey_ss58, + hotkey_ss58=hotkey_ss58_, coldkey_ss58=wallet.coldkeypub.ss58_address, - netuid=netuid, + netuid=netuid_, block_hash=block_hash, ), ) console.print( - f":white_heavy_check_mark: [dark_sea_green3]Finalized. Stake added to netuid: {netuid}[/dark_sea_green3]" + f":white_heavy_check_mark: [dark_sea_green3]Finalized. Stake added to netuid: {netuid_}[/dark_sea_green3]" ) console.print( f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}" ) amount_staked = current_balance - new_balance - if allow_partial_stake and (amount_staked != amount): + if allow_partial_stake and (amount_staked != amount_): console.print( "Partial stake transaction. Staked:\n" f" [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{amount_staked}[/{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}] " f"instead of " - f"[blue]{amount}[/blue]" + f"[blue]{amount_}[/blue]" ) console.print( - f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{netuid}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] " + f"Subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{netuid_}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] " f"Stake:\n" f" [blue]{current_stake}[/blue] " f":arrow_right: " @@ -361,13 +357,11 @@ async def stake_extrinsic( else: stake_coroutines.append( safe_stake_extrinsic( - netuid=ni, - amount=am, + netuid_=ni, + amount_=am, current_stake=curr, - hotkey_ss58=staking_address, + hotkey_ss58_=staking_address, price_limit=price_with_tolerance, - wallet=wallet, - subtensor=subtensor, ) ) else: @@ -590,7 +584,9 @@ def _print_table_and_slippage(table: Table, max_slippage: float, safe_staking: b console.print(base_description + (safe_staking_description if safe_staking else "")) -def _calculate_slippage(subnet_info, amount: Balance) -> tuple[Balance, str, float]: +def _calculate_slippage( + subnet_info, amount: Balance +) -> tuple[Balance, str, float, str]: """Calculate slippage when adding stake. Args: diff --git a/bittensor_cli/src/commands/stake/children_hotkeys.py b/bittensor_cli/src/commands/stake/children_hotkeys.py index a19bf5af..fff84c65 100644 --- a/bittensor_cli/src/commands/stake/children_hotkeys.py +++ b/bittensor_cli/src/commands/stake/children_hotkeys.py @@ -313,9 +313,12 @@ async def get_take(child: tuple, netuid__: int) -> float: """ Get the take value for a given subtensor, hotkey, and netuid. - @param child: The hotkey to retrieve the take value for. + Arguments: + child: The hotkey to retrieve the take value for. + netuid__: the netuid to retrieve the take value for. - @return: The take value as a float. If the take value is not available, it returns 0. + Returns: + The take value as a float. If the take value is not available, it returns 0. """ child_hotkey = child[1] @@ -383,7 +386,7 @@ async def _render_table( f"The total stake of parent hotkey '{parent_hotkey}'{insert_text}is {parent_total}." ) - for index, (netuid_, children_) in enumerate(netuid_children_): + for index, (child_netuid, children_) in enumerate(netuid_children_): # calculate totals total_proportion_per_netuid = 0 total_stake_weight_per_netuid = 0 @@ -393,7 +396,7 @@ async def _render_table( children_info = [] child_takes = await asyncio.gather( - *[get_take(c, netuid_) for c in children_] + *[get_take(c, child_netuid) for c in children_] ) for child, child_take in zip(children_, child_takes): proportion = child[0] @@ -408,7 +411,7 @@ async def _render_table( ( converted_proportion, child_hotkey, - hotkey_stake_dict[child_hotkey][netuid_], + hotkey_stake_dict[child_hotkey][child_netuid], child_take, ) ) @@ -420,7 +423,7 @@ async def _render_table( for proportion_, hotkey, stake, child_take in children_info: proportion_percent = proportion_ * 100 # Proportion in percent proportion_tao = ( - hotkey_stake[netuid_].tao * proportion_ + hotkey_stake[child_netuid].tao * proportion_ ) # Proportion in TAO total_proportion_per_netuid += proportion_percent @@ -433,7 +436,7 @@ async def _render_table( hotkey = Text(hotkey, style="italic red" if proportion_ == 0 else "") table.add_row( - str(netuid_), + str(child_netuid), hotkey, proportion_str, take_str, @@ -661,28 +664,28 @@ def print_all_takes(takes: list[tuple[int, float]], ss58: str): table.add_column("Netuid", justify="center", style="cyan") table.add_column("Take (%)", justify="right", style="magenta") - for netuid, take_value in takes: - table.add_row(str(netuid), f"{take_value:.2f}%") + for take_netuid, take_value in takes: + table.add_row(str(take_netuid), f"{take_value:.2f}%") console.print(table) - async def display_chk_take(ss58, netuid): + async def display_chk_take(ss58, take_netuid): """Print single key take for hotkey and netuid""" chk_take = await get_childkey_take( - subtensor=subtensor, netuid=netuid, hotkey=ss58 + subtensor=subtensor, netuid=take_netuid, hotkey=ss58 ) if chk_take is None: chk_take = 0 chk_take = u16_to_float(chk_take) console.print( - f"Child take for {ss58} is: {chk_take * 100:.2f}% on netuid {netuid}." + f"Child take for {ss58} is: {chk_take * 100:.2f}% on netuid {take_netuid}." ) async def chk_all_subnets(ss58): """Aggregate data for childkey take from all subnets""" - netuids = await subtensor.get_all_subnet_netuids() + all_netuids = await subtensor.get_all_subnet_netuids() takes = [] - for subnet in netuids: + for subnet in all_netuids: if subnet == 0: continue curr_take = await get_childkey_take( diff --git a/bittensor_cli/src/commands/stake/list.py b/bittensor_cli/src/commands/stake/list.py index d5b5493b..eb5340fc 100644 --- a/bittensor_cli/src/commands/stake/list.py +++ b/bittensor_cli/src/commands/stake/list.py @@ -34,38 +34,37 @@ async def stake_list( ): coldkey_address = coldkey_ss58 if coldkey_ss58 else wallet.coldkeypub.ss58_address - async def get_stake_data(block_hash: str = None): + async def get_stake_data(block_hash_: str = None): ( - sub_stakes, - registered_delegate_info, + sub_stakes_, + registered_delegate_info_, _dynamic_info, ) = await asyncio.gather( subtensor.get_stake_for_coldkey( - coldkey_ss58=coldkey_address, block_hash=block_hash + coldkey_ss58=coldkey_address, block_hash=block_hash_ ), - subtensor.get_delegate_identities(block_hash=block_hash), - subtensor.all_subnets(block_hash=block_hash), + subtensor.get_delegate_identities(block_hash=block_hash_), + subtensor.all_subnets(block_hash=block_hash_), ) # sub_stakes = substakes[coldkey_address] - dynamic_info = {info.netuid: info for info in _dynamic_info} + dynamic_info__ = {info.netuid: info for info in _dynamic_info} return ( - sub_stakes, - registered_delegate_info, - dynamic_info, + sub_stakes_, + registered_delegate_info_, + dynamic_info__, ) def define_table( - hotkey_name: str, + hotkey_name_: str, rows: list[list[str]], - total_tao_value: Balance, - total_swapped_tao_value: Balance, - live: bool = False, + total_tao_value_: Balance, + total_swapped_tao_value_: Balance, ): - title = f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Hotkey: {hotkey_name}\nNetwork: {subtensor.network}\n\n" + title = f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Hotkey: {hotkey_name_}\nNetwork: {subtensor.network}\n\n" # TODO: Add hint back in after adding columns descriptions # if not live: # title += f"[{COLOR_PALETTE['GENERAL']['HINT']}]See below for an explanation of the columns\n" - table = Table( + defined_table = Table( title=title, show_footer=True, show_edge=False, @@ -76,74 +75,74 @@ def define_table( show_lines=False, pad_edge=True, ) - table.add_column( + defined_table.add_column( "[white]Netuid", footer=f"{len(rows)}", footer_style="overline white", style="grey89", ) - table.add_column( + defined_table.add_column( "[white]Name", style="cyan", justify="left", no_wrap=True, ) - table.add_column( + defined_table.add_column( f"[white]Value \n({Balance.get_unit(1)} x {Balance.unit}/{Balance.get_unit(1)})", footer_style="overline white", style=COLOR_PALETTE["STAKE"]["TAO"], justify="right", - footer=f"τ {millify_tao(total_tao_value.tao)}" + footer=f"τ {millify_tao(total_tao_value_.tao)}" if not verbose - else f"{total_tao_value}", + else f"{total_tao_value_}", ) - table.add_column( + defined_table.add_column( f"[white]Stake ({Balance.get_unit(1)})", footer_style="overline white", style=COLOR_PALETTE["STAKE"]["STAKE_ALPHA"], justify="center", ) - table.add_column( + defined_table.add_column( f"[white]Price \n({Balance.unit}_in/{Balance.get_unit(1)}_in)", footer_style="white", style=COLOR_PALETTE["POOLS"]["RATE"], justify="center", ) - table.add_column( + defined_table.add_column( f"[white]Swap ({Balance.get_unit(1)} -> {Balance.unit})", footer_style="overline white", style=COLOR_PALETTE["STAKE"]["STAKE_SWAP"], justify="right", - footer=f"τ {millify_tao(total_swapped_tao_value.tao)}" + footer=f"τ {millify_tao(total_swapped_tao_value_.tao)}" if not verbose - else f"{total_swapped_tao_value}", + else f"{total_swapped_tao_value_}", ) - table.add_column( + defined_table.add_column( "[white]Registered", style=COLOR_PALETTE["STAKE"]["STAKE_ALPHA"], justify="right", ) - table.add_column( + defined_table.add_column( f"[white]Emission \n({Balance.get_unit(1)}/block)", style=COLOR_PALETTE["POOLS"]["EMISSION"], justify="right", ) - table.add_column( + defined_table.add_column( f"[white]Emission \n({Balance.get_unit(0)}/block)", style=COLOR_PALETTE["POOLS"]["EMISSION"], justify="right", ) - return table + return defined_table def create_table(hotkey_: str, substakes: list[StakeInfo]): - name = ( + name_ = ( f"{registered_delegate_info[hotkey_].display} ({hotkey_})" if hotkey_ in registered_delegate_info else hotkey_ ) rows = [] - total_tao_value = Balance(0) - total_swapped_tao_value = Balance(0) + total_tao_value_ = Balance(0) + total_swapped_tao_value_ = Balance(0) root_stakes = [s for s in substakes if s.netuid == 0] other_stakes = sorted( [s for s in substakes if s.netuid != 0], @@ -162,14 +161,14 @@ def create_table(hotkey_: str, substakes: list[StakeInfo]): alpha_value = Balance.from_rao(int(substake_.stake.rao)).set_unit(netuid) # TAO value cell - tao_value = pool.alpha_to_tao(alpha_value) - total_tao_value += tao_value + tao_value_ = pool.alpha_to_tao(alpha_value) + total_tao_value_ += tao_value_ # Swapped TAO value and slippage cell - swapped_tao_value, _, slippage_percentage_ = ( + swapped_tao_value_, _, slippage_percentage_ = ( pool.alpha_to_tao_with_slippage(substake_.stake) ) - total_swapped_tao_value += swapped_tao_value + total_swapped_tao_value_ += swapped_tao_value_ # Slippage percentage cell if pool.is_dynamic: @@ -181,9 +180,9 @@ def create_table(hotkey_: str, substakes: list[StakeInfo]): swap_value = f"[{COLOR_PALETTE['STAKE']['NOT_REGISTERED']}]N/A[/{COLOR_PALETTE['STAKE']['NOT_REGISTERED']}] ({slippage_percentage})" else: swap_value = ( - f"τ {millify_tao(swapped_tao_value.tao)} ({slippage_percentage})" + f"τ {millify_tao(swapped_tao_value_.tao)} ({slippage_percentage})" if not verbose - else f"{swapped_tao_value} ({slippage_percentage})" + else f"{swapped_tao_value_} ({slippage_percentage})" ) # Per block emission cell @@ -202,9 +201,9 @@ def create_table(hotkey_: str, substakes: list[StakeInfo]): [ str(netuid), # Number subnet_name_cell, # Symbol + name - f"τ {millify_tao(tao_value.tao)}" + f"τ {millify_tao(tao_value_.tao)}" if not verbose - else f"{tao_value}", # Value (α x τ/α) + else f"{tao_value_}", # Value (α x τ/α) f"{stake_value} {symbol}" if netuid != 0 else f"{symbol} {stake_value}", # Stake (a) @@ -221,27 +220,33 @@ def create_table(hotkey_: str, substakes: list[StakeInfo]): str(Balance.from_tao(per_block_tao_emission)), ] ) - table = define_table(name, rows, total_tao_value, total_swapped_tao_value) + created_table = define_table( + name_, rows, total_tao_value_, total_swapped_tao_value_ + ) for row in rows: - table.add_row(*row) - console.print(table) - return total_tao_value, total_swapped_tao_value + created_table.add_row(*row) + console.print(created_table) + return total_tao_value_, total_swapped_tao_value_ def create_live_table( substakes: list, - registered_delegate_info: dict, - dynamic_info: dict, - hotkey_name: str, - previous_data: Optional[dict] = None, - ) -> tuple[Table, dict, Balance, Balance, Balance]: + dynamic_info_for_lt: dict, + hotkey_name_: str, + previous_data_: Optional[dict] = None, + ) -> tuple[Table, dict]: rows = [] - current_data = {} + current_data_ = {} - total_tao_value = Balance(0) - total_swapped_tao_value = Balance(0) + total_tao_value_ = Balance(0) + total_swapped_tao_value_ = Balance(0) def format_cell( - value, previous_value, unit="", unit_first=False, precision=4, millify=False + value, + previous_value, + unit="", + unit_first_=False, + precision=4, + millify=False, ): if previous_value is not None: change = value - previous_value @@ -265,7 +270,7 @@ def format_cell( ) return ( f"{formatted_value} {unit}{change_text}" - if not unit_first + if not unit_first_ else f"{unit} {formatted_value}{change_text}" ) @@ -273,7 +278,7 @@ def format_cell( root_stakes = [s for s in substakes if s.netuid == 0] other_stakes = sorted( [s for s in substakes if s.netuid != 0], - key=lambda x: dynamic_info[x.netuid] + key=lambda x: dynamic_info_for_lt[x.netuid] .alpha_to_tao(Balance.from_rao(int(x.stake.rao)).set_unit(x.netuid)) .tao, reverse=True, @@ -281,41 +286,41 @@ def format_cell( sorted_substakes = root_stakes + other_stakes # Process each stake - for substake in sorted_substakes: - netuid = substake.netuid - pool = dynamic_info.get(netuid) - if substake.stake.rao == 0 or not pool: + for substake_ in sorted_substakes: + netuid = substake_.netuid + pool = dynamic_info_for_lt.get(netuid) + if substake_.stake.rao == 0 or not pool: continue # Calculate base values symbol = f"{Balance.get_unit(netuid)}\u200e" - alpha_value = Balance.from_rao(int(substake.stake.rao)).set_unit(netuid) - tao_value = pool.alpha_to_tao(alpha_value) - total_tao_value += tao_value - swapped_tao_value, slippage, slippage_pct = pool.alpha_to_tao_with_slippage( - substake.stake + alpha_value = Balance.from_rao(int(substake_.stake.rao)).set_unit(netuid) + tao_value_ = pool.alpha_to_tao(alpha_value) + total_tao_value_ += tao_value_ + swapped_tao_value_, slippage, slippage_pct = ( + pool.alpha_to_tao_with_slippage(substake_.stake) ) - total_swapped_tao_value += swapped_tao_value + total_swapped_tao_value_ += swapped_tao_value_ # Store current values for future delta tracking - current_data[netuid] = { + current_data_[netuid] = { "stake": alpha_value.tao, "price": pool.price.tao, - "tao_value": tao_value.tao, - "swapped_value": swapped_tao_value.tao, - "emission": substake.emission.tao / (pool.tempo or 1), - "tao_emission": substake.tao_emission.tao / (pool.tempo or 1), + "tao_value": tao_value_.tao, + "swapped_value": swapped_tao_value_.tao, + "emission": substake_.emission.tao / (pool.tempo or 1), + "tao_emission": substake_.tao_emission.tao / (pool.tempo or 1), } # Get previous values for delta tracking - prev = previous_data.get(netuid, {}) if previous_data else {} + prev = previous_data_.get(netuid, {}) if previous_data_ else {} unit_first = True if netuid == 0 else False stake_cell = format_cell( alpha_value.tao, prev.get("stake"), unit=symbol, - unit_first=unit_first, + unit_first_=unit_first, precision=4, millify=True if not verbose else False, ) @@ -324,16 +329,16 @@ def format_cell( pool.price.tao, prev.get("price"), unit=f"τ/{symbol}", - unit_first=False, + unit_first_=False, precision=5, millify=True if not verbose else False, ) exchange_cell = format_cell( - tao_value.tao, + tao_value_.tao, prev.get("tao_value"), unit="τ", - unit_first=True, + unit_first_=True, precision=4, millify=True if not verbose else False, ) @@ -341,10 +346,10 @@ def format_cell( if netuid != 0: swap_cell = ( format_cell( - swapped_tao_value.tao, + swapped_tao_value_.tao, prev.get("swapped_value"), unit="τ", - unit_first=True, + unit_first_=True, precision=4, millify=True if not verbose else False, ) @@ -353,27 +358,27 @@ def format_cell( else: swap_cell = f"[{COLOR_PALETTE['STAKE']['NOT_REGISTERED']}]N/A[/{COLOR_PALETTE['STAKE']['NOT_REGISTERED']}] ({slippage_pct}%)" - emission_value = substake.emission.tao / (pool.tempo or 1) + emission_value = substake_.emission.tao / (pool.tempo or 1) emission_cell = format_cell( emission_value, prev.get("emission"), unit=symbol, - unit_first=unit_first, + unit_first_=unit_first, precision=4, ) - tao_emission_value = substake.tao_emission.tao / (pool.tempo or 1) + tao_emission_value = substake_.tao_emission.tao / (pool.tempo or 1) tao_emission_cell = format_cell( tao_emission_value, prev.get("tao_emission"), unit="τ", - unit_first=unit_first, + unit_first_=unit_first, precision=4, ) subnet_name_cell = ( f"[{COLOR_PALETTE['GENERAL']['SYMBOL']}]{symbol if netuid != 0 else 'τ'}[/{COLOR_PALETTE['GENERAL']['SYMBOL']}]" - f" {get_subnet_name(dynamic_info[netuid])}" + f" {get_subnet_name(dynamic_info_for_lt[netuid])}" ) rows.append( @@ -385,25 +390,21 @@ def format_cell( rate_cell, # Rate swap_cell, # Swap value with slippage "YES" - if substake.is_registered + if substake_.is_registered else f"[{COLOR_PALETTE['STAKE']['NOT_REGISTERED']}]NO", # Registration status emission_cell, # Emission rate tao_emission_cell, # TAO emission rate ] ) - table = define_table( - hotkey_name, - rows, - total_tao_value, - total_swapped_tao_value, - live=True, + live_table = define_table( + hotkey_name_, rows, total_tao_value_, total_swapped_tao_value_ ) for row in rows: - table.add_row(*row) + live_table.add_row(*row) - return table, current_data + return live_table, current_data_ # Main execution block_hash = await subtensor.substrate.get_chain_head() @@ -448,10 +449,8 @@ def format_cell( choices=[str(i) for i in range(len(hotkeys_to_substakes))], ) selected_hotkey = list(hotkeys_to_substakes.keys())[int(selected_idx)] - selected_stakes = hotkeys_to_substakes[selected_hotkey] else: selected_hotkey = list(hotkeys_to_substakes.keys())[0] - selected_stakes = hotkeys_to_substakes[selected_hotkey] hotkey_name = ( f"{registered_delegate_info[selected_hotkey].display} ({selected_hotkey})" @@ -499,7 +498,6 @@ def format_cell( table, current_data = create_live_table( selected_stakes, - registered_delegate_info, dynamic_info_, hotkey_name, previous_data, diff --git a/bittensor_cli/src/commands/stake/move.py b/bittensor_cli/src/commands/stake/move.py index 0aa23278..70afaa45 100644 --- a/bittensor_cli/src/commands/stake/move.py +++ b/bittensor_cli/src/commands/stake/move.py @@ -494,7 +494,6 @@ async def move_stake( ) # Determine the amount we are moving. - amount_to_move_as_balance = None if amount: amount_to_move_as_balance = Balance.from_tao(amount) elif stake_all: @@ -669,7 +668,6 @@ async def transfer_stake( ) return False - amount_to_transfer = None if amount: amount_to_transfer = Balance.from_tao(amount).set_unit(origin_netuid) elif stake_all: @@ -784,7 +782,6 @@ async def swap_stake( Args: wallet (Wallet): The wallet to swap stake from. subtensor (SubtensorInterface): Subtensor interface instance. - hotkey_ss58 (str): The SS58 address of the hotkey whose stake is being swapped. origin_netuid (int): The netuid from which stake is removed. destination_netuid (int): The netuid to which stake is added. amount (float): The amount to swap. diff --git a/bittensor_cli/src/commands/stake/remove.py b/bittensor_cli/src/commands/stake/remove.py index 1097faf7..07718e40 100644 --- a/bittensor_cli/src/commands/stake/remove.py +++ b/bittensor_cli/src/commands/stake/remove.py @@ -43,7 +43,6 @@ async def unstake( allow_partial_stake: bool, ): """Unstake from hotkey(s).""" - unstake_all_from_hk = False with console.status( f"Retrieving subnet data & identities from {subtensor.network}...", spinner="earth", @@ -145,7 +144,7 @@ async def unstake( staking_address_name, staking_address_ss58, netuid = hotkey netuids_to_process = [netuid] else: - staking_address_name, staking_address_ss58 = hotkey + staking_address_name, staking_address_ss58, _ = hotkey netuids_to_process = netuids initial_amount = amount @@ -179,7 +178,6 @@ async def unstake( if staking_address_name else staking_address_ss58, staking_address_ss58, - interactive, ) if amount_to_unstake_as_balance is None: skip_remaining_subnets = True @@ -189,8 +187,10 @@ async def unstake( amount_to_unstake_as_balance.set_unit(netuid) if amount_to_unstake_as_balance > current_stake_balance: err_console.print( - f"[red]Not enough stake to remove[/red]:\n Stake balance: [dark_orange]{current_stake_balance}[/dark_orange]" - f" < Unstaking amount: [dark_orange]{amount_to_unstake_as_balance}[/dark_orange] on netuid: {netuid}" + f"[red]Not enough stake to remove[/red]:\n" + f" Stake balance: [dark_orange]{current_stake_balance}[/dark_orange]" + f" < Unstaking amount: [dark_orange]{amount_to_unstake_as_balance}[/dark_orange]" + f" on netuid: {netuid}" ) continue # Skip to the next subnet - useful when single amount is specified for all subnets @@ -292,7 +292,6 @@ async def unstake( subtensor=subtensor, netuid=op["netuid"], amount=op["amount_to_unstake"], - current_stake=op["current_stake_balance"], hotkey_ss58=op["hotkey_ss58"], price_limit=op["price_with_tolerance"], allow_partial_stake=allow_partial_stake, @@ -320,12 +319,13 @@ async def unstake_all( hotkey_ss58_address: str, unstake_all_alpha: bool = False, all_hotkeys: bool = False, - include_hotkeys: list[str] = [], - exclude_hotkeys: list[str] = [], + include_hotkeys: Optional[list[str]] = None, + exclude_hotkeys: Optional[list[str]] = None, prompt: bool = True, ) -> bool: """Unstakes all stakes from all hotkeys in all subnets.""" - + include_hotkeys = include_hotkeys or [] + exclude_hotkeys = exclude_hotkeys or [] with console.status( f"Retrieving stake information & identities from {subtensor.network}...", spinner="earth", @@ -356,12 +356,12 @@ async def unstake_all( old_identities=old_identities, ) elif not hotkey_ss58_address: - hotkeys = [(wallet.hotkey_str, wallet.hotkey.ss58_address)] + hotkeys = [(wallet.hotkey_str, wallet.hotkey.ss58_address, None)] else: - hotkeys = [(None, hotkey_ss58_address)] + hotkeys = [(None, hotkey_ss58_address, None)] - hotkey_names = {ss58: name for name, ss58 in hotkeys if name is not None} - hotkey_ss58s = [ss58 for _, ss58 in hotkeys] + hotkey_names = {ss58: name for name, ss58, _ in hotkeys if name is not None} + hotkey_ss58s = [item[1] for item in hotkeys] stake_info = [ stake for stake in stake_info if stake.hotkey_ss58 in hotkey_ss58s ] @@ -567,7 +567,6 @@ async def _safe_unstake_extrinsic( subtensor: "SubtensorInterface", netuid: int, amount: Balance, - current_stake: Balance, hotkey_ss58: str, price_limit: Balance, allow_partial_stake: bool, @@ -578,7 +577,6 @@ async def _safe_unstake_extrinsic( Args: netuid: The subnet ID amount: Amount to unstake - current_stake: Current stake balance hotkey_ss58: Hotkey SS58 address price_limit: Maximum acceptable price wallet: Wallet instance @@ -724,6 +722,7 @@ async def _unstake_all_extrinsic( current_balance = await subtensor.get_balance( wallet.coldkeypub.ss58_address, block_hash=block_hash ) + previous_root_stake = None call_function = "unstake_all_alpha" if unstake_all_alpha else "unstake_all" call = await subtensor.substrate.compose_call( @@ -768,6 +767,7 @@ async def _unstake_all_extrinsic( new_balance = await subtensor.get_balance( wallet.coldkeypub.ss58_address, block_hash=block_hash ) + new_root_stake = None success_message = ( ":white_heavy_check_mark: [green]Finalized: Successfully unstaked all stakes[/green]" @@ -781,7 +781,9 @@ async def _unstake_all_extrinsic( if unstake_all_alpha: console.print( - f"Root Stake for {hotkey_name}:\n [blue]{previous_root_stake}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_root_stake}" + f"Root Stake for {hotkey_name}:\n " + f"[blue]{previous_root_stake}[/blue] :arrow_right: " + f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_root_stake}" ) except Exception as e: @@ -793,7 +795,7 @@ def _calculate_slippage(subnet_info, amount: Balance) -> tuple[Balance, str, flo """Calculate slippage and received amount for unstaking operation. Args: - dynamic_info: Subnet information containing price data + subnet_info: Subnet information containing price data amount: Amount being unstaked Returns: @@ -821,7 +823,7 @@ async def _unstake_selection( old_identities, stake_infos, netuid: Optional[int] = None, -): +) -> tuple[list[tuple[str, str, int]], bool]: if not stake_infos: print_error("You have no stakes to unstake.") raise ValueError @@ -899,7 +901,9 @@ async def _unstake_selection( # Display hotkey's staked netuids with amount. table = Table( - title=f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Stakes for hotkey \n[{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{selected_hotkey_name}\n{selected_hotkey_ss58}\n", + title=f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]Stakes for hotkey \n" + f"[{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{selected_hotkey_name}\n" + f"{selected_hotkey_ss58}\n", show_footer=True, show_edge=False, header_style="bold white", @@ -925,19 +929,20 @@ async def _unstake_selection( console.print("\n", table, "\n") # Ask which netuids to unstake from for the selected hotkey. - unstake_all = False + unstake_all_ = False if netuid is not None: selected_netuids = [netuid] else: while True: netuid_input = Prompt.ask( - "\nEnter the netuids of the [blue]subnets to unstake[/blue] from (comma-separated), or '[blue]all[/blue]' to unstake from all", + "\nEnter the netuids of the [blue]subnets to unstake[/blue] from (comma-separated), or " + "'[blue]all[/blue]' to unstake from all", default="all", ) if netuid_input.lower() == "all": selected_netuids = list(netuid_stakes.keys()) - unstake_all = True + unstake_all_ = True break else: try: @@ -960,7 +965,7 @@ async def _unstake_selection( hotkeys_to_unstake_from.append( (selected_hotkey_name, selected_hotkey_ss58, netuid_) ) - return hotkeys_to_unstake_from, unstake_all + return hotkeys_to_unstake_from, unstake_all_ def _ask_unstake_amount( @@ -968,7 +973,6 @@ def _ask_unstake_amount( netuid: int, staking_address_name: str, staking_address_ss58: str, - interactive: bool, ) -> Optional[Balance]: """Prompt the user to decide the amount to unstake. @@ -977,7 +981,6 @@ def _ask_unstake_amount( netuid: The subnet ID staking_address_name: Display name of the staking address staking_address_ss58: SS58 address of the staking address - interactive: Whether in interactive mode (affects default choice) Returns: Balance amount to unstake, or None if user chooses to quit @@ -1055,7 +1058,7 @@ def _get_hotkeys_to_unstake( stake_infos: list, identities: dict, old_identities: dict, -) -> list[tuple[Optional[str], str]]: +) -> list[tuple[Optional[str], str, None]]: """Get list of hotkeys to unstake from based on input parameters. Args: @@ -1066,26 +1069,28 @@ def _get_hotkeys_to_unstake( exclude_hotkeys: List of hotkey names to exclude Returns: - List of tuples containing (hotkey_name, hotkey_ss58) pairs to unstake from + List of tuples containing (hotkey_name, hotkey_ss58, None) pairs to unstake from. The final None is important + for compatibility with the `_unstake_selection` function. """ if hotkey_ss58_address: print_verbose(f"Unstaking from ss58 ({hotkey_ss58_address})") - return [(None, hotkey_ss58_address)] + return [(None, hotkey_ss58_address, None)] if all_hotkeys: print_verbose("Unstaking from all hotkeys") all_hotkeys_ = get_hotkey_wallets_for_wallet(wallet=wallet) wallet_hotkeys = [ - (wallet.hotkey_str, wallet.hotkey.ss58_address) + (wallet.hotkey_str, wallet.hotkey.ss58_address, None) for wallet in all_hotkeys_ if wallet.hotkey_str not in exclude_hotkeys ] - wallet_hotkey_addresses = {addr for _, addr in wallet_hotkeys} + wallet_hotkey_addresses = {hk[1] for hk in wallet_hotkeys} chain_hotkeys = [ ( get_hotkey_identity(stake_info.hotkey_ss58, identities, old_identities), stake_info.hotkey_ss58, + None, ) for stake_info in stake_infos if ( @@ -1100,14 +1105,14 @@ def _get_hotkeys_to_unstake( result = [] for hotkey_identifier in include_hotkeys: if is_valid_ss58_address(hotkey_identifier): - result.append((None, hotkey_identifier)) + result.append((None, hotkey_identifier, None)) else: wallet_ = Wallet( name=wallet.name, path=wallet.path, hotkey=hotkey_identifier, ) - result.append((wallet_.hotkey_str, wallet_.hotkey.ss58_address)) + result.append((wallet_.hotkey_str, wallet_.hotkey.ss58_address, None)) return result # Only cli.config.wallet.hotkey is specified @@ -1115,7 +1120,7 @@ def _get_hotkeys_to_unstake( f"Unstaking from wallet: ({wallet.name}) from hotkey: ({wallet.hotkey_str})" ) assert wallet.hotkey is not None - return [(wallet.hotkey_str, wallet.hotkey.ss58_address)] + return [(wallet.hotkey_str, wallet.hotkey.ss58_address, None)] def _create_unstake_table( diff --git a/bittensor_cli/src/commands/subnets/subnets.py b/bittensor_cli/src/commands/subnets/subnets.py index 1db23a4d..81043d8c 100644 --- a/bittensor_cli/src/commands/subnets/subnets.py +++ b/bittensor_cli/src/commands/subnets/subnets.py @@ -111,6 +111,12 @@ async def _find_event_attributes_in_extrinsic_receipt( ): return False + call_params = { + "hotkey": wallet.hotkey.ss58_address, + "mechid": 1, + } + call_function = "register_network" + has_identity = any(subnet_identity.values()) if has_identity: identity_data = { @@ -136,6 +142,8 @@ async def _find_event_attributes_in_extrinsic_receipt( if subnet_identity.get("additional") else b"", } + call_params["identity"] = identity_data + call_function = "register_network_with_identity" for field, value in identity_data.items(): max_size = 64 # bytes if len(value) > max_size: @@ -149,15 +157,6 @@ async def _find_event_attributes_in_extrinsic_receipt( return False with console.status(":satellite: Registering subnet...", spinner="earth"): - call_params = { - "hotkey": wallet.hotkey.ss58_address, - "mechid": 1, - } - call_function = "register_network" - if has_identity: - call_params["identity"] = identity_data - call_function = "register_network_with_identity" - substrate = subtensor.substrate # create extrinsic call call = await substrate.compose_call( @@ -1341,12 +1340,12 @@ async def burn_cost(subtensor: "SubtensorInterface") -> Optional[Balance]: f":satellite:Retrieving lock cost from {subtensor.network}...", spinner="aesthetic", ): - burn_cost = await subtensor.burn_cost() - if burn_cost: + current_burn_cost = await subtensor.burn_cost() + if current_burn_cost: console.print( - f"Subnet burn cost: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{burn_cost}" + f"Subnet burn cost: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{current_burn_cost}" ) - return burn_cost + return current_burn_cost else: err_console.print( "Subnet burn cost: [red]Failed to get subnet burn cost[/red]" @@ -1417,7 +1416,7 @@ async def pow_register( use_cuda, dev_id, threads_per_block, - prompt: bool + prompt: bool, ): """Register neuron.""" diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index ef9895d2..8184bd79 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -155,7 +155,6 @@ async def set_hyperparameter_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - :param prompt: If `True`, the call waits for confirmation from the user before proceeding. :return: success: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the response is `True`. @@ -178,6 +177,7 @@ async def set_hyperparameter_extrinsic( arbitrary_extrinsic = False extrinsic, sudo_ = HYPERPARAMS.get(parameter, ("", False)) + call_params = {"netuid": netuid} if not extrinsic: arbitrary_extrinsic, call_params = search_metadata( parameter, value, netuid, subtensor.substrate.metadata @@ -207,7 +207,6 @@ async def set_hyperparameter_extrinsic( extrinsic_params = await substrate.get_metadata_call_function( "AdminUtils", extrinsic ) - call_params = {"netuid": netuid} # if input value is a list, iterate through the list and assign values if isinstance(value, list): diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index 8710de3e..82b1253a 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -124,17 +124,18 @@ async def regen_hotkey( json_str = f.read() try: - new_hotkey = wallet.regenerate_hotkey( + new_hotkey_ = wallet.regenerate_hotkey( mnemonic=mnemonic, seed=seed, json=(json_str, json_password) if all([json_str, json_password]) else None, use_password=use_password, overwrite=overwrite, ) - if isinstance(new_hotkey, Wallet): + if isinstance(new_hotkey_, Wallet): console.print( "\n✅ [dark_sea_green]Regenerated hotkey successfully!\n", - f"[dark_sea_green]Wallet name: ({new_hotkey.name}), path: ({new_hotkey.path}), hotkey ss58: ({new_hotkey.hotkey.ss58_address})", + f"[dark_sea_green]Wallet name: " + f"({new_hotkey_.name}), path: ({new_hotkey_.path}), hotkey ss58: ({new_hotkey_.hotkey.ss58_address})", ) except ValueError: print_error("Mnemonic phrase is invalid") diff --git a/tests/e2e_tests/test_unstaking.py b/tests/e2e_tests/test_unstaking.py index b885d8ad..66dda33c 100644 --- a/tests/e2e_tests/test_unstaking.py +++ b/tests/e2e_tests/test_unstaking.py @@ -228,8 +228,10 @@ def test_unstaking(local_chain, wallet_setup): "ws://127.0.0.1:9945", "--all-alpha", "--no-prompt", + "--verbose", ], ) + assert ( "✅ Finalized: Successfully unstaked all Alpha stakes" in unstake_alpha.stdout )