diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index c274246ff..e9c338ba1 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -1581,3 +1581,21 @@ async def get_coldkey_swap_schedule_duration( ) return result + + async def get_subnet_price( + self, + netuid: int = None, + block_hash: Optional[str] = None, + ) -> Balance: + if not block_hash: + block_hash = await self.substrate.get_chain_head() + current_sqrt_price = await self.substrate.query( + module="Swap", + storage_function="AlphaSqrtPrice", + params=[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)) diff --git a/bittensor_cli/src/commands/stake/add.py b/bittensor_cli/src/commands/stake/add.py index a8d1ecc59..36345c2db 100644 --- a/bittensor_cli/src/commands/stake/add.py +++ b/bittensor_cli/src/commands/stake/add.py @@ -307,27 +307,36 @@ async def stake_extrinsic( return False remaining_wallet_balance -= amount_to_stake - stake_fee = await subtensor.get_stake_fee( - origin_hotkey_ss58=None, - origin_netuid=None, - origin_coldkey_ss58=wallet.coldkeypub.ss58_address, - destination_hotkey_ss58=hotkey[1], - destination_netuid=netuid, - destination_coldkey_ss58=wallet.coldkeypub.ss58_address, - amount=amount_to_stake.rao, + stake_fee, current_price = await asyncio.gather( + subtensor.get_stake_fee( + origin_hotkey_ss58=None, + origin_netuid=None, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_hotkey_ss58=hotkey[1], + destination_netuid=netuid, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, + amount=amount_to_stake.rao, + ), + subtensor.get_subnet_price(netuid=netuid), ) # Calculate slippage - 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) - - # Add rows for the table + # 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 + current_price_float = float(current_price.tao) + rate = 1. / current_price_float + received_amount = rate * amount_to_stake + + # Add rows for the table base_row = [ str(netuid), # netuid f"{hotkey[1]}", # hotkey @@ -336,16 +345,16 @@ async def stake_extrinsic( + f" {Balance.get_unit(netuid)}/{Balance.get_unit(0)} ", # rate str(received_amount.set_unit(netuid)), # received str(stake_fee), # fee - str(slippage_pct), # slippage + # str(slippage_pct), # slippage ] # If we are staking safe, add price tolerance if safe_staking: if subnet_info.is_dynamic: - rate = amount_to_stake.rao / received_amount.rao - _rate_with_tolerance = rate * ( + price_with_tolerance_float = current_price_float * ( 1 + rate_tolerance - ) # Rate only for display + ) + _rate_with_tolerance = 1. / price_with_tolerance_float # Rate only for display rate_with_tolerance = f"{_rate_with_tolerance:.4f}" price_with_tolerance = Balance.from_tao( _rate_with_tolerance @@ -581,9 +590,10 @@ def _define_stake_table( justify="center", style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"], ) - table.add_column( - "Slippage", justify="center", style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"] - ) + # TODO: Uncomment when slippage is reimplemented for v3 + # table.add_column( + # "Slippage", justify="center", style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"] + # ) if safe_staking: table.add_column( @@ -628,8 +638,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. @@ -654,6 +664,9 @@ def _calculate_slippage( - 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 @@ -670,6 +683,7 @@ def _calculate_slippage( 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 ) diff --git a/bittensor_cli/src/commands/stake/remove.py b/bittensor_cli/src/commands/stake/remove.py index b2ef8b608..f8584ef07 100644 --- a/bittensor_cli/src/commands/stake/remove.py +++ b/bittensor_cli/src/commands/stake/remove.py @@ -65,7 +65,13 @@ async def unstake( wallet.coldkeypub.ss58_address, block_hash=chain_head ), ) - all_sn_dynamic_info = {info.netuid: info for info in all_sn_dynamic_info_} + prices = await asyncio.gather(*[ + subtensor.get_subnet_price(netuid=netuid) for _ in all_sn_dynamic_info_ + ]) + + for item, price in zip(all_sn_dynamic_info_, prices): + item.price = price + all_sn_dynamic_info = {info.netuid: info for info in all_sn_dynamic_info_} if interactive: try: @@ -241,8 +247,8 @@ async def unstake( str(subnet_info.price.tao) + f"({Balance.get_unit(0)}/{Balance.get_unit(netuid)})", # Rate str(stake_fee), # Fee - str(received_amount), # Received Amount - slippage_pct, # Slippage Percent + # str(received_amount), # Received Amount + # slippage_pct, # Slippage Percent ] # Additional fields for safe unstaking @@ -443,16 +449,16 @@ async def unstake_all( justify="center", style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"], ) - table.add_column( - f"Received ({Balance.unit})", - justify="center", - style=COLOR_PALETTE["POOLS"]["TAO_EQUIV"], - ) - table.add_column( - "Slippage", - justify="center", - style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"], - ) + # table.add_column( + # f"Received ({Balance.unit})", + # justify="center", + # style=COLOR_PALETTE["POOLS"]["TAO_EQUIV"], + # ) + # table.add_column( + # "Slippage", + # justify="center", + # style=COLOR_PALETTE["STAKE"]["SLIPPAGE_PERCENT"], + # ) # Calculate slippage and total received max_slippage = 0.0 @@ -490,8 +496,8 @@ async def unstake_all( str(float(subnet_info.price)) + f"({Balance.get_unit(0)}/{Balance.get_unit(stake.netuid)})", str(stake_fee), - str(received_amount), - slippage_pct, + # str(received_amount), + # slippage_pct, ) console.print(table) if max_slippage > 5: @@ -863,6 +869,9 @@ def _calculate_slippage( - received_amount: Balance after slippage deduction - slippage_pct: Formatted string of slippage percentage - slippage_pct_float: Float value of slippage percentage + + TODO: Update to v3. This method only works for protocol-liquidity-only + mode (user liquidity disabled) """ received_amount, _, _ = subnet_info.alpha_to_tao_with_slippage(amount) received_amount -= stake_fee @@ -884,6 +893,7 @@ def _calculate_slippage( ) slippage_pct = f"{slippage_pct_float:.4f} %" else: + # TODO: Fix this. Slippage is always zero for static networks. # Root will only have fee-based slippage slippage_pct_float = ( 100 * float(stake_fee.tao) / float(amount.tao) if amount.tao != 0 else 0 @@ -993,7 +1003,7 @@ async def _unstake_selection( table.add_column("Symbol", style=COLOR_PALETTE["GENERAL"]["SYMBOL"]) table.add_column("Stake Amount", style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"]) table.add_column( - f"[bold white]RATE ({Balance.get_unit(0)}_in/{Balance.get_unit(1)}_in)", + f"[bold white]Rate ({Balance.get_unit(0)}/{Balance.get_unit(1)})", style=COLOR_PALETTE["POOLS"]["RATE"], justify="left", ) @@ -1255,15 +1265,15 @@ def _create_unstake_table( justify="center", style=COLOR_PALETTE["STAKE"]["STAKE_AMOUNT"], ) - table.add_column( - f"Received ({Balance.get_unit(0)})", - justify="center", - 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( + # f"Received ({Balance.get_unit(0)})", + # justify="center", + # style=COLOR_PALETTE["POOLS"]["TAO_EQUIV"], + # footer=str(total_received_amount), + # ) + # 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]",