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
70 changes: 68 additions & 2 deletions bittensor/core/async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1322,21 +1322,53 @@ async def get_all_commitments(
)
return result

async def get_all_metagraphs_info(
async def get_all_ema_tao_inflow(
self,
block: Optional[int] = None,
block_hash: Optional[str] = None,
reuse_block: bool = False,
) -> dict[int, tuple[int, Balance]]:
"""
Query EMA TAO flow for all subnets using query_map.

The EMA TAO flow represents the exponential moving average of TAO flowing into or out of a subnet. Negative
values indicate net outflow.

Args:
block: The blockchain block number for the query.
block_hash: The hash of the blockchain block number at which to perform the query.
reuse_block: Whether to reuse the last-used block hash when retrieving info.

Returns:
Dict mapping netuid -> (block_number, Balance).
"""
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
query = await self.substrate.query_map(
module="SubtensorModule",
storage_function="SubnetEmaTaoFlow",
block_hash=block_hash,
)
tao_inflow_ema = {}
async for netuid, (block_updated, tao_bits) in query:
ema_value = int(fixed_to_float(tao_bits))
tao_inflow_ema[netuid] = (block_updated, Balance.from_rao(ema_value))
return tao_inflow_ema

async def get_all_metagraphs_info(
self,
all_mechanisms: bool = False,
block: Optional[int] = None,
block_hash: Optional[str] = None,
reuse_block: bool = False,
) -> Optional[list[MetagraphInfo]]:
"""
Retrieves a list of MetagraphInfo objects for all subnets

Parameters:
all_mechanisms: If True then returns all mechanisms, otherwise only those with index 0 for all subnets.
block: The blockchain block number for the query.
block_hash: The hash of the blockchain block number at which to perform the query.
reuse_block: Whether to reuse the last-used block hash when retrieving info.
all_mechanisms: If True then returns all mechanisms, otherwise only those with index 0 for all subnets.

Returns:
List of MetagraphInfo objects for all existing subnets.
Expand Down Expand Up @@ -2288,6 +2320,40 @@ async def get_existential_deposit(

return Balance.from_rao(getattr(result, "value", 0))

async def get_ema_tao_inflow(
self,
netuid: int,
block: Optional[int] = None,
) -> Optional[tuple[int, Balance]]:
"""
Query EMA TAO flow for all subnets using query_map.

The EMA TAO flow represents the exponential moving average of TAO flowing into or out of a subnet. Negative
values indicate net outflow.

Args:
netuid: The unique identifier of the subnetwork.
block: The block number to retrieve the commitment from.

Returns:
The tuple with block_number, Balance
"""
block_hash = await self.determine_block_hash(block)
query = await self.substrate.query(
module="SubtensorModule",
storage_function="SubnetEmaTaoFlow",
params=[netuid],
block_hash=block_hash,
)

# sn0 doesn't have EmaTaoInflow
if query is None:
return None

block_updated, tao_bits = query.value
ema_value = int(fixed_to_float(tao_bits))
return block_updated, Balance.from_rao(ema_value)

async def get_hotkey_owner(
self,
hotkey_ss58: str,
Expand Down
66 changes: 64 additions & 2 deletions bittensor/core/subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,17 +874,45 @@ def get_all_commitments(
)
return result

def get_all_metagraphs_info(
def get_all_ema_tao_inflow(
self,
block: Optional[int] = None,
) -> dict[int, tuple[int, Balance]]:
"""
Query EMA TAO flow for all subnets using query_map.

The EMA TAO flow represents the exponential moving average of TAO flowing
into or out of a subnet. Negative values indicate net outflow.

Args:
block: The block number to retrieve the commitment from.

Returns:
Dict mapping netuid -> (block_number, Balance).
"""
block_hash = self.determine_block_hash(block)
query = self.substrate.query_map(
module="SubtensorModule",
storage_function="SubnetEmaTaoFlow",
block_hash=block_hash,
)
tao_inflow_ema = {}
for netuid, (block_updated, tao_bits) in query:
ema_value = int(fixed_to_float(tao_bits))
tao_inflow_ema[netuid] = (block_updated, Balance.from_rao(ema_value))
return tao_inflow_ema

def get_all_metagraphs_info(
self,
all_mechanisms: bool = False,
block: Optional[int] = None,
) -> Optional[list[MetagraphInfo]]:
"""
Retrieves a list of MetagraphInfo objects for all subnets

Parameters:
block: The blockchain block number for the query.
all_mechanisms: If True then returns all mechanisms, otherwise only those with index 0 for all subnets.
block: The blockchain block number for the query.

Returns:
List of MetagraphInfo objects for all existing subnets.
Expand Down Expand Up @@ -1598,6 +1626,40 @@ def get_existential_deposit(self, block: Optional[int] = None) -> Optional[Balan

return Balance.from_rao(getattr(result, "value", 0))

def get_ema_tao_inflow(
self,
netuid: int,
block: Optional[int] = None,
) -> Optional[tuple[int, Balance]]:
"""
Query EMA TAO flow for all subnets using query_map.

The EMA TAO flow represents the exponential moving average of TAO flowing into or out of a subnet. Negative
values indicate net outflow.

Args:
netuid: The unique identifier of the subnetwork.
block: The block number to retrieve the commitment from.

Returns:
The tuple with block_number, Balance
"""
block_hash = self.determine_block_hash(block)
query = self.substrate.query(
module="SubtensorModule",
storage_function="SubnetEmaTaoFlow",
params=[netuid],
block_hash=block_hash,
)

# sn0 doesn't have EmaTaoInflow
if query is None:
return None

block_updated, tao_bits = query.value
ema_value = int(fixed_to_float(tao_bits))
return block_updated, Balance.from_rao(ema_value)

def get_hotkey_owner(
self, hotkey_ss58: str, block: Optional[int] = None
) -> Optional[str]:
Expand Down
2 changes: 2 additions & 0 deletions bittensor/extras/subtensor_api/subnets.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]):
self.burned_register = subtensor.burned_register
self.commit_reveal_enabled = subtensor.commit_reveal_enabled
self.difficulty = subtensor.difficulty
self.get_all_ema_tao_inflow = subtensor.get_all_ema_tao_inflow
self.get_all_subnets_info = subtensor.get_all_subnets_info
self.get_all_subnets_netuid = subtensor.get_all_subnets_netuid
self.get_ema_tao_inflow = subtensor.get_ema_tao_inflow
self.get_parents = subtensor.get_parents
self.get_children = subtensor.get_children
self.get_children_pending = subtensor.get_children_pending
Expand Down
2 changes: 2 additions & 0 deletions bittensor/extras/subtensor_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def add_legacy_methods(subtensor: "SubtensorApi"):
subtensor.inner_subtensor.get_admin_freeze_window
)
subtensor.get_all_commitments = subtensor.inner_subtensor.get_all_commitments
subtensor.get_all_ema_tao_inflow = subtensor.inner_subtensor.get_all_ema_tao_inflow
subtensor.get_all_metagraphs_info = (
subtensor.inner_subtensor.get_all_metagraphs_info
)
Expand Down Expand Up @@ -74,6 +75,7 @@ def add_legacy_methods(subtensor: "SubtensorApi"):
subtensor.get_delegate_take = subtensor.inner_subtensor.get_delegate_take
subtensor.get_delegated = subtensor.inner_subtensor.get_delegated
subtensor.get_delegates = subtensor.inner_subtensor.get_delegates
subtensor.get_ema_tao_inflow = subtensor.inner_subtensor.get_ema_tao_inflow
subtensor.get_existential_deposit = (
subtensor.inner_subtensor.get_existential_deposit
)
Expand Down
8 changes: 4 additions & 4 deletions tests/e2e_tests/test_metagraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -821,18 +821,16 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet):

