Skip to content

Commit d26b319

Browse files
authored
Merge pull request #2954 from opentensor/release/9.8.1
Release/9.8.1
2 parents e437b7f + e07cd11 commit d26b319

File tree

14 files changed

+369
-69
lines changed

14 files changed

+369
-69
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## 9.8.1 /2025-07-08
4+
5+
## What's Changed
6+
* New logic to get price for `DynamicInfo` by @basfroman in https://github.com/opentensor/bittensor/pull/2952
7+
* Update to safe_staking limits by @ibraheem-abe in https://github.com/opentensor/bittensor/pull/2950
8+
9+
**Full Changelog**: https://github.com/opentensor/bittensor/compare/v9.8.0...v9.8.1
10+
311
## 9.8.0 /2025-07-07
412

513
## What's Changed

bittensor/core/async_subtensor.py

Lines changed: 101 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -646,13 +646,21 @@ async def all_subnets(
646646
)
647647
if not block_hash and reuse_block:
648648
block_hash = self.substrate.last_block_hash
649-
query = await self.substrate.runtime_call(
650-
"SubnetInfoRuntimeApi",
651-
"get_all_dynamic_info",
652-
block_hash=block_hash,
649+
650+
query, subnet_prices = await asyncio.gather(
651+
self.substrate.runtime_call(
652+
"SubnetInfoRuntimeApi",
653+
"get_all_dynamic_info",
654+
block_hash=block_hash,
655+
),
656+
self.get_subnet_prices(),
653657
)
654-
subnets = DynamicInfo.list_from_dicts(query.decode())
655-
return subnets
658+
659+
decoded = query.decode()
660+
661+
for sn in decoded:
662+
sn.update({"price": subnet_prices.get(sn["netuid"], Balance.from_tao(0))})
663+
return DynamicInfo.list_from_dicts(decoded)
656664

657665
async def blocks_since_last_step(
658666
self,
@@ -902,8 +910,13 @@ async def get_all_subnets_info(
902910
)
903911
if not result:
904912
return []
905-
else:
906-
return SubnetInfo.list_from_dicts(result)
913+
914+
subnets_prices = await self.get_subnet_prices()
915+
916+
for subnet in result:
917+
subnet.update({"price": subnets_prices.get(subnet["netuid"], 0)})
918+
919+
return SubnetInfo.list_from_dicts(result)
907920

908921
async def get_balance(
909922
self,
@@ -2267,6 +2280,84 @@ async def get_subnet_info(
22672280
return None
22682281
return SubnetInfo.from_dict(result)
22692282

2283+
async def get_subnet_price(
2284+
self,
2285+
netuid: int,
2286+
block: Optional[int] = None,
2287+
block_hash: Optional[str] = None,
2288+
reuse_block: bool = False,
2289+
) -> Balance:
2290+
"""Gets the current Alpha price in TAO for all subnets.
2291+
2292+
Arguments:
2293+
netuid: The unique identifier of the subnet.
2294+
block: The blockchain block number for the query.
2295+
block_hash (Optional[str]): The hash of the block to retrieve the stake from. Do not specify if using block
2296+
or reuse_block
2297+
reuse_block (bool): Whether to use the last-used block. Do not set if using block_hash or block.
2298+
2299+
Returns:
2300+
The current Alpha price in TAO units for the specified subnet.
2301+
"""
2302+
# SN0 price is always 1 TAO
2303+
if netuid == 0:
2304+
return Balance.from_tao(1)
2305+
2306+
block_hash = await self.determine_block_hash(
2307+
block=block, block_hash=block_hash, reuse_block=reuse_block
2308+
)
2309+
current_sqrt_price = await self.substrate.query(
2310+
module="Swap",
2311+
storage_function="AlphaSqrtPrice",
2312+
params=[netuid],
2313+
block_hash=block_hash,
2314+
)
2315+
2316+
current_sqrt_price = fixed_to_float(current_sqrt_price)
2317+
current_price = current_sqrt_price * current_sqrt_price
2318+
return Balance.from_rao(int(current_price * 1e9))
2319+
2320+
async def get_subnet_prices(
2321+
self,
2322+
block: Optional[int] = None,
2323+
block_hash: Optional[str] = None,
2324+
reuse_block: bool = False,
2325+
) -> dict[int, Balance]:
2326+
"""Gets the current Alpha price in TAO for a specified subnet.
2327+
2328+
Args:
2329+
block: The blockchain block number for the query.
2330+
block_hash (Optional[str]): The hash of the block to retrieve the stake from. Do not specify if using block
2331+
or reuse_block
2332+
reuse_block (bool): Whether to use the last-used block. Do not set if using block_hash or block.
2333+
2334+
Returns:
2335+
dict:
2336+
- subnet unique ID
2337+
- The current Alpha price in TAO units for the specified subnet.
2338+
"""
2339+
block_hash = await self.determine_block_hash(
2340+
block=block, block_hash=block_hash, reuse_block=reuse_block
2341+
)
2342+
2343+
current_sqrt_prices = await self.substrate.query_map(
2344+
module="Swap",
2345+
storage_function="AlphaSqrtPrice",
2346+
block_hash=block_hash,
2347+
page_size=129, # total number of subnets
2348+
)
2349+
2350+
prices = {}
2351+
async for id_, current_sqrt_price in current_sqrt_prices:
2352+
current_sqrt_price = fixed_to_float(current_sqrt_price)
2353+
current_price = current_sqrt_price * current_sqrt_price
2354+
current_price_in_tao = Balance.from_rao(int(current_price * 1e9))
2355+
prices.update({id_: current_price_in_tao})
2356+
2357+
# SN0 price is always 1 TAO
2358+
prices.update({0: Balance.from_tao(1)})
2359+
return prices
2360+
22702361
async def get_unstake_fee(
22712362
self,
22722363
amount: Balance,
@@ -3336,7 +3427,8 @@ async def subnet(
33363427
)
33373428

33383429
if isinstance(decoded := query.decode(), dict):
3339-
return DynamicInfo.from_dict(decoded)
3430+
price = self.get_subnet_price(netuid=netuid, block=block)
3431+
return DynamicInfo.from_dict({**decoded, "price": price})
33403432
return None
33413433

33423434
async def subnet_exists(

bittensor/core/chain_data/dynamic_info.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class DynamicInfo(InfoBase):
2727
alpha_in: Balance
2828
alpha_out: Balance
2929
tao_in: Balance
30-
price: Balance
30+
price: Optional[Balance]
3131
k: float
3232
is_dynamic: bool
3333
alpha_out_emission: Balance
@@ -74,13 +74,6 @@ def _from_dict(cls, decoded: dict) -> "DynamicInfo":
7474
).set_unit(0)
7575

7676
subnet_volume = Balance.from_rao(decoded["subnet_volume"]).set_unit(netuid)
77-
price = (
78-
Balance.from_tao(1.0)
79-
if netuid == 0
80-
else Balance.from_tao(tao_in.tao / alpha_in.tao).set_unit(netuid)
81-
if alpha_in.tao > 0
82-
else Balance.from_tao(1).set_unit(netuid)
83-
) # Root always has 1-1 price
8477

8578
if decoded.get("subnet_identity"):
8679
subnet_identity = SubnetIdentity(
@@ -97,6 +90,10 @@ def _from_dict(cls, decoded: dict) -> "DynamicInfo":
9790
)
9891
else:
9992
subnet_identity = None
93+
price = decoded.get("price", None)
94+
95+
if price and not isinstance(price, Balance):
96+
raise ValueError(f"price must be a Balance object, got {type(price)}.")
10097

10198
return cls(
10299
netuid=netuid,

bittensor/core/extrinsics/asyncex/staking.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -120,28 +120,31 @@ async def add_stake_extrinsic(
120120

121121
if safe_staking:
122122
pool = await subtensor.subnet(netuid=netuid)
123-
base_price = pool.price.rao
124-
price_with_tolerance = base_price * (1 + rate_tolerance)
125-
call_params.update(
126-
{
127-
"limit_price": price_with_tolerance,
128-
"allow_partial": allow_partial_stake,
129-
}
130-
)
131-
call_function = "add_stake_limit"
123+
base_price = pool.price.tao
124+
125+
if pool.netuid == 0:
126+
price_with_tolerance = base_price
127+
else:
128+
price_with_tolerance = base_price * (1 + rate_tolerance)
132129

133-
# For logging
134-
base_rate = pool.price.tao
135-
rate_with_tolerance = base_rate * (1 + rate_tolerance)
136130
logging.info(
137131
f":satellite: [magenta]Safe Staking to:[/magenta] "
138132
f"[blue]netuid: [green]{netuid}[/green], amount: [green]{staking_balance}[/green], "
139133
f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], "
140-
f"price limit: [green]{rate_with_tolerance}[/green], "
141-
f"original price: [green]{base_rate}[/green], "
134+
f"price limit: [green]{price_with_tolerance}[/green], "
135+
f"original price: [green]{base_price}[/green], "
142136
f"with partial stake: [green]{allow_partial_stake}[/green] "
143137
f"on [blue]{subtensor.network}[/blue][/magenta]...[/magenta]"
144138
)
139+
140+
limit_price = Balance.from_tao(price_with_tolerance).rao
141+
call_params.update(
142+
{
143+
"limit_price": limit_price,
144+
"allow_partial": allow_partial_stake,
145+
}
146+
)
147+
call_function = "add_stake_limit"
145148
else:
146149
logging.info(
147150
f":satellite: [magenta]Staking to:[/magenta] "

bittensor/core/extrinsics/asyncex/unstaking.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,26 +107,27 @@ async def unstake_extrinsic(
107107
}
108108
if safe_staking:
109109
pool = await subtensor.subnet(netuid=netuid)
110-
base_price = pool.price.rao
111-
price_with_tolerance = base_price * (1 - rate_tolerance)
110+
base_price = pool.price.tao
112111

113-
# For logging
114-
base_rate = pool.price.tao
115-
rate_with_tolerance = base_rate * (1 - rate_tolerance)
112+
if pool.netuid == 0:
113+
price_with_tolerance = base_price
114+
else:
115+
price_with_tolerance = base_price * (1 - rate_tolerance)
116116

117117
logging.info(
118118
f":satellite: [magenta]Safe Unstaking from:[/magenta] "
119119
f"netuid: [green]{netuid}[/green], amount: [green]{unstaking_balance}[/green], "
120120
f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], "
121-
f"price limit: [green]{rate_with_tolerance}[/green], "
122-
f"original price: [green]{base_rate}[/green], "
121+
f"price limit: [green]{price_with_tolerance}[/green], "
122+
f"original price: [green]{base_price}[/green], "
123123
f"with partial unstake: [green]{allow_partial_stake}[/green] "
124124
f"on [blue]{subtensor.network}[/blue][magenta]...[/magenta]"
125125
)
126126

127+
limit_price = Balance.from_tao(price_with_tolerance).rao
127128
call_params.update(
128129
{
129-
"limit_price": price_with_tolerance,
130+
"limit_price": limit_price,
130131
"allow_partial": allow_partial_stake,
131132
}
132133
)

bittensor/core/extrinsics/staking.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,26 +112,27 @@ def add_stake_extrinsic(
112112

113113
if safe_staking:
114114
pool = subtensor.subnet(netuid=netuid)
115-
base_price = pool.price.rao
116-
price_with_tolerance = base_price * (1 + rate_tolerance)
115+
base_price = pool.price.tao
117116

118-
# For logging
119-
base_rate = pool.price.tao
120-
rate_with_tolerance = base_rate * (1 + rate_tolerance)
117+
if pool.netuid == 0:
118+
price_with_tolerance = base_price
119+
else:
120+
price_with_tolerance = base_price * (1 + rate_tolerance)
121121

122122
logging.info(
123123
f":satellite: [magenta]Safe Staking to:[/magenta] "
124124
f"[blue]netuid: [green]{netuid}[/green], amount: [green]{staking_balance}[/green], "
125125
f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], "
126-
f"price limit: [green]{rate_with_tolerance}[/green], "
127-
f"original price: [green]{base_rate}[/green], "
126+
f"price limit: [green]{price_with_tolerance}[/green], "
127+
f"original price: [green]{base_price}[/green], "
128128
f"with partial stake: [green]{allow_partial_stake}[/green] "
129129
f"on [blue]{subtensor.network}[/blue][/magenta]...[/magenta]"
130130
)
131131

132+
limit_price = Balance.from_tao(price_with_tolerance).rao
132133
call_params.update(
133134
{
134-
"limit_price": price_with_tolerance,
135+
"limit_price": limit_price,
135136
"allow_partial": allow_partial_stake,
136137
}
137138
)

bittensor/core/extrinsics/unstaking.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,26 +105,27 @@ def unstake_extrinsic(
105105

106106
if safe_staking:
107107
pool = subtensor.subnet(netuid=netuid)
108-
base_price = pool.price.rao
109-
price_with_tolerance = base_price * (1 - rate_tolerance)
108+
base_price = pool.price.tao
110109

111-
# For logging
112-
base_rate = pool.price.tao
113-
rate_with_tolerance = base_rate * (1 - rate_tolerance)
110+
if pool.netuid == 0:
111+
price_with_tolerance = base_price
112+
else:
113+
price_with_tolerance = base_price * (1 - rate_tolerance)
114114

115115
logging.info(
116116
f":satellite: [magenta]Safe Unstaking from:[/magenta] "
117117
f"netuid: [green]{netuid}[/green], amount: [green]{unstaking_balance}[/green], "
118118
f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], "
119-
f"price limit: [green]{rate_with_tolerance}[/green], "
120-
f"original price: [green]{base_rate}[/green], "
119+
f"price limit: [green]{price_with_tolerance}[/green], "
120+
f"original price: [green]{base_price}[/green], "
121121
f"with partial unstake: [green]{allow_partial_stake}[/green] "
122122
f"on [blue]{subtensor.network}[/blue][magenta]...[/magenta]"
123123
)
124124

125+
limit_price = Balance.from_tao(price_with_tolerance).rao
125126
call_params.update(
126127
{
127-
"limit_price": price_with_tolerance,
128+
"limit_price": limit_price,
128129
"allow_partial": allow_partial_stake,
129130
}
130131
)

0 commit comments

Comments
 (0)