Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 23 additions & 13 deletions bittensor/core/async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -799,15 +799,15 @@ async def all_subnets(
subnets = await subtensor.all_subnets()
"""
block_hash = await self.determine_block_hash(
block_number, block_hash, reuse_block
block=block_number, block_hash=block_hash, reuse_block=reuse_block
)
if not block_hash and reuse_block:
block_hash = self.substrate.last_block_hash

query, subnet_prices = await asyncio.gather(
self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_all_dynamic_info",
api="SubnetInfoRuntimeApi",
method="get_all_dynamic_info",
block_hash=block_hash,
),
self.get_subnet_prices(),
Expand Down Expand Up @@ -2616,9 +2616,7 @@ async def get_subnet_price(
if netuid == 0:
return Balance.from_tao(1)

block_hash = await self.determine_block_hash(
block=block, block_hash=block_hash, reuse_block=reuse_block
)
block_hash = await self.determine_block_hash(block=block)
current_sqrt_price = await self.substrate.query(
module="Swap",
storage_function="AlphaSqrtPrice",
Expand Down Expand Up @@ -3729,20 +3727,32 @@ async def subnet(
Returns:
Optional[DynamicInfo]: A DynamicInfo object, containing detailed information about a subnet.
"""
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
block_hash = await self.determine_block_hash(
block=block, block_hash=block_hash, reuse_block=reuse_block
)

if not block_hash and reuse_block:
block_hash = self.substrate.last_block_hash

query = await self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_dynamic_info",
params=[netuid],
block_hash=block_hash,
query, price = await asyncio.gather(
self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_dynamic_info",
params=[netuid],
block_hash=block_hash,
),
self.get_subnet_price(
netuid=netuid,
block=block,
block_hash=block_hash,
reuse_block=reuse_block,
),
return_exceptions=True,
)

if isinstance(decoded := query.decode(), dict):
price = self.get_subnet_price(netuid=netuid, block=block)
if isinstance(price, SubstrateRequestException):
price = None
return DynamicInfo.from_dict({**decoded, "price": price})
return None

Expand Down
31 changes: 19 additions & 12 deletions bittensor/core/chain_data/dynamic_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,24 @@ def _from_dict(cls, decoded: dict) -> "DynamicInfo":

subnet_volume = Balance.from_rao(decoded["subnet_volume"]).set_unit(netuid)

if decoded.get("subnet_identity"):
if subnet_identity := decoded.get("subnet_identity"):
# we need to check it for keep backwards compatibility
logo_bytes = subnet_identity.get("logo_url")
si_logo_url = bytes(logo_bytes).decode() if logo_bytes else None

subnet_identity = SubnetIdentity(
subnet_name=bytes(decoded["subnet_identity"]["subnet_name"]).decode(),
github_repo=bytes(decoded["subnet_identity"]["github_repo"]).decode(),
subnet_contact=bytes(
decoded["subnet_identity"]["subnet_contact"]
).decode(),
subnet_url=bytes(decoded["subnet_identity"]["subnet_url"]).decode(),
logo_url=bytes(decoded["subnet_identity"]["logo_url"]).decode(),
discord=bytes(decoded["subnet_identity"]["discord"]).decode(),
description=bytes(decoded["subnet_identity"]["description"]).decode(),
additional=bytes(decoded["subnet_identity"]["additional"]).decode(),
subnet_name=bytes(subnet_identity["subnet_name"]).decode(),
github_repo=bytes(subnet_identity["github_repo"]).decode(),
subnet_contact=bytes(subnet_identity["subnet_contact"]).decode(),
subnet_url=bytes(subnet_identity["subnet_url"]).decode(),
logo_url=si_logo_url,
discord=bytes(subnet_identity["discord"]).decode(),
description=bytes(subnet_identity["description"]).decode(),
additional=bytes(subnet_identity["additional"]).decode(),
)
else:
subnet_identity = None

price = decoded.get("price", None)