bob_sn = TestSubnet(subtensor)
bob_sn.execute_one(REGISTER_SUBNET(bob_wallet))
print(">>> bob_sn", bob_sn.netuid)

block = subtensor.chain.get_current_block()
metagraph_info = subtensor.metagraphs.get_metagraph_info(
netuid=bob_sn.netuid, block=block
)
print(">>> metagraph_info", metagraph_info)

assert metagraph_info.owner_coldkey == bob_wallet.coldkey.ss58_address
assert metagraph_info.owner_hotkey == bob_wallet.hotkey.ss58_address

metagraph_infos = subtensor.metagraphs.get_all_metagraphs_info(block)
metagraph_infos = subtensor.metagraphs.get_all_metagraphs_info(block=block)

assert len(metagraph_infos) == 4
assert metagraph_infos[-1] == metagraph_info
Expand Down Expand Up @@ -1085,7 +1083,9 @@ async def test_metagraph_info_async(async_subtensor, alice_wallet, bob_wallet):
assert metagraph_info.owner_coldkey == bob_wallet.coldkey.ss58_address
assert metagraph_info.owner_hotkey == bob_wallet.hotkey.ss58_address

metagraph_infos = await async_subtensor.metagraphs.get_all_metagraphs_info(block)
metagraph_infos = await async_subtensor.metagraphs.get_all_metagraphs_info(
block=block
)

assert len(metagraph_infos) == 4
assert metagraph_infos[-1] == metagraph_info
Expand Down
72 changes: 72 additions & 0 deletions tests/unit_tests/test_async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4885,3 +4885,75 @@ async def test_set_root_claim_type(mocker, subtensor):
wait_for_finalization=True,
)
assert response == mocked_set_root_claim_type_extrinsic.return_value


