From 8c22bb1d6b41a3a2564b744337edce6af27ff609 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Wed, 28 Jan 2026 16:24:24 -0800 Subject: [PATCH 01/15] update get_subnet_prices --- .../src/bittensor/subtensor_interface.py | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index 8b393c88..4885f136 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -2546,20 +2546,28 @@ async def get_subnet_prices( :return: A dictionary mapping netuid to the current Alpha price in TAO units. """ - query = await self.substrate.query_map( - module="Swap", - storage_function="AlphaSqrtPrice", - page_size=page_size, - block_hash=block_hash, - ) + all_netuids = await self.get_all_subnet_netuids(block_hash=block_hash) - map_ = {} - async for netuid_, current_sqrt_price in query: - current_sqrt_price_ = fixed_to_float(current_sqrt_price.value) - current_price = current_sqrt_price_**2 - map_[netuid_] = Balance.from_rao(int(current_price * 1e9)) + price_tasks = [ + self.query_runtime_api( + "SwapRuntimeApi", + "current_alpha_price", + params={"netuid": netuid}, + block_hash=block_hash, + ) + for netuid in all_netuids + if netuid != 0 + ] - return map_ + prices = await asyncio.gather(*price_tasks, return_exceptions=True) + + result = {0: Balance.from_tao(1.0)} + netuids_to_query = [netuid for netuid in all_netuids if netuid != 0] + for netuid, current_price in zip(netuids_to_query, prices): + if not isinstance(current_price, Exception): + result[netuid] = Balance.from_rao(current_price) + + return result async def get_all_subnet_ema_tao_inflow( self, From 9a873ea39d7c6a442f3a52af7c72ae994f55c06d Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Wed, 28 Jan 2026 16:24:57 -0800 Subject: [PATCH 02/15] get_subnet_price updated --- .../src/bittensor/subtensor_interface.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index 4885f136..c3d15d64 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -2510,6 +2510,7 @@ async def get_claimable_stakes_for_coldkey( results[hotkey][netuid] = net_claimable.set_unit(netuid) return results + async def get_subnet_price( self, netuid: int = None, @@ -2523,17 +2524,16 @@ async def get_subnet_price( :return: The current Alpha price in TAO units for the specified subnet. """ - # TODO update this to use the runtime call SwapRuntimeAPI.current_alpha_price - current_sqrt_price = await self.query( - module="Swap", - storage_function="AlphaSqrtPrice", - params=[netuid], + if netuid == 0: + return Balance.from_tao(1.0) + + current_price = await self.query_runtime_api( + "SwapRuntimeApi", + "current_alpha_price", + params={"netuid": netuid}, block_hash=block_hash, ) - - current_sqrt_price = fixed_to_float(current_sqrt_price) - current_price = current_sqrt_price * current_sqrt_price - return Balance.from_rao(int(current_price * 1e9)) + return Balance.from_rao(current_price) async def get_subnet_prices( self, block_hash: Optional[str] = None, page_size: int = 100 From 2e54aa912862835150f7f5bad9a4a760c73bf27c Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 14:02:59 -0800 Subject: [PATCH 03/15] remove deprecated methods --- bittensor_cli/src/bittensor/chain_data.py | 81 ----------------------- 1 file changed, 81 deletions(-) diff --git a/bittensor_cli/src/bittensor/chain_data.py b/bittensor_cli/src/bittensor/chain_data.py index 718dbd69..eac92304 100644 --- a/bittensor_cli/src/bittensor/chain_data.py +++ b/bittensor_cli/src/bittensor/chain_data.py @@ -800,87 +800,6 @@ 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, float]: - """ - Returns an estimate of how much Alpha a staker would receive if they stake their tao using the current pool - state. - - Args: - tao: Amount of TAO to stake. - Returns: - Tuple of balances where the first part is the amount of Alpha received, and the - second part (slippage) is the difference between the estimated amount and ideal - amount as if there was no slippage - """ - if self.is_dynamic: - new_tao_in = self.tao_in + tao - if new_tao_in == 0: - return tao, Balance.from_rao(0) - new_alpha_in = self.k / new_tao_in - - # Amount of alpha given to the staker - alpha_returned = Balance.from_rao( - self.alpha_in.rao - new_alpha_in.rao - ).set_unit(self.netuid) - - # Ideal conversion as if there is no slippage, just price - alpha_ideal = self.tao_to_alpha(tao) - - if alpha_ideal.tao > alpha_returned.tao: - slippage = Balance.from_tao( - alpha_ideal.tao - alpha_returned.tao - ).set_unit(self.netuid) - else: - slippage = Balance.from_tao(0) - else: - alpha_returned = tao.set_unit(self.netuid) - slippage = Balance.from_tao(0) - - slippage_pct_float = ( - 100 * float(slippage) / float(slippage + alpha_returned) - if slippage + alpha_returned != 0 - else 0 - ) - return alpha_returned, slippage, slippage_pct_float - - def alpha_to_tao_with_slippage( - self, alpha: Balance - ) -> tuple[Balance, Balance, float]: - """ - Returns an estimate of how much TAO a staker would receive if they unstake their alpha using the current pool - state. - - Args: - alpha: Amount of Alpha to stake. - Returns: - Tuple of balances where the first part is the amount of TAO received, and the - second part (slippage) is the difference between the estimated amount and ideal - amount as if there was no slippage - """ - if self.is_dynamic: - new_alpha_in = self.alpha_in + alpha - new_tao_reserve = self.k / new_alpha_in - # Amount of TAO given to the unstaker - tao_returned = Balance.from_rao(self.tao_in - new_tao_reserve) - - # Ideal conversion as if there is no slippage, just price - tao_ideal = self.alpha_to_tao(alpha) - - if tao_ideal > tao_returned: - slippage = Balance.from_tao(tao_ideal.tao - tao_returned.tao) - else: - slippage = Balance.from_tao(0) - else: - tao_returned = alpha.set_unit(0) - slippage = Balance.from_tao(0) - slippage_pct_float = ( - 100 * float(slippage) / float(slippage + tao_returned) - if slippage + tao_returned != 0 - else 0 - ) - return tao_returned, slippage, slippage_pct_float @dataclass From 67b1e33e7059044c5785ed75671db042cdbeb728 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 15:14:08 -0800 Subject: [PATCH 04/15] add tao_to_alpha_slippage --- bittensor_cli/src/bittensor/chain_data.py | 40 +++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/bittensor_cli/src/bittensor/chain_data.py b/bittensor_cli/src/bittensor/chain_data.py index eac92304..eca6c1f9 100644 --- a/bittensor_cli/src/bittensor/chain_data.py +++ b/bittensor_cli/src/bittensor/chain_data.py @@ -1,7 +1,7 @@ from abc import abstractmethod from dataclasses import dataclass from enum import Enum -from typing import Optional, Any, Union +from typing import Optional, Any, Union, Literal import netaddr from scalecodec.utils.ss58 import ss58_encode @@ -801,7 +801,6 @@ def alpha_to_tao(self, alpha: Balance) -> Balance: return Balance.from_tao(alpha.tao * self.price.tao) - @dataclass class ColdkeySwapAnnouncementInfo(InfoBase): """ @@ -1147,6 +1146,43 @@ def from_dict(cls, d: dict, netuid: int) -> "SimSwapResult": alpha_fee=Balance.from_rao(d["alpha_fee"]).set_unit(netuid), ) + def tao_to_alpha_slippage( + self, + tao_amount: Balance, + current_price: float, + netuid: int, + ) -> tuple[Balance, Balance, float]: + """ + Calculate slippage for a TAO -> Alpha swap. + + Args: + tao_amount: Amount of TAO provided as input. + current_price: Current alpha price in TAO (TAO per 1 alpha). + netuid: Target subnet netuid (used for unit tagging). + + Returns: + A tuple of: + received_alpha (Balance): Simulated alpha received. + slippage_alpha (Balance): Shortfall vs ideal at current_price. + slippage_pct_float (float): Slippage percentage (0 - 100). + """ + if current_price <= 0: + zero = Balance.from_tao(0).set_unit(netuid) + return zero, zero, 0.0 + + ideal_amount = Balance.from_tao(tao_amount.tao / current_price).set_unit(netuid) + received_amount = self.alpha_amount + + if ideal_amount.tao == 0: + zero = Balance.from_tao(0).set_unit(netuid) + return received_amount, zero, 0.0 + + slippage_amount = max(ideal_amount.tao - received_amount.tao, 0) + slippage_amount_balance = Balance.from_tao(slippage_amount).set_unit(netuid) + slippage_pct = 100 * slippage_amount / ideal_amount.tao + + return received_amount, slippage_amount_balance, slippage_pct + @dataclass class CrowdloanData(InfoBase): From 5bf8bc376e6d6beca821c7291453a3194def61a3 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 15:14:21 -0800 Subject: [PATCH 05/15] add alpha_to_tao_slippage --- bittensor_cli/src/bittensor/chain_data.py | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/bittensor_cli/src/bittensor/chain_data.py b/bittensor_cli/src/bittensor/chain_data.py index eca6c1f9..4b144a44 100644 --- a/bittensor_cli/src/bittensor/chain_data.py +++ b/bittensor_cli/src/bittensor/chain_data.py @@ -1183,6 +1183,41 @@ def tao_to_alpha_slippage( return received_amount, slippage_amount_balance, slippage_pct + def alpha_to_tao_slippage( + self, + alpha_amount: Balance, + current_price: float, + ) -> tuple[Balance, Balance, float]: + """ + Calculate slippage for an Alpha -> TAO swap. + + Args: + alpha_amount: Amount of Alpha provided as input. + current_price: Current alpha price in TAO (TAO per 1 alpha). + + Returns: + A tuple of: + received_tao (Balance): Simulated TAO received. + slippage_tao (Balance): Shortfall vs ideal at current_price. + slippage_pct_float (float): Slippage percentage (0 - 100). + """ + if current_price <= 0: + zero = Balance.from_tao(0).set_unit(0) + return zero, zero, 0.0 + + ideal_amount = Balance.from_tao(alpha_amount.tao * current_price).set_unit(0) + received_amount = self.tao_amount + + if ideal_amount.tao == 0: + zero = Balance.from_tao(0).set_unit(0) + return received_amount, zero, 0.0 + + slippage_amount = max(ideal_amount.tao - received_amount.tao, 0) + slippage_amount_balance = Balance.from_tao(slippage_amount).set_unit(0) + slippage_pct = 100 * slippage_amount / ideal_amount.tao + + return received_amount, slippage_amount_balance, slippage_pct + @dataclass class CrowdloanData(InfoBase): From f36dcb82ed076c7be289c9067217fc7359267da0 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 15:50:56 -0800 Subject: [PATCH 06/15] improve get_subnet_prices --- .../src/bittensor/subtensor_interface.py | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index c3d15d64..d97bf2e9 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -1654,7 +1654,7 @@ async def all_subnets(self, block_hash: Optional[str] = None) -> list[DynamicInf "get_all_dynamic_info", block_hash=block_hash, ), - self.get_subnet_prices(block_hash=block_hash, page_size=129), + self.get_subnet_prices(block_hash=block_hash), ) sns: list[DynamicInfo] = DynamicInfo.list_from_any(result) for sn in sns: @@ -2510,7 +2510,6 @@ async def get_claimable_stakes_for_coldkey( results[hotkey][netuid] = net_claimable.set_unit(netuid) return results - async def get_subnet_price( self, netuid: int = None, @@ -2536,36 +2535,32 @@ async def get_subnet_price( return Balance.from_rao(current_price) async def get_subnet_prices( - self, block_hash: Optional[str] = None, page_size: int = 100 + self, block_hash: Optional[str] = None ) -> dict[int, Balance]: """ Gets the current Alpha prices in TAO for all subnets. :param block_hash: The hash of the block to retrieve prices from. - :param page_size: The page size for batch queries (default: 100). :return: A dictionary mapping netuid to the current Alpha price in TAO units. """ all_netuids = await self.get_all_subnet_netuids(block_hash=block_hash) - price_tasks = [ - self.query_runtime_api( - "SwapRuntimeApi", - "current_alpha_price", - params={"netuid": netuid}, - block_hash=block_hash, - ) - for netuid in all_netuids - if netuid != 0 - ] - - prices = await asyncio.gather(*price_tasks, return_exceptions=True) - result = {0: Balance.from_tao(1.0)} netuids_to_query = [netuid for netuid in all_netuids if netuid != 0] + prices = await asyncio.gather( + *[ + self.query_runtime_api( + "SwapRuntimeApi", + "current_alpha_price", + params={"netuid": netuid}, + block_hash=block_hash, + ) + for netuid in netuids_to_query + ], + ) for netuid, current_price in zip(netuids_to_query, prices): - if not isinstance(current_price, Exception): - result[netuid] = Balance.from_rao(current_price) + result[netuid] = Balance.from_rao(current_price) return result From 71fd52a9d99899615e7188351244dd19721ba45e Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 16:12:21 -0800 Subject: [PATCH 07/15] update get_total_stake_for_coldkey --- .../src/bittensor/subtensor_interface.py | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index d97bf2e9..6719daf7 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -444,40 +444,52 @@ async def get_total_stake_for_coldkey( :return: {address: Balance objects} """ - sub_stakes, dynamic_info = await asyncio.gather( + sub_stakes, price_map = await asyncio.gather( self.get_stake_for_coldkeys(list(ss58_addresses), block_hash=block_hash), - # Token pricing info - self.all_subnets(block_hash=block_hash), + self.get_subnet_prices(block_hash=block_hash), ) - results = {} + results: dict[str, tuple[Balance, Balance]] = {} + dynamic_stakes: list[tuple[str, "StakeInfo"]] = [] + for ss58, stake_info_list in sub_stakes.items(): - total_tao_value = Balance(0) - total_swapped_tao_value = Balance(0) + total_tao_value, total_swapped_tao_value = Balance(0), Balance(0) for sub_stake in stake_info_list: if sub_stake.stake.rao == 0: continue - netuid = sub_stake.netuid - pool = dynamic_info[netuid] - alpha_value = Balance.from_rao(int(sub_stake.stake.rao)).set_unit( - netuid + netuid = sub_stake.netuid + price = price_map[netuid] + ideal_tao = Balance.from_tao(sub_stake.stake.tao * price.tao).set_unit( + 0 ) + total_tao_value += ideal_tao - # Without slippage - tao_value = pool.alpha_to_tao(alpha_value) - total_tao_value += tao_value - - # With slippage if netuid == 0: - swapped_tao_value = tao_value + total_swapped_tao_value += ideal_tao else: - swapped_tao_value, _, _ = pool.alpha_to_tao_with_slippage( - sub_stake.stake - ) - total_swapped_tao_value += swapped_tao_value + dynamic_stakes.append((ss58, sub_stake)) results[ss58] = (total_tao_value, total_swapped_tao_value) + + if dynamic_stakes: + sim_results = await asyncio.gather( + *[ + self.sim_swap( + origin_netuid=sub_stake.netuid, + destination_netuid=0, + amount=sub_stake.stake.rao, + block_hash=block_hash, + ) + for _, sub_stake in dynamic_stakes + ] + ) + + for (ss58, sub_stake), sim_result in zip(dynamic_stakes, sim_results): + total_tao_value, total_swapped_tao_value = results[ss58] + total_swapped_tao_value += sim_result.tao_amount + results[ss58] = (total_tao_value, total_swapped_tao_value) + return results async def get_total_stake_for_hotkey( From 971b31e1c1ca7b3cccebea33acda62f242e45ec6 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 17:24:57 -0800 Subject: [PATCH 08/15] update remove stake slippage --- bittensor_cli/src/commands/stake/remove.py | 36 ++++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/bittensor_cli/src/commands/stake/remove.py b/bittensor_cli/src/commands/stake/remove.py index d1490e9f..7d9724d5 100644 --- a/bittensor_cli/src/commands/stake/remove.py +++ b/bittensor_cli/src/commands/stake/remove.py @@ -250,6 +250,13 @@ async def unstake( received_amount = sim_swap.tao_amount if not proxy: received_amount -= extrinsic_fee + + _, _, slippage_pct_float = sim_swap.alpha_to_tao_slippage( + alpha_amount=amount_to_unstake_as_balance, + current_price=current_price, + ) + slippage_pct = f"{slippage_pct_float:.4f} %" + max_float_slippage = max(slippage_pct_float, max_float_slippage) except ValueError: continue total_received_amount += received_amount @@ -275,7 +282,7 @@ async def unstake( str(sim_swap.alpha_fee), # Fee str(extrinsic_fee), # Extrinsic fee str(received_amount), # Received Amount - # slippage_pct, # Slippage Percent + slippage_pct, # Slippage Percent ] # Additional fields for safe unstaking @@ -444,6 +451,7 @@ async def unstake_all( return all_sn_dynamic_info = {info.netuid: info for info in all_sn_dynamic_info_} + max_float_slippage = 0.0 # Create table for unstaking all table_title = ( @@ -488,11 +496,11 @@ async def unstake_all( justify="center", style=COLOR_PALETTE["POOLS"]["TAO_EQUIV"], ) - # table.add_column( - # "Slippage", - # justify="center", - # style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"], - # ) + table.add_column( + "Slippage", + justify="center", + style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"], + ) # Calculate total received total_received_value = Balance(0) @@ -524,6 +532,13 @@ async def unstake_all( if received_amount < Balance.from_tao(0): print_error("Not enough Alpha to pay the transaction fee.") continue + + _, _, slippage_pct_float = sim_swap.alpha_to_tao_slippage( + alpha_amount=stake_amount, + current_price=current_price, + ) + slippage_pct = f"{slippage_pct_float:.4f} %" + max_float_slippage = max(slippage_pct_float, max_float_slippage) except (AttributeError, ValueError): continue @@ -538,8 +553,9 @@ async def unstake_all( str(sim_swap.alpha_fee), str(extrinsic_fee), str(received_amount), + slippage_pct, ) - console.print(table) + _print_table_and_slippage(table, max_float_slippage, False) console.print( f"Total expected return: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{total_received_value}" @@ -1350,9 +1366,9 @@ def _create_unstake_table( style=COLOR_PALETTE["POOLS"]["TAO_EQUIV"], footer=str(total_received_amount), ) - # table.add_column( - # "Slippage", justify="center", style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"] - # ) + table.add_column( + "Slippage", justify="center", style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"] + ) if safe_staking: table.add_column( f"Rate with tolerance: [blue]({rate_tolerance * 100}%)[/blue]", From 6813d5b0a35f7e14b7e7241845850a594c401d49 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 18:01:48 -0800 Subject: [PATCH 09/15] update slippage calc in add stake --- bittensor_cli/src/commands/stake/add.py | 79 +++++-------------------- 1 file changed, 16 insertions(+), 63 deletions(-) diff --git a/bittensor_cli/src/commands/stake/add.py b/bittensor_cli/src/commands/stake/add.py index b9bff970..340f336e 100644 --- a/bittensor_cli/src/commands/stake/add.py +++ b/bittensor_cli/src/commands/stake/add.py @@ -382,18 +382,7 @@ async def stake_extrinsic( return remaining_wallet_balance -= amount_to_stake - # Calculate slippage - # TODO: Update for V3, slippage calculation is significantly different in v3 - # try: - # received_amount, slippage_pct, slippage_pct_float, rate = ( - # _calculate_slippage(subnet_info, amount_to_stake, stake_fee) - # ) - # except ValueError: - # return False - # - # max_slippage = max(slippage_pct_float, max_slippage) - - # Temporary workaround - calculations without slippage + # Calculate rate current_price_float = float(subnet_info.price.tao) rate = 1.0 / current_price_float @@ -443,6 +432,15 @@ async def stake_extrinsic( amount=amount_minus_fee.rao, ) received_amount = sim_swap.alpha_amount + + _, _, slippage_pct_float = sim_swap.tao_to_alpha_slippage( + tao_amount=amount_minus_fee, + current_price=current_price_float, + netuid=netuid, + ) + slippage_pct = f"{slippage_pct_float:.4f} %" + max_slippage = max(slippage_pct_float, max_slippage) + # Add rows for the table base_row = [ str(netuid), # netuid @@ -453,7 +451,7 @@ async def stake_extrinsic( str(received_amount.set_unit(netuid)), # received str(sim_swap.tao_fee), # fee str(extrinsic_fee), - # str(slippage_pct), # slippage + str(slippage_pct), # slippage ] + row_extension rows.append(tuple(base_row)) @@ -688,10 +686,9 @@ def _define_stake_table( justify="center", style=COLOR_PALETTE.STAKE.TAO, ) - # TODO: Uncomment when slippage is reimplemented for v3 - # table.add_column( - # "Slippage", justify="center", style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"] - # ) + table.add_column( + "Slippage", justify="center", style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"] + ) if safe_staking: table.add_column( @@ -736,8 +733,8 @@ def _print_table_and_slippage(table: Table, max_slippage: float, safe_staking: b - [bold white]Hotkey[/bold white]: The ss58 address of the hotkey you are staking to. - [bold white]Amount[/bold white]: The TAO you are staking into this subnet onto this hotkey. - [bold white]Rate[/bold white]: The rate of exchange between your TAO and the subnet's stake. - - [bold white]Received[/bold white]: The amount of stake you will receive on this subnet after slippage.""" - # - [bold white]Slippage[/bold white]: The slippage percentage of the stake operation. (0% if the subnet is not dynamic i.e. root).""" + - [bold white]Received[/bold white]: The amount of stake you will receive on this subnet after slippage. + - [bold white]Slippage[/bold white]: The slippage percentage of the stake operation. (0% if the subnet is not dynamic i.e. root).""" safe_staking_description = """ - [bold white]Rate Tolerance[/bold white]: Maximum acceptable alpha rate. If the rate exceeds this tolerance, the transaction will be limited or rejected. @@ -745,47 +742,3 @@ 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, stake_fee: Balance -) -> tuple[Balance, str, float, str]: - """Calculate slippage when adding stake. - - Args: - subnet_info: Subnet dynamic info - amount: Amount being staked - stake_fee: Transaction fee for the stake operation - - Returns: - tuple containing: - - received_amount: Amount received after slippage and fees - - slippage_str: Formatted slippage percentage string - - slippage_float: Raw slippage percentage value - - rate: Exchange rate string - - TODO: Update to v3. This method only works for protocol-liquidity-only - mode (user liquidity disabled) - """ - amount_after_fee = amount - stake_fee - - if amount_after_fee < 0: - print_error("You don't have enough balance to cover the stake fee.") - raise ValueError() - - received_amount, _, _ = subnet_info.tao_to_alpha_with_slippage(amount_after_fee) - - if subnet_info.is_dynamic: - ideal_amount = subnet_info.tao_to_alpha(amount) - total_slippage = ideal_amount - received_amount - slippage_pct_float = 100 * (total_slippage.tao / ideal_amount.tao) - slippage_str = f"{slippage_pct_float:.4f} %" - rate = f"{(1 / subnet_info.price.tao or 1):.4f}" - else: - # TODO: Fix this. Slippage is always zero for static networks. - slippage_pct_float = ( - 100 * float(stake_fee.tao) / float(amount.tao) if amount.tao != 0 else 0 - ) - slippage_str = f"{slippage_pct_float:.4f} %" - rate = "1" - - return received_amount, slippage_str, slippage_pct_float, rate From 7b86d41d7acdadd83264790e0e22a8405fc8e7a2 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 19:08:00 -0800 Subject: [PATCH 10/15] add disabled cmds --- bittensor_cli/src/commands/view.py | 74 +++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/bittensor_cli/src/commands/view.py b/bittensor_cli/src/commands/view.py index 26bb3eb9..0f959fc9 100644 --- a/bittensor_cli/src/commands/view.py +++ b/bittensor_cli/src/commands/view.py @@ -31,7 +31,10 @@ async def display_network_dashboard( try: with console.status("[dark_sea_green3]Fetching data...", spinner="earth"): _subnet_data = await fetch_subnet_data(wallet, subtensor) - subnet_data = process_subnet_data(_subnet_data) + # Add block_hash to raw_data for RPC calls + if "block_hash" not in _subnet_data: + _subnet_data["block_hash"] = await subtensor.substrate.get_chain_head() + subnet_data = await process_subnet_data(_subnet_data, subtensor) html_content = generate_full_page(subnet_data) if use_wry: @@ -154,10 +157,13 @@ async def fetch_subnet_data( "old_identities": old_identities, "wallet": wallet, "block_number": block_number, + "block_hash": block_hash, } -def process_subnet_data(raw_data: dict[str, Any]) -> dict[str, Any]: +async def process_subnet_data( + raw_data: dict[str, Any], subtensor: "SubtensorInterface" +) -> dict[str, Any]: """ Process and prepare subnet data. """ @@ -169,20 +175,74 @@ def process_subnet_data(raw_data: dict[str, Any]) -> dict[str, Any]: old_identities = raw_data["old_identities"] wallet = raw_data["wallet"] block_number = raw_data["block_number"] + block_hash = raw_data.get("block_hash") pool_info = {info.netuid: info for info in subnets_info} total_ideal_stake_value = Balance.from_tao(0) total_slippage_value = Balance.from_tao(0) + # Batch calculate slippage using RPC for accurate Balancer calculations + slippage_tasks = [] + stake_list = [] + for stake in stake_info: + if stake.stake.tao > 0: + if stake.netuid == 0: + # Root subnet - no slippage, skip RPC call + stake_list.append((stake, None)) + else: + slippage_tasks.append( + subtensor.sim_swap( + origin_netuid=stake.netuid, + destination_netuid=0, + amount=stake.stake.rao, + block_hash=block_hash, + ) + ) + stake_list.append((stake, len(slippage_tasks) - 1)) + + slippage_results = ( + await asyncio.gather(*slippage_tasks, return_exceptions=True) + if slippage_tasks + else [] + ) + # Process stake stake_dict: dict[int, list[dict[str, Any]]] = {} - for stake in stake_info: + for stake, slippage_idx in stake_list: if stake.stake.tao > 0: - slippage_value, _, slippage_percentage = pool_info[ - stake.netuid - ].alpha_to_tao_with_slippage(stake.stake) - ideal_value = pool_info[stake.netuid].alpha_to_tao(stake.stake) + if stake.netuid == 0: + # Root subnet - no slippage + slippage_value = stake.stake.set_unit(0) + slippage_percentage = 0.0 + current_price = 1.0 + else: + if slippage_idx is not None and slippage_idx < len(slippage_results): + slippage_result = slippage_results[slippage_idx] + if isinstance(slippage_result, Exception): + # On RPC error, skip slippage calculation for this stake + console.print( + f"[yellow]Warning:[/yellow] Could not calculate slippage " + f"for stake on netuid {stake.netuid}. Skipping slippage display." + ) + slippage_value = Balance.from_rao(0).set_unit(0) + slippage_percentage = 0.0 + else: + current_price = pool_info[stake.netuid].price.tao + slippage_value, _, slippage_percentage = ( + slippage_result.alpha_to_tao_slippage( + alpha_amount=stake.stake, current_price=current_price + ) + ) + else: + # This should not happen, but handle gracefully + slippage_value = Balance.from_rao(0).set_unit(0) + slippage_percentage = 0.0 + # Ideal TAO value at current price (runtime price) + current_price = ( + pool_info[stake.netuid].price.tao if stake.netuid in pool_info else 0.0 + ) + ideal_value = Balance.from_tao(stake.stake.tao * current_price).set_unit(0) total_ideal_stake_value += ideal_value total_slippage_value += slippage_value stake_dict.setdefault(stake.netuid, []).append( From 8057d82039a1eaed614f1ed3ac823da9cc8f975e Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 19:10:07 -0800 Subject: [PATCH 11/15] wip --- bittensor_cli/src/commands/view.py | 74 +++--------------------------- 1 file changed, 7 insertions(+), 67 deletions(-) diff --git a/bittensor_cli/src/commands/view.py b/bittensor_cli/src/commands/view.py index 0f959fc9..26bb3eb9 100644 --- a/bittensor_cli/src/commands/view.py +++ b/bittensor_cli/src/commands/view.py @@ -31,10 +31,7 @@ async def display_network_dashboard( try: with console.status("[dark_sea_green3]Fetching data...", spinner="earth"): _subnet_data = await fetch_subnet_data(wallet, subtensor) - # Add block_hash to raw_data for RPC calls - if "block_hash" not in _subnet_data: - _subnet_data["block_hash"] = await subtensor.substrate.get_chain_head() - subnet_data = await process_subnet_data(_subnet_data, subtensor) + subnet_data = process_subnet_data(_subnet_data) html_content = generate_full_page(subnet_data) if use_wry: @@ -157,13 +154,10 @@ async def fetch_subnet_data( "old_identities": old_identities, "wallet": wallet, "block_number": block_number, - "block_hash": block_hash, } -async def process_subnet_data( - raw_data: dict[str, Any], subtensor: "SubtensorInterface" -) -> dict[str, Any]: +def process_subnet_data(raw_data: dict[str, Any]) -> dict[str, Any]: """ Process and prepare subnet data. """ @@ -175,74 +169,20 @@ async def process_subnet_data( old_identities = raw_data["old_identities"] wallet = raw_data["wallet"] block_number = raw_data["block_number"] - block_hash = raw_data.get("block_hash") pool_info = {info.netuid: info for info in subnets_info} total_ideal_stake_value = Balance.from_tao(0) total_slippage_value = Balance.from_tao(0) - # Batch calculate slippage using RPC for accurate Balancer calculations - slippage_tasks = [] - stake_list = [] - for stake in stake_info: - if stake.stake.tao > 0: - if stake.netuid == 0: - # Root subnet - no slippage, skip RPC call - stake_list.append((stake, None)) - else: - slippage_tasks.append( - subtensor.sim_swap( - origin_netuid=stake.netuid, - destination_netuid=0, - amount=stake.stake.rao, - block_hash=block_hash, - ) - ) - stake_list.append((stake, len(slippage_tasks) - 1)) - - slippage_results = ( - await asyncio.gather(*slippage_tasks, return_exceptions=True) - if slippage_tasks - else [] - ) - # Process stake stake_dict: dict[int, list[dict[str, Any]]] = {} - for stake, slippage_idx in stake_list: + for stake in stake_info: if stake.stake.tao > 0: - if stake.netuid == 0: - # Root subnet - no slippage - slippage_value = stake.stake.set_unit(0) - slippage_percentage = 0.0 - current_price = 1.0 - else: - if slippage_idx is not None and slippage_idx < len(slippage_results): - slippage_result = slippage_results[slippage_idx] - if isinstance(slippage_result, Exception): - # On RPC error, skip slippage calculation for this stake - console.print( - f"[yellow]Warning:[/yellow] Could not calculate slippage " - f"for stake on netuid {stake.netuid}. Skipping slippage display." - ) - slippage_value = Balance.from_rao(0).set_unit(0) - slippage_percentage = 0.0 - else: - current_price = pool_info[stake.netuid].price.tao - slippage_value, _, slippage_percentage = ( - slippage_result.alpha_to_tao_slippage( - alpha_amount=stake.stake, current_price=current_price - ) - ) - else: - # This should not happen, but handle gracefully - slippage_value = Balance.from_rao(0).set_unit(0) - slippage_percentage = 0.0 - # Ideal TAO value at current price (runtime price) - current_price = ( - pool_info[stake.netuid].price.tao if stake.netuid in pool_info else 0.0 - ) - ideal_value = Balance.from_tao(stake.stake.tao * current_price).set_unit(0) + slippage_value, _, slippage_percentage = pool_info[ + stake.netuid + ].alpha_to_tao_with_slippage(stake.stake) + ideal_value = pool_info[stake.netuid].alpha_to_tao(stake.stake) total_ideal_stake_value += ideal_value total_slippage_value += slippage_value stake_dict.setdefault(stake.netuid, []).append( From 491581a52ed3e53e277ed020d5db7f24ec3d07e1 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 19:10:25 -0800 Subject: [PATCH 12/15] add disabled warnings --- bittensor_cli/cli.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index dadf57eb..768eef89 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -8557,6 +8557,9 @@ def liquidity_add( json_output: bool = Options.json_output, ): """Add liquidity to the swap (as a combination of TAO + Alpha).""" + console.print_error("User liquidity is currently disabled on Bittensor.") + raise typer.Exit() + self.verbosity_handler(quiet, verbose, json_output, prompt, decline) proxy = self.is_valid_proxy_name_or_ss58(proxy, announce_only) if not netuid: @@ -8634,6 +8637,9 @@ def liquidity_list( json_output: bool = Options.json_output, ): """Displays liquidity positions in given subnet.""" + console.print_error("User liquidity is currently disabled on Bittensor.") + raise typer.Exit() + self.verbosity_handler(quiet, verbose, json_output, prompt=False) if not netuid: netuid = IntPrompt.ask( @@ -8687,6 +8693,8 @@ def liquidity_remove( ): """Remove liquidity from the swap (as a combination of TAO + Alpha).""" + console.print_error("User liquidity is currently disabled on Bittensor.") + raise typer.Exit() self.verbosity_handler(quiet, verbose, json_output, prompt, decline) proxy = self.is_valid_proxy_name_or_ss58(proxy, announce_only) if all_liquidity_ids and position_id: @@ -8763,6 +8771,8 @@ def liquidity_modify( json_output: bool = Options.json_output, ): """Modifies the liquidity position for the given subnet.""" + console.print_error("User liquidity is currently disabled on Bittensor.") + raise typer.Exit() self.verbosity_handler(quiet, verbose, json_output, prompt, decline) proxy = self.is_valid_proxy_name_or_ss58(proxy, announce_only) if not netuid: From 263f732e90e0f570bfda9d995d4d6cd834fd080b Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 19:10:44 -0800 Subject: [PATCH 13/15] disable liquidity cmd --- bittensor_cli/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 768eef89..c56926a3 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -866,7 +866,7 @@ def __init__(self): self.subnet_mechanisms_app = typer.Typer(epilog=_epilog) self.weights_app = typer.Typer(epilog=_epilog) self.view_app = typer.Typer(epilog=_epilog) - self.liquidity_app = typer.Typer(epilog=_epilog) + self.liquidity_app = typer.Typer(epilog=_epilog, hidden=True) self.crowd_app = typer.Typer(epilog=_epilog) self.utils_app = typer.Typer(epilog=_epilog) self.axon_app = typer.Typer(epilog=_epilog) From f6e9af74b701bf14d599b951ac5fdb8c6ebabbb7 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 19:11:30 -0800 Subject: [PATCH 14/15] cleanup --- bittensor_cli/src/bittensor/chain_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor_cli/src/bittensor/chain_data.py b/bittensor_cli/src/bittensor/chain_data.py index 4b144a44..af2b4711 100644 --- a/bittensor_cli/src/bittensor/chain_data.py +++ b/bittensor_cli/src/bittensor/chain_data.py @@ -1,7 +1,7 @@ from abc import abstractmethod from dataclasses import dataclass from enum import Enum -from typing import Optional, Any, Union, Literal +from typing import Optional, Any, Union import netaddr from scalecodec.utils.ss58 import ss58_encode From 86973fabd5bc492a5cb0b4168e4db78e3e6d9593 Mon Sep 17 00:00:00 2001 From: ibraheem-latent Date: Fri, 30 Jan 2026 19:13:48 -0800 Subject: [PATCH 15/15] ruff --- bittensor_cli/src/commands/stake/add.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bittensor_cli/src/commands/stake/add.py b/bittensor_cli/src/commands/stake/add.py index 340f336e..a024d560 100644 --- a/bittensor_cli/src/commands/stake/add.py +++ b/bittensor_cli/src/commands/stake/add.py @@ -741,4 +741,3 @@ def _print_table_and_slippage(table: Table, max_slippage: float, safe_staking: b - [bold white]Partial staking[/bold white]: If True, allows staking up to the rate tolerance limit. If False, the entire transaction will fail if rate tolerance is exceeded.\n""" console.print(base_description + (safe_staking_description if safe_staking else "")) -