if price and not isinstance(price, Balance):
Expand All @@ -110,7 +113,11 @@ def _from_dict(cls, decoded: dict) -> "DynamicInfo":
tao_in=tao_in,
k=tao_in.rao * alpha_in.rao,
is_dynamic=is_dynamic,
price=price,
price=(
price
if price is not None
else Balance.from_tao(tao_in.tao / alpha_in.tao).set_unit(netuid)
),
alpha_out_emission=alpha_out_emission,
alpha_in_emission=alpha_in_emission,
tao_in_emission=tao_in_emission,
Expand Down
18 changes: 10 additions & 8 deletions bittensor/core/subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,15 +445,14 @@ def all_subnets(self, block: Optional[int] = None) -> Optional[list["DynamicInfo
Optional[DynamicInfo]: A list of DynamicInfo objects, each containing detailed information about a subnet.

"""
block_hash = self.determine_block_hash(block)
block_hash = self.determine_block_hash(block=block)
query = self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_all_dynamic_info",
api="SubnetInfoRuntimeApi",
method="get_all_dynamic_info",
block_hash=block_hash,
)
subnet_prices = self.get_subnet_prices()
decoded = query.decode()

for sn in decoded:
sn.update({"price": subnet_prices.get(sn["netuid"], Balance.from_tao(0))})
return DynamicInfo.list_from_dicts(decoded)
Expand Down Expand Up @@ -2696,17 +2695,20 @@ def subnet(self, netuid: int, block: Optional[int] = None) -> Optional[DynamicIn
Optional[DynamicInfo]: A DynamicInfo object, containing detailed information about a subnet.

"""
block_hash = self.determine_block_hash(block)
block_hash = self.determine_block_hash(block=block)

query = self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_dynamic_info",
api="SubnetInfoRuntimeApi",
method="get_dynamic_info",
params=[netuid],
block_hash=block_hash,
)

if isinstance(decoded := query.decode(), dict):
price = self.get_subnet_price(netuid=netuid, block=block)
try:
price = self.get_subnet_price(netuid=netuid, block=block)
except SubstrateRequestException:
price = None
return DynamicInfo.from_dict({**decoded, "price": price})
return None

Expand Down
86 changes: 83 additions & 3 deletions tests/unit_tests/test_async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3833,9 +3833,7 @@ async def test_get_subnet_price(subtensor, mocker):
)

# Asserts
mocked_determine_block_hash.assert_awaited_once_with(
block=None, block_hash=None, reuse_block=False
)
mocked_determine_block_hash.assert_awaited_once_with(block=None)
mocked_query.assert_awaited_once_with(
module="Swap",
storage_function="AlphaSqrtPrice",
Expand Down Expand Up @@ -3875,3 +3873,85 @@ async def fake_current_sqrt_prices():
page_size=129, # total number of subnets
)
assert result == expected_prices


@pytest.mark.asyncio
async def test_all_subnets(subtensor, mocker):
"""Verify that `all_subnets` calls proper methods and returns the correct value."""
# Preps
mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
mocked_di_list_from_dicts = mocker.patch.object(
async_subtensor.DynamicInfo, "list_from_dicts"
)
mocked_get_subnet_prices = mocker.patch.object(
subtensor,
"get_subnet_prices",
return_value={0: Balance.from_tao(1), 1: Balance.from_tao(0.029258617)},
)
mocked_decode = mocker.Mock(return_value=[{"netuid": 0}, {"netuid": 1}])
mocked_runtime_call = mocker.Mock(decode=mocked_decode)
mocker.patch.object(
subtensor.substrate, "runtime_call", return_value=mocked_runtime_call
)

# Call
result = await subtensor.all_subnets()

# Asserts
mocked_determine_block_hash.assert_awaited_once_with(
block=None, block_hash=None, reuse_block=False
)
subtensor.substrate.runtime_call.assert_called_once_with(
api="SubnetInfoRuntimeApi",
method="get_all_dynamic_info",
block_hash=mocked_determine_block_hash.return_value,
)
mocked_get_subnet_prices.assert_called_once()
mocked_di_list_from_dicts.assert_called_once_with(
[
{"netuid": 0, "price": Balance.from_tao(1)},
{"netuid": 1, "price": Balance.from_tao(0.029258617)},
]
)
assert result == mocked_di_list_from_dicts.return_value


@pytest.mark.asyncio
async def test_subnet(subtensor, mocker):
"""Verify that `subnet` calls proper methods and returns the correct value."""
# Preps
netuid = 14
mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
mocked_di_from_dict = mocker.patch.object(async_subtensor.DynamicInfo, "from_dict")
mocked_get_subnet_price = mocker.patch.object(
subtensor, "get_subnet_price", return_value=Balance.from_tao(100.0)
)
mocked_decode = mocker.Mock(return_value={"netuid": netuid})
mocked_runtime_call = mocker.Mock(decode=mocked_decode)
mocker.patch.object(
subtensor.substrate, "runtime_call", return_value=mocked_runtime_call
)

# Call
result = await subtensor.subnet(netuid=netuid)

# Asserts
mocked_determine_block_hash.assert_awaited_once_with(
block=None, block_hash=None, reuse_block=False
)
subtensor.substrate.runtime_call.assert_awaited_once_with(
"SubnetInfoRuntimeApi",
"get_dynamic_info",
params=[netuid],
block_hash=mocked_determine_block_hash.return_value,
)
mocked_get_subnet_price.assert_awaited_once_with(
netuid=netuid,
block=None,
block_hash=mocked_determine_block_hash.return_value,
reuse_block=False,
)
mocked_di_from_dict.assert_called_once_with(
{"netuid": netuid, "price": Balance.from_tao(100.0)}
)
assert result == mocked_di_from_dict.return_value
71 changes: 71 additions & 0 deletions tests/unit_tests/test_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4195,3 +4195,74 @@ def test_get_subnet_prices(subtensor, mocker):
page_size=129, # total number of subnets
)
assert result == expected_prices


def test_all_subnets(subtensor, mocker):
"""Verify that `all_subnets` calls proper methods and returns the correct value."""
# Preps
mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
mocked_di_list_from_dicts = mocker.patch.object(
subtensor_module.DynamicInfo, "list_from_dicts"
)
mocked_get_subnet_prices = mocker.patch.object(
subtensor,
"get_subnet_prices",
return_value={0: Balance.from_tao(1), 1: Balance.from_tao(0.029258617)},
)
mocked_decode = mocker.Mock(return_value=[{"netuid": 0}, {"netuid": 1}])
mocked_runtime_call = mocker.Mock(decode=mocked_decode)
mocker.patch.object(
subtensor.substrate, "runtime_call", return_value=mocked_runtime_call
)

# Call
result = subtensor.all_subnets()

# Asserts
mocked_determine_block_hash.assert_called_once_with(block=None)
subtensor.substrate.runtime_call.assert_called_once_with(
api="SubnetInfoRuntimeApi",
method="get_all_dynamic_info",
block_hash=mocked_determine_block_hash.return_value,
)
mocked_get_subnet_prices.assert_called_once()
mocked_di_list_from_dicts.assert_called_once_with(
[
{"netuid": 0, "price": Balance.from_tao(1)},
{"netuid": 1, "price": Balance.from_tao(0.029258617)},
]
)
assert result == mocked_di_list_from_dicts.return_value


def test_subnet(subtensor, mocker):
"""Verify that `subnet` calls proper methods and returns the correct value."""
# Preps
netuid = 14
mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
mocked_di_from_dict = mocker.patch.object(subtensor_module.DynamicInfo, "from_dict")
mocked_get_subnet_price = mocker.patch.object(
subtensor, "get_subnet_price", return_value=Balance.from_tao(100.0)
)
mocked_decode = mocker.Mock(return_value={"netuid": netuid})
mocked_runtime_call = mocker.Mock(decode=mocked_decode)
mocker.patch.object(
subtensor.substrate, "runtime_call", return_value=mocked_runtime_call
)

# Call
result = subtensor.subnet(netuid=netuid)

# Asserts
subtensor.substrate.runtime_call.assert_called_once_with(
api="SubnetInfoRuntimeApi",
method="get_dynamic_info",
params=[netuid],
block_hash=mocked_determine_block_hash.return_value,
)
mocked_determine_block_hash.assert_called_once_with(block=None)
mocked_get_subnet_price.assert_called_once_with(netuid=netuid, block=None)
mocked_di_from_dict.assert_called_once_with(
{"netuid": netuid, "price": Balance.from_tao(100.0)}
)
assert result == mocked_di_from_dict.return_value