@pytest.mark.asyncio
async def test_get_all_ema_tao_inflow(subtensor, mocker):
"""Test get_all_ema_tao_inflow returns correct values."""
# Preps
fake_block = 123
fake_netuid = 1
fake_block_updated = 100
fake_tao_bits = {"bits": 6520190}

mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
fake_query_result = [(fake_netuid, (fake_block_updated, fake_tao_bits))]

fake_result = mocker.AsyncMock(autospec=list)
fake_result.__aiter__.return_value = iter(fake_query_result)

mocked_query_map = mocker.patch.object(
subtensor.substrate,
"query_map",
return_value=fake_result,
)
mocked_fixed_to_float = mocker.patch.object(
async_subtensor, "fixed_to_float", return_value=1000000
)

# Call
result = await subtensor.get_all_ema_tao_inflow(block=fake_block)

# Asserts
mocked_determine_block_hash.assert_awaited_once_with(fake_block, None, False)
mocked_query_map.assert_awaited_once_with(
module="SubtensorModule",
storage_function="SubnetEmaTaoFlow",
block_hash=mocked_determine_block_hash.return_value,
)
mocked_fixed_to_float.assert_called_once_with(fake_tao_bits)
assert result == {fake_netuid: (fake_block_updated, Balance.from_rao(1000000))}


@pytest.mark.asyncio
async def test_get_ema_tao_inflow(subtensor, mocker):
"""Test get_ema_tao_inflow returns correct values."""
# Preps
fake_block = 123
fake_netuid = 1
fake_block_updated = 100
fake_tao_bits = {"bits": 6520190}

mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
mocked_query = mocker.patch.object(
subtensor.substrate,
"query",
return_value=mocker.Mock(value=(fake_block_updated, fake_tao_bits)),
)
mocked_fixed_to_float = mocker.patch.object(
async_subtensor, "fixed_to_float", return_value=1000000
)

# Call
result = await subtensor.get_ema_tao_inflow(netuid=fake_netuid, block=fake_block)

# Asserts
mocked_determine_block_hash.assert_awaited_once_with(fake_block)
mocked_query.assert_awaited_once_with(
module="SubtensorModule",
storage_function="SubnetEmaTaoFlow",
params=[fake_netuid],
block_hash=mocked_determine_block_hash.return_value,
)
mocked_fixed_to_float.assert_called_once_with(fake_tao_bits)
assert result == (fake_block_updated, Balance.from_rao(1000000))
64 changes: 64 additions & 0 deletions tests/unit_tests/test_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4968,3 +4968,67 @@ def test_set_root_claim_type(mocker, subtensor):
wait_for_finalization=True,
)
assert response == mocked_set_root_claim_type_extrinsic.return_value


def test_get_all_ema_tao_inflow(subtensor, mocker):
"""Test get_all_ema_tao_inflow returns correct values."""
# Preps
fake_block = 123
fake_netuid = 1
fake_block_updated = 100
fake_tao_bits = {"bits": 6520190}

mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
fake_query_result = [(fake_netuid, (fake_block_updated, fake_tao_bits))]
mock_query_map = mocker.patch.object(
subtensor.substrate, "query_map", return_value=fake_query_result
)
mocked_fixed_to_float = mocker.patch.object(
subtensor_module, "fixed_to_float", return_value=1000000
)

# Call
result = subtensor.get_all_ema_tao_inflow(block=fake_block)

# Asserts
mocked_determine_block_hash.assert_called_once_with(fake_block)
mock_query_map.assert_called_once_with(
module="SubtensorModule",
storage_function="SubnetEmaTaoFlow",
block_hash=mocked_determine_block_hash.return_value,
)
mocked_fixed_to_float.assert_called_once_with(fake_tao_bits)
assert result == {fake_netuid: (fake_block_updated, Balance.from_rao(1000000))}


def test_get_ema_tao_inflow(subtensor, mocker):
"""Test get_ema_tao_inflow returns correct values."""
# Preps
fake_block = 123
fake_netuid = 1
fake_block_updated = 100
fake_tao_bits = {"bits": 6520190}

mocked_determine_block_hash = mocker.patch.object(subtensor, "determine_block_hash")
mocked_query = mocker.patch.object(
subtensor.substrate,
"query",
return_value=mocker.Mock(value=(fake_block_updated, fake_tao_bits)),
)
mocked_fixed_to_float = mocker.patch.object(
subtensor_module, "fixed_to_float", return_value=1000000
)

# Call
result = subtensor.get_ema_tao_inflow(netuid=fake_netuid, block=fake_block)

# Asserts
mocked_determine_block_hash.assert_called_once_with(fake_block)
mocked_query.assert_called_once_with(
module="SubtensorModule",
storage_function="SubnetEmaTaoFlow",
params=[fake_netuid],
block_hash=mocked_determine_block_hash.return_value,
)
mocked_fixed_to_float.assert_called_once_with(fake_tao_bits)
assert result == (fake_block_updated, Balance.from_rao(1000000))