Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c394ec0
Fixed broken type annotation.
thewhaleking Jul 8, 2025
4ca8ff8
Merge pull request #523 from opentensor/fix/thewhaleking/random-broke…
thewhaleking Jul 8, 2025
4344a24
Partially fix slippage display
gztensor Jul 8, 2025
a343018
Fixes issue where netuid is 0 and treated as None
thewhaleking Jul 8, 2025
01963a0
Update bittensor_cli/src/bittensor/subtensor_interface.py
ibraheem-abe Jul 8, 2025
73498c4
Updated to use query map
thewhaleking Jul 8, 2025
da67a42
typo
thewhaleking Jul 8, 2025
8ce752b
Better calculation
thewhaleking Jul 8, 2025
1c449fa
Refactored to included the price in the initial all_subnets call
thewhaleking Jul 8, 2025
182ea5e
Added the same logic to SubtensorInterface.subnet
thewhaleking Jul 8, 2025
617889b
Adds a unit test lol
thewhaleking Jul 8, 2025
158feb2
updates price sent to extrinsic
ibraheem-abe Jul 8, 2025
7ad84c2
ruff
ibraheem-abe Jul 8, 2025
9ba1ff8
updates stake remove
ibraheem-abe Jul 8, 2025
069234c
updates move cmds
ibraheem-abe Jul 8, 2025
7b40887
update remove all
ibraheem-abe Jul 8, 2025
ee15a4c
updates wallet balance
ibraheem-abe Jul 8, 2025
4be75cb
updates stake list
ibraheem-abe Jul 8, 2025
86c032d
ruff
ibraheem-abe Jul 8, 2025
59ba1da
added docstrings
ibraheem-abe Jul 8, 2025
5ccd5fa
Merge pull request #526 from opentensor/update/slippage-price-calcs
ibraheem-abe Jul 8, 2025
a0fb6cb
Merge pull request #525 from opentensor/fix/thewhaleking/netuid-0
ibraheem-abe Jul 8, 2025
82445e4
bumps version and changelog
ibraheem-abe Jul 9, 2025
c1cac5c
Merge pull request #527 from opentensor/changelog/981
ibraheem-abe Jul 9, 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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 9.8.1/2025-07-08

## What's Changed
* Fixed broken type annotation. by @thewhaleking in https://github.com/opentensor/btcli/pull/523
* Update/slippage price calcs by @ibraheem-abe in https://github.com/opentensor/btcli/pull/526
* Partially fix slippage display by @gztensor in https://github.com/opentensor/btcli/pull/524
* stake add: netuid 0 by @thewhaleking in https://github.com/opentensor/btcli/pull/525

## New Contributors
* @gztensor made their first contribution in https://github.com/opentensor/btcli/pull/524

**Full Changelog**: https://github.com/opentensor/btcli/compare/v9.8.0...v9.8.1

## 9.8.0/2025-07-07

## What's Changed
Expand Down
2 changes: 1 addition & 1 deletion bittensor_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3352,7 +3352,7 @@ def stake_add(
)
else:
netuid_ = get_optional_netuid(None, all_netuids)
netuids = [netuid_] if netuid_ else None
netuids = [netuid_] if netuid_ is not None else None
if netuids:
for netuid_ in netuids:
# ensure no negative netuids make it into our list
Expand Down
89 changes: 78 additions & 11 deletions bittensor_cli/src/bittensor/subtensor_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -1423,23 +1423,40 @@ async def get_stake_for_coldkeys(
return stake_info_map if stake_info_map else None

async def all_subnets(self, block_hash: Optional[str] = None) -> list[DynamicInfo]:
result = await self.query_runtime_api(
"SubnetInfoRuntimeApi",
"get_all_dynamic_info",
block_hash=block_hash,
result, prices = await asyncio.gather(
self.query_runtime_api(
"SubnetInfoRuntimeApi",
"get_all_dynamic_info",
block_hash=block_hash,
),
self.get_subnet_prices(block_hash=block_hash, page_size=129),
)
return DynamicInfo.list_from_any(result)
sns: list[DynamicInfo] = DynamicInfo.list_from_any(result)
for sn in sns:
if sn.netuid == 0:
sn.price = Balance.from_tao(1.0)
else:
try:
sn.price = prices[sn.netuid]
except KeyError:
sn.price = sn.tao_in / sn.alpha_in
return sns

async def subnet(
self, netuid: int, block_hash: Optional[str] = None
) -> "DynamicInfo":
result = await self.query_runtime_api(
"SubnetInfoRuntimeApi",
"get_dynamic_info",
params=[netuid],
block_hash=block_hash,
result, price = await asyncio.gather(
self.query_runtime_api(
"SubnetInfoRuntimeApi",
"get_dynamic_info",
params=[netuid],
block_hash=block_hash,
),
self.get_subnet_price(netuid=netuid, block_hash=block_hash),
)
return DynamicInfo.from_any(result)
subnet_ = DynamicInfo.from_any(result)
subnet_.price = price
return subnet_

async def get_owned_hotkeys(
self,
Expand Down Expand Up @@ -1581,3 +1598,53 @@ async def get_coldkey_swap_schedule_duration(
)

return result

async def get_subnet_price(
self,
netuid: int = None,
block_hash: Optional[str] = None,
) -> Balance:
"""
Gets the current Alpha price in TAO for a specific subnet.

:param netuid: The unique identifier of the subnet.
:param block_hash: The hash of the block to retrieve the price from.

:return: The current Alpha price in TAO units for the specified subnet.
"""
current_sqrt_price = await self.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))

async def get_subnet_prices(
self, block_hash: Optional[str] = None, page_size: int = 100
) -> 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.
"""
query = await self.substrate.query_map(
module="Swap",
storage_function="AlphaSqrtPrice",
page_size=page_size,
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))

return map_
52 changes: 32 additions & 20 deletions bittensor_cli/src/commands/stake/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,12 @@ async def stake_extrinsic(
# Get subnet data and stake information for coldkey
chain_head = await subtensor.substrate.get_chain_head()
_all_subnets, _stake_info, current_wallet_balance = await asyncio.gather(
subtensor.all_subnets(),
subtensor.all_subnets(block_hash=chain_head),
subtensor.get_stake_for_coldkey(
coldkey_ss58=wallet.coldkeypub.ss58_address,
block_hash=chain_head,
),
subtensor.get_balance(wallet.coldkeypub.ss58_address),
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash=chain_head),
)
all_subnets = {di.netuid: di for di in _all_subnets}

Expand Down Expand Up @@ -307,6 +307,7 @@ async def stake_extrinsic(
return False
remaining_wallet_balance -= amount_to_stake

# TODO this should be asyncio gathered before the for loop
stake_fee = await subtensor.get_stake_fee(
origin_hotkey_ss58=None,
origin_netuid=None,
Expand All @@ -318,14 +319,20 @@ async def stake_extrinsic(
)

# 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)
# 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(subnet_info.price.tao)
rate = 1.0 / current_price_float
received_amount = rate * amount_to_stake

# Add rows for the table
base_row = [
Expand All @@ -336,19 +343,19 @@ 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 * (
1 + rate_tolerance
price_with_tolerance = current_price_float * (1 + rate_tolerance)
_rate_with_tolerance = (
1.0 / price_with_tolerance
) # Rate only for display
rate_with_tolerance = f"{_rate_with_tolerance:.4f}"
price_with_tolerance = Balance.from_tao(
_rate_with_tolerance
price_with_tolerance
).rao # Actual price to pass to extrinsic
else:
rate_with_tolerance = "1"
Expand Down Expand Up @@ -581,9 +588,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(
Expand Down Expand Up @@ -628,8 +636,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.
Expand All @@ -654,6 +662,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

Expand All @@ -670,6 +681,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
)
Expand Down
Loading
Loading