diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 84e9c87d69..c39fb4bae6 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -65,8 +65,7 @@ ) from bittensor.core.metagraph import AsyncMetagraph from bittensor.core.settings import version_as_int, TYPE_REGISTRY, DELEGATES_DETAILS_URL -from bittensor.core.types import ParamWithTypes -from bittensor.core.types import SubtensorMixin +from bittensor.core.types import ParamWithTypes, SubtensorMixin from bittensor.utils import ( decode_hex_identity_dict, format_error_message, @@ -563,12 +562,16 @@ async def all_subnets( block_number: Optional[int] = None, block_hash: Optional[str] = None, reuse_block: bool = False, - ) -> Optional[list["DynamicInfo"]]: + ) -> Optional[list[DynamicInfo]]: """ Retrieves the subnet information for all subnets in the network. Args: - block_number (Optional[int]): The block number to query the subnet information from. + block_number (Optional[int]): The block number to query the subnet information from. Do not specify if using + block_hash or reuse_block + block_hash: The hash of the blockchain block number for the query. Do not specify if using reuse_block or + block. + reuse_block: Whether to reuse the last-used blockchain block hash. Do not set if using block_hash or block. Returns: Optional[DynamicInfo]: A list of DynamicInfo objects, each containing detailed information about a subnet. @@ -577,6 +580,8 @@ async def all_subnets( block_hash = await self.determine_block_hash( block_number, block_hash, reuse_block ) + if not block_hash and reuse_block: + block_hash = self.substrate.last_block_hash query = await self.substrate.runtime_call( "SubnetInfoRuntimeApi", "get_all_dynamic_info", @@ -799,7 +804,7 @@ async def get_balance( block: Optional[int] = None, block_hash: Optional[str] = None, reuse_block: bool = False, - ) -> "Balance": + ) -> Balance: """ Retrieves the balance for given coldkey. @@ -1321,9 +1326,29 @@ async def get_minimum_required_stake(self): return Balance.from_rao(getattr(result, "value", 0)) async def get_metagraph_info( - self, netuid: int, block: Optional[int] = None - ) -> Optional[MetagraphInfo]: - block_hash = await self.get_block_hash(block) + self, + netuid: int, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, + ) -> MetagraphInfo: + """ + Retrieves the MetagraphInfo dataclass from the node for a single subnet (netuid) + + Arguments: + netuid: The NetUID of the subnet. + block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or + reuse_block + block_hash: The hash of blockchain block number for the query. Do not specify if using + block or reuse_block + reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block. + + Returns: + MetagraphInfo dataclass + """ + block_hash = await self.determine_block_hash(block, block_hash, reuse_block) + if not block_hash and reuse_block: + block_hash = self.substrate.last_block_hash query = await self.substrate.runtime_call( "SubnetInfoRuntimeApi", @@ -1335,10 +1360,27 @@ async def get_metagraph_info( return MetagraphInfo.from_vec_u8(metagraph_bytes) async def get_all_metagraphs_info( - self, block: Optional[int] = None + self, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, ) -> list[MetagraphInfo]: - block_hash = await self.get_block_hash(block) + """ + Retrieves a list of MetagraphInfo objects for all subnets + + Arguments: + block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or + reuse_block + block_hash (Optional[str]): The hash of blockchain block number for the query. Do not specify if using + block or reuse_block + reuse_block (bool): Whether to reuse the last-used block hash. Do not set if using block_hash or block. + Returns: + MetagraphInfo dataclass + """ + block_hash = await self.determine_block_hash(block, block_hash.reuse_block) + if not block_hash and reuse_block: + block_hash = self.substrate.last_block_hash query = await self.substrate.runtime_call( "SubnetInfoRuntimeApi", "get_all_metagraphs", @@ -1492,9 +1534,11 @@ async def get_stake( Args: hotkey_ss58 (str): The SS58 address of the hotkey. coldkey_ss58 (str): The SS58 address of the coldkey. - netuid (Optional[int]): The subnet ID to filter by. If provided, only returns stake for this specific subnet. + netuid (Optional[int]): The subnet ID to filter by. If provided, only returns stake for this specific + subnet. block (Optional[int]): The block number at which to query the stake information. - block_hash (Optional[str]): The hash of the block to retrieve the stake from. Do not specify if using block or reuse_block + block_hash (Optional[str]): The hash of the block to retrieve the stake from. Do not specify if using block + or reuse_block reuse_block (bool): Whether to use the last-used block. Do not set if using block_hash or block. Returns: @@ -1569,18 +1613,15 @@ async def get_stake_for_coldkey( hex_bytes_result = await self.query_runtime_api( runtime_api="StakeInfoRuntimeApi", method="get_stake_info_for_coldkey", - params=[encoded_coldkey], # type: ignore + params=[encoded_coldkey], block_hash=block_hash, + reuse_block=reuse_block, ) if hex_bytes_result is None: return [] - try: - bytes_result = bytes.fromhex(hex_bytes_result[2:]) - except ValueError: - bytes_result = bytes.fromhex(hex_bytes_result) - stakes = StakeInfo.list_from_vec_u8(bytes_result) # type: ignore + stakes = StakeInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result)) # type: ignore return [stake for stake in stakes if stake.stake > 0] async def get_stake_info_for_coldkey( @@ -1898,8 +1939,8 @@ async def get_total_subnets( return getattr(result, "value", None) async def get_transfer_fee( - self, wallet: "Wallet", dest: str, value: Union["Balance", float, int] - ) -> "Balance": + self, wallet: "Wallet", dest: str, value: Union[Balance, float, int] + ) -> Balance: """ Calculates the transaction fee for transferring tokens from a wallet to a specified destination address. This function simulates the transfer to estimate the associated cost, taking into account the current network @@ -2501,7 +2542,7 @@ async def recycle( block: Optional[int] = None, block_hash: Optional[str] = None, reuse_block: bool = False, - ) -> Optional["Balance"]: + ) -> Optional[Balance]: """ Retrieves the 'Burn' hyperparameter for a specified subnet. The 'Burn' parameter represents the amount of Tao that is effectively recycled within the Bittensor network. @@ -2530,7 +2571,7 @@ async def recycle( async def subnet( self, netuid: int, - block_number: int = None, + block: int = None, block_hash: Optional[str] = None, reuse_block: bool = False, ) -> Optional[DynamicInfo]: @@ -2539,21 +2580,16 @@ async def subnet( Args: netuid (int): The unique identifier of the subnet. - block_number (Optional[int]): The block number to get the subnets at. + block (Optional[int]): The block number to get the subnets at. + block_hash (str): The hash of the blockchain block number for the query. + reuse_block (bool): Whether to reuse the last-used blockchain block hash. Returns: Optional[DynamicInfo]: A DynamicInfo object, containing detailed information about a subnet. - - This function can be called in two ways: - 1. As a context manager: - async with sub: - subnet = await sub.subnet(1) - 2. Directly: - subnet = await sub.subnet(1) """ - block_hash = await self.determine_block_hash( - block_number, block_hash, reuse_block - ) + block_hash = await self.determine_block_hash(block, block_hash, 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", @@ -2681,6 +2717,41 @@ async def tx_rate_limit( ) return getattr(result, "value", None) + async def wait_for_block(self, block: Optional[int] = None): + """ + Waits until a specific block is reached on the chain. If no block is specified, + waits for the next block. + + Args: + block (Optional[int]): The block number to wait for. If None, waits for next block. + + Returns: + bool: True if the target block was reached, False if timeout occurred. + + Example: + >>> await subtensor.wait_for_block() # Waits for next block + >>> await subtensor.wait_for_block(block=1234) # Waits for specific block + """ + + async def handler(block_data: dict): + logging.debug( + f'reached block {block_data["header"]["number"]}. Waiting for block {target_block}' + ) + if block_data["header"]["number"] >= target_block: + return True + + current_block = await self.substrate.get_block() + current_block_hash = current_block.get("header", {}).get("hash") + if block is not None: + target_block = block + else: + target_block = current_block["header"]["number"] + 1 + + await self.substrate._get_block_handler( + current_block_hash, header_only=True, subscription_handler=handler + ) + return True + async def weights( self, netuid: int, @@ -2817,6 +2888,7 @@ async def add_stake( Args: wallet (bittensor_wallet.Wallet): The wallet to be used for staking. hotkey_ss58 (Optional[str]): The ``SS58`` address of the hotkey associated with the neuron. + netuid: subnet UID amount (Balance): The amount of TAO to stake. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. @@ -2853,6 +2925,7 @@ async def add_stake_multiple( Args: wallet (bittensor_wallet.Wallet): The wallet used for staking. hotkey_ss58s (list[str]): List of ``SS58`` addresses of hotkeys to stake to. + netuids: list of subnet UIDs amounts (list[Balance]): Corresponding amounts of TAO to stake for each hotkey. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. @@ -3387,7 +3460,7 @@ async def swap_stake( hotkey_ss58: str, origin_netuid: int, destination_netuid: int, - amount: Union["Balance", float], + amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, ) -> bool: @@ -3465,7 +3538,7 @@ async def transfer( self, wallet: "Wallet", dest: str, - amount: "Balance", + amount: Balance, transfer_all: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index 4c3272d3c9..9d103d9f6a 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -1,3 +1,4 @@ +import asyncio from typing import TYPE_CHECKING from bittensor.utils.balance import Balance @@ -8,6 +9,33 @@ from bittensor.core.async_subtensor import AsyncSubtensor +async def _get_stake_in_origin_and_dest( + subtensor: "AsyncSubtensor", + origin_hotkey_ss58: str, + destination_hotkey_ss58: str, + origin_coldkey_ss58: str, + destination_coldkey_ss58: str, + origin_netuid: int, + destination_netuid: int, +) -> tuple[Balance, Balance]: + block_hash = await subtensor.substrate.get_chain_head() + stake_in_origin, stake_in_destination = await asyncio.gather( + subtensor.get_stake( + coldkey_ss58=origin_coldkey_ss58, + hotkey_ss58=origin_hotkey_ss58, + netuid=origin_netuid, + block_hash=block_hash, + ), + subtensor.get_stake( + coldkey_ss58=destination_coldkey_ss58, + hotkey_ss58=destination_hotkey_ss58, + netuid=destination_netuid, + block_hash=block_hash, + ), + ) + return stake_in_origin, stake_in_destination + + async def transfer_stake_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", @@ -42,31 +70,34 @@ async def transfer_stake_extrinsic( hotkey_owner = await subtensor.get_hotkey_owner(hotkey_ss58) if hotkey_owner != wallet.coldkeypub.ss58_address: logging.error( - f":cross_mark: [red]Failed[/red]: Hotkey: {hotkey_ss58} does not belong to the origin coldkey owner: {wallet.coldkeypub.ss58_address}" + f":cross_mark: [red]Failed[/red]: Hotkey: {hotkey_ss58} does not belong to the origin coldkey owner: " + f"{wallet.coldkeypub.ss58_address}" ) return False # Check sufficient stake - stake_in_origin = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=origin_netuid, - ) - stake_in_destination = await subtensor.get_stake( - coldkey_ss58=destination_coldkey_ss58, - hotkey_ss58=hotkey_ss58, - netuid=destination_netuid, + stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=destination_coldkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, ) if stake_in_origin < amount: logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}" + f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. " + f"Stake: {stake_in_origin}, amount: {amount}" ) return False try: logging.info( - f"Transferring stake from coldkey [blue]{wallet.coldkeypub.ss58_address}[/blue] to coldkey [blue]{destination_coldkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid [yellow]{destination_netuid}[/yellow]" + f"Transferring stake from coldkey [blue]{wallet.coldkeypub.ss58_address}[/blue] to coldkey " + f"[blue]{destination_coldkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " + f"[yellow]{destination_netuid}[/yellow]" ) call = await subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -94,18 +125,14 @@ async def transfer_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") # Get updated stakes - block = await subtensor.get_current_block() - origin_stake = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=origin_netuid, - block=block, - ) - dest_stake = await subtensor.get_stake( - coldkey_ss58=destination_coldkey_ss58, - hotkey_ss58=hotkey_ss58, - netuid=destination_netuid, - block=block, + origin_stake, dest_stake = await _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=destination_coldkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, ) logging.info( f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" @@ -155,31 +182,33 @@ async def swap_stake_extrinsic( hotkey_owner = await subtensor.get_hotkey_owner(hotkey_ss58) if hotkey_owner != wallet.coldkeypub.ss58_address: logging.error( - f":cross_mark: [red]Failed[/red]: Hotkey: {hotkey_ss58} does not belong to the origin coldkey owner: {wallet.coldkeypub.ss58_address}" + f":cross_mark: [red]Failed[/red]: Hotkey: {hotkey_ss58} does not belong to the origin coldkey owner: " + f"{wallet.coldkeypub.ss58_address}" ) return False # Check sufficient stake - stake_in_origin = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=origin_netuid, - ) - stake_in_destination = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=destination_netuid, + stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, ) if stake_in_origin < amount: logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}" + f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. " + f"Stake: {stake_in_origin}, amount: {amount}" ) return False try: logging.info( f"Swapping stake for hotkey [blue]{hotkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid [yellow]{destination_netuid}[/yellow]" + f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " + f"[yellow]{destination_netuid}[/yellow]" ) call = await subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -206,18 +235,14 @@ async def swap_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") # Get updated stakes - block = await subtensor.get_current_block() - origin_stake = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=origin_netuid, - block=block, - ) - dest_stake = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=destination_netuid, - block=block, + origin_stake, dest_stake = await _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, ) logging.info( f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" @@ -269,31 +294,33 @@ async def move_stake_extrinsic( origin_owner = await subtensor.get_hotkey_owner(origin_hotkey) if origin_owner != wallet.coldkeypub.ss58_address: logging.error( - f":cross_mark: [red]Failed[/red]: Origin hotkey: {origin_hotkey} does not belong to the coldkey owner: {wallet.coldkeypub.ss58_address}" + f":cross_mark: [red]Failed[/red]: Origin hotkey: {origin_hotkey} does not belong to the coldkey owner: " + f"{wallet.coldkeypub.ss58_address}" ) return False # Check sufficient stake - stake_in_origin = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=origin_hotkey, - netuid=origin_netuid, - ) - stake_in_destination = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=destination_hotkey, - netuid=destination_netuid, + stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=origin_hotkey, + destination_hotkey_ss58=destination_hotkey, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, ) if stake_in_origin < amount: logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {origin_hotkey}. Stake: {stake_in_origin}, amount: {amount}" + f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {origin_hotkey}. " + f"Stake: {stake_in_origin}, amount: {amount}" ) return False try: logging.info( f"Moving stake from hotkey [blue]{origin_hotkey}[/blue] to hotkey [blue]{destination_hotkey}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid [yellow]{destination_netuid}[/yellow]" + f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " + f"[yellow]{destination_netuid}[/yellow]" ) call = await subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -321,18 +348,14 @@ async def move_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") # Get updated stakes - block = await subtensor.get_current_block() - origin_stake = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=origin_hotkey, - netuid=origin_netuid, - block=block, - ) - dest_stake = await subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=destination_hotkey, - netuid=destination_netuid, - block=block, + origin_stake, dest_stake = await _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=origin_hotkey, + destination_hotkey_ss58=destination_hotkey, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, ) logging.info( f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index 00bc243cc9..7d1a5de978 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -196,7 +196,7 @@ async def add_stake_multiple_extrinsic( if amounts is not None and len(amounts) != len(hotkey_ss58s): raise ValueError("amounts must be a list of the same length as hotkey_ss58s") - if netuids is not None and len(netuids) != len(hotkey_ss58s): + if len(netuids) != len(hotkey_ss58s): raise ValueError("netuids must be a list of the same length as hotkey_ss58s") new_amounts: Sequence[Optional[Balance]] @@ -236,7 +236,7 @@ async def add_stake_multiple_extrinsic( old_balance = await subtensor.get_balance( wallet.coldkeypub.ss58_address, block_hash=block_hash ) - inital_balance = old_balance + initial_balance = old_balance if total_staking_rao == 0: # Staking all to the first wallet. @@ -279,7 +279,8 @@ async def add_stake_multiple_extrinsic( try: logging.info( - f"Staking [blue]{staking_balance}[/blue] to hotkey: [magenta]{hotkey_ss58}[/magenta] on netuid: [blue]{netuid}[/blue]" + f"Staking [blue]{staking_balance}[/blue] to hotkey: [magenta]{hotkey_ss58}[/magenta] on netuid: " + f"[blue]{netuid}[/blue]" ) call = await subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -335,7 +336,8 @@ async def add_stake_multiple_extrinsic( ), ) logging.info( - f"Stake ({hotkey_ss58}) on netuid {netuid}: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" + f"Stake ({hotkey_ss58}) on netuid {netuid}: [blue]{old_stake}[/blue] :arrow_right: " + f"[green]{new_stake}[/green]" ) logging.info( f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" @@ -366,7 +368,7 @@ async def add_stake_multiple_extrinsic( ) new_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address) logging.info( - f"Balance: [blue]{inital_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" + f"Balance: [blue]{initial_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) return True diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 352f8441eb..0a87a2d03a 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -80,7 +80,8 @@ async def unstake_extrinsic( try: logging.info( - f"Unstaking [blue]{unstaking_balance}[/blue] from hotkey: [magenta]{hotkey_ss58}[/magenta] on netuid: [blue]{netuid}[/blue]" + f"Unstaking [blue]{unstaking_balance}[/blue] from hotkey: [magenta]{hotkey_ss58}[/magenta] on netuid: " + f"[blue]{netuid}[/blue]" ) call = await subtensor.substrate.compose_call( @@ -155,6 +156,7 @@ async def unstake_multiple_extrinsic( subtensor (bittensor.core.subtensor.Subtensor): Subtensor instance. wallet (bittensor_wallet.Wallet): The wallet with the coldkey to unstake to. hotkey_ss58s (List[str]): List of hotkeys to unstake from. + netuids (List[int]): List of netuids to unstake from. amounts (List[Union[Balance, float]]): List of amounts to unstake. If ``None``, unstake all. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning ``True``, or returns ``False`` if the extrinsic fails to enter the block within the timeout. @@ -239,7 +241,8 @@ async def unstake_multiple_extrinsic( try: logging.info( - f"Unstaking [blue]{unstaking_balance}[/blue] from hotkey: [magenta]{hotkey_ss58}[/magenta] on netuid: [blue]{netuid}[/blue]" + f"Unstaking [blue]{unstaking_balance}[/blue] from hotkey: [magenta]{hotkey_ss58}[/magenta] on netuid: " + f"[blue]{netuid}[/blue]" ) call = await subtensor.substrate.compose_call( call_module="SubtensorModule", diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index a7ecdf01fe..38ac70da01 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -8,6 +8,31 @@ from bittensor.core.subtensor import Subtensor +def _get_stake_in_origin_and_dest( + subtensor: "Subtensor", + origin_hotkey_ss58: str, + destination_hotkey_ss58: str, + origin_coldkey_ss58: str, + destination_coldkey_ss58: str, + origin_netuid: int, + destination_netuid: int, +) -> tuple[Balance, Balance]: + block = subtensor.get_current_block() + stake_in_origin = subtensor.get_stake( + coldkey_ss58=origin_coldkey_ss58, + hotkey_ss58=origin_hotkey_ss58, + netuid=origin_netuid, + block=block, + ) + stake_in_destination = subtensor.get_stake( + coldkey_ss58=destination_coldkey_ss58, + hotkey_ss58=destination_hotkey_ss58, + netuid=destination_netuid, + block=block, + ) + return stake_in_origin, stake_in_destination + + def transfer_stake_extrinsic( subtensor: "Subtensor", wallet: "Wallet", @@ -42,31 +67,34 @@ def transfer_stake_extrinsic( hotkey_owner = subtensor.get_hotkey_owner(hotkey_ss58) if hotkey_owner != wallet.coldkeypub.ss58_address: logging.error( - f":cross_mark: [red]Failed[/red]: Hotkey: {hotkey_ss58} does not belong to the origin coldkey owner: {wallet.coldkeypub.ss58_address}" + f":cross_mark: [red]Failed[/red]: Hotkey: {hotkey_ss58} does not belong to the origin coldkey owner: " + f"{wallet.coldkeypub.ss58_address}" ) return False # Check sufficient stake - stake_in_origin = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=origin_netuid, - ) - stake_in_destination = subtensor.get_stake( - coldkey_ss58=destination_coldkey_ss58, - hotkey_ss58=hotkey_ss58, - netuid=destination_netuid, + stake_in_origin, stake_in_destination = _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=destination_coldkey_ss58, ) if stake_in_origin < amount: logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}" + f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. " + f"Stake: {stake_in_origin}, amount: {amount}" ) return False try: logging.info( - f"Transferring stake from coldkey [blue]{wallet.coldkeypub.ss58_address}[/blue] to coldkey [blue]{destination_coldkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid [yellow]{destination_netuid}[/yellow]" + f"Transferring stake from coldkey [blue]{wallet.coldkeypub.ss58_address}[/blue] to coldkey [" + f"blue]{destination_coldkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " + f"[yellow]{destination_netuid}[/yellow]" ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -94,18 +122,14 @@ def transfer_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") # Get updated stakes - block = subtensor.get_current_block() - origin_stake = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=origin_netuid, - block=block, - ) - dest_stake = subtensor.get_stake( - coldkey_ss58=destination_coldkey_ss58, - hotkey_ss58=hotkey_ss58, - netuid=destination_netuid, - block=block, + origin_stake, dest_stake = _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=destination_coldkey_ss58, ) logging.info( f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" @@ -156,31 +180,33 @@ def swap_stake_extrinsic( hotkey_owner = subtensor.get_hotkey_owner(hotkey_ss58) if hotkey_owner != wallet.coldkeypub.ss58_address: logging.error( - f":cross_mark: [red]Failed[/red]: Hotkey: {hotkey_ss58} does not belong to the origin coldkey owner: {wallet.coldkeypub.ss58_address}" + f":cross_mark: [red]Failed[/red]: Hotkey: {hotkey_ss58} does not belong to the origin coldkey owner: " + f"{wallet.coldkeypub.ss58_address}" ) return False # Check sufficient stake - stake_in_origin = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=origin_netuid, - ) - stake_in_destination = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=destination_netuid, + stake_in_origin, stake_in_destination = _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, ) if stake_in_origin < amount: logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}" + f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. " + f"Stake: {stake_in_origin}, amount: {amount}" ) return False try: logging.info( f"Swapping stake for hotkey [blue]{hotkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid [yellow]{destination_netuid}[/yellow]" + f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " + f"[yellow]{destination_netuid}[/yellow]" ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -207,18 +233,14 @@ def swap_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") # Get updated stakes - block = subtensor.get_current_block() - origin_stake = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=origin_netuid, - block=block, - ) - dest_stake = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=destination_netuid, - block=block, + origin_stake, dest_stake = _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, ) logging.info( f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" @@ -276,15 +298,14 @@ def move_stake_extrinsic( return False # Check sufficient stake - stake_in_origin = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=origin_hotkey, - netuid=origin_netuid, - ) - stake_in_destination = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=destination_hotkey, - netuid=destination_netuid, + stake_in_origin, stake_in_destination = _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=origin_hotkey, + destination_hotkey_ss58=destination_hotkey, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, ) if stake_in_origin < amount: logging.error( @@ -323,18 +344,14 @@ def move_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") # Get updated stakes - block = subtensor.get_current_block() - origin_stake = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=origin_hotkey, - netuid=origin_netuid, - block=block, - ) - dest_stake = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=destination_hotkey, - netuid=destination_netuid, - block=block, + origin_stake, dest_stake = _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=origin_hotkey, + destination_hotkey_ss58=destination_hotkey, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, ) logging.info( f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index 5257fc347d..128be8750c 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -1,4 +1,4 @@ -from typing import Union, TYPE_CHECKING +from typing import TYPE_CHECKING from bittensor.core.settings import NETWORK_EXPLORER_MAP from bittensor.utils.balance import Balance @@ -19,7 +19,7 @@ def _do_transfer( subtensor: "Subtensor", wallet: "Wallet", destination: str, - amount: "Balance", + amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, ) -> tuple[bool, str, str]: @@ -68,7 +68,7 @@ def transfer_extrinsic( subtensor: "Subtensor", wallet: "Wallet", dest: str, - amount: Union["Balance", float], + amount: Balance, transfer_all: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 5e2990c392..7cc2e3c4e0 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -118,18 +118,14 @@ def get_old_stakes( Returns: list[Balance]: A list of Balances, each representing the stake for a given hotkey and netuid. """ - old_stakes = [] - for hotkey_ss58, netuid in zip(hotkey_ss58s, netuids): - stake = next( - ( - stake.stake - for stake in all_stakes - if stake.hotkey_ss58 == hotkey_ss58 - and stake.coldkey_ss58 == wallet.coldkeypub.ss58_address - and stake.netuid == netuid - ), + stake_lookup = { + (stake.hotkey_ss58, stake.coldkey_ss58, stake.netuid): stake.stake + for stake in all_stakes + } + return [ + stake_lookup.get( + (hotkey_ss58, wallet.coldkeypub.ss58_address, netuid), Balance.from_tao(0), # Default to 0 balance if no match found ) - old_stakes.append(stake) - - return old_stakes + for hotkey_ss58, netuid in zip(hotkey_ss58s, netuids) + ] diff --git a/bittensor/core/metagraph.py b/bittensor/core/metagraph.py index 1a78b10065..9874d02a08 100644 --- a/bittensor/core/metagraph.py +++ b/bittensor/core/metagraph.py @@ -14,6 +14,7 @@ from bittensor.core import settings from bittensor.core.chain_data import AxonInfo, SubnetState +from bittensor.utils import hex_to_bytes from bittensor.utils.btlogging import logging from bittensor.utils.registration import torch, use_torch from bittensor.utils.weight_utils import ( @@ -1494,7 +1495,7 @@ async def _get_all_stakes_from_chain( """Fills in the stake associated attributes of a class instance from a chain response.""" try: if not subtensor: - subtensor = self._initialize_subtensor(subtensor=subtensor) + subtensor = await self._initialize_subtensor(subtensor=subtensor) hex_bytes_result = await subtensor.query_runtime_api( runtime_api="SubnetInfoRuntimeApi", @@ -1508,12 +1509,9 @@ async def _get_all_stakes_from_chain( ) return [] - if hex_bytes_result.startswith("0x"): - bytes_result = bytes.fromhex(hex_bytes_result[2:]) - else: - bytes_result = bytes.fromhex(hex_bytes_result) - - subnet_state: "SubnetState" = SubnetState.from_vec_u8(bytes_result) + subnet_state: "SubnetState" = SubnetState.from_vec_u8( + hex_to_bytes(hex_bytes_result) + ) if self.netuid == 0: self.total_stake = self.stake = self.tao_stake = self.alpha_stake = ( subnet_state.tao_stake @@ -1797,12 +1795,9 @@ def _get_all_stakes_from_chain(self, subtensor: Optional["Subtensor"] = None): ) return [] - if hex_bytes_result.startswith("0x"): - bytes_result = bytes.fromhex(hex_bytes_result[2:]) - else: - bytes_result = bytes.fromhex(hex_bytes_result) - - subnet_state: "SubnetState" = SubnetState.from_vec_u8(bytes_result) + subnet_state: "SubnetState" = SubnetState.from_vec_u8( + hex_to_bytes(hex_bytes_result) + ) if self.netuid == 0: self.total_stake = self.stake = self.tao_stake = self.alpha_stake = ( subnet_state.tao_stake diff --git a/bittensor/core/settings.py b/bittensor/core/settings.py index 1b78945dd0..2853886df2 100644 --- a/bittensor/core/settings.py +++ b/bittensor/core/settings.py @@ -46,6 +46,7 @@ RAO_ENTRYPOINT: NETWORKS[5], } +# TODO must be changed before 9.0 mainnet release DEFAULT_NETWORK = NETWORKS[1] DEFAULT_ENDPOINT = NETWORK_MAP[DEFAULT_NETWORK] diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index f799a582e4..8d674b7719 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1,4 +1,3 @@ -import time import copy from functools import lru_cache from typing import TYPE_CHECKING, Any, Iterable, Optional, Union, cast @@ -374,20 +373,18 @@ def state_call( def block(self) -> int: return self.get_current_block() - def all_subnets( - self, block_number: Optional[int] = None - ) -> Optional[list["DynamicInfo"]]: + def all_subnets(self, block: Optional[int] = None) -> Optional[list["DynamicInfo"]]: """ Retrieves the subnet information for all subnets in the network. Args: - block_number (Optional[int]): The block number to query the subnet information from. + block (Optional[int]): The block number to query the subnet information from. Returns: Optional[DynamicInfo]: A list of DynamicInfo objects, each containing detailed information about a subnet. """ - block_hash = self.get_block_hash(block_number) if block_number else None + block_hash = self.determine_block_hash(block) query = self.substrate.runtime_call( "SubnetInfoRuntimeApi", "get_all_dynamic_info", @@ -552,7 +549,7 @@ def get_all_subnets_info(self, block: Optional[int] = None) -> list["SubnetInfo" else: return SubnetInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result)) - def get_balance(self, address: str, block: Optional[int] = None) -> "Balance": + def get_balance(self, address: str, block: Optional[int] = None) -> Balance: """ Retrieves the balance for given coldkey. @@ -577,7 +574,7 @@ def get_balances( self, *addresses: str, block: Optional[int] = None, - ) -> dict[str, "Balance"]: + ) -> dict[str, Balance]: """ Retrieves the balance for given coldkey(s) @@ -908,7 +905,7 @@ def get_delegate_take( def get_delegated( self, coldkey_ss58: str, block: Optional[int] = None - ) -> list[tuple["DelegateInfo", "Balance"]]: + ) -> list[tuple["DelegateInfo", Balance]]: """ Retrieves a list of delegates and their associated stakes for a given coldkey. This function identifies the delegates that a specific account has staked tokens on. @@ -957,9 +954,7 @@ def get_delegates(self, block: Optional[int] = None) -> list["DelegateInfo"]: else: return [] - def get_existential_deposit( - self, block: Optional[int] = None - ) -> Optional["Balance"]: + def get_existential_deposit(self, block: Optional[int] = None) -> Optional[Balance]: """ Retrieves the existential deposit amount for the Bittensor blockchain. The existential deposit is the minimum amount of TAO required for an account to exist on the blockchain. @@ -1015,7 +1010,7 @@ def get_hotkey_owner( hotkey_owner = val if exists else None return hotkey_owner - def get_minimum_required_stake(self) -> "Balance": + def get_minimum_required_stake(self) -> Balance: """ Returns the minimum required stake for nominators in the Subtensor network. This method retries the substrate call up to three times with exponential backoff in case of failures. @@ -1035,34 +1030,26 @@ def get_minimum_required_stake(self) -> "Balance": def get_metagraph_info( self, netuid: int, block: Optional[int] = None ) -> Optional[MetagraphInfo]: - if block is not None: - block_hash = self.get_block_hash(block) - else: - block_hash = None - + block_hash = self.determine_block_hash(block) query = self.substrate.runtime_call( "SubnetInfoRuntimeApi", "get_metagraph", params=[netuid], block_hash=block_hash, ) - metagraph_bytes = bytes.fromhex(query.decode()[2:]) + metagraph_bytes = hex_to_bytes(query.decode()) return MetagraphInfo.from_vec_u8(metagraph_bytes) def get_all_metagraphs_info( self, block: Optional[int] = None ) -> list[MetagraphInfo]: - if block is not None: - block_hash = self.get_block_hash(block) - else: - block_hash = None - + block_hash = self.determine_block_hash(block) query = self.substrate.runtime_call( "SubnetInfoRuntimeApi", "get_all_metagraphs", block_hash=block_hash, ) - metagraphs_bytes = bytes.fromhex(query.decode()[2:]) + metagraphs_bytes = hex_to_bytes(query.decode()) return MetagraphInfo.list_from_vec_u8(metagraphs_bytes) def get_netuids_for_hotkey( @@ -1219,7 +1206,7 @@ def get_stake( def get_stake_for_coldkey( self, coldkey_ss58: str, block: Optional[int] = None - ) -> Optional[list["StakeInfo"]]: + ) -> list["StakeInfo"]: """ Retrieves the stake information for a given coldkey. @@ -1240,12 +1227,7 @@ def get_stake_for_coldkey( if hex_bytes_result is None: return [] - try: - bytes_result = bytes.fromhex(hex_bytes_result[2:]) - except ValueError: - bytes_result = bytes.fromhex(hex_bytes_result) - - stakes = StakeInfo.list_from_vec_u8(bytes_result) # type: ignore + stakes = StakeInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result)) # type: ignore return [stake for stake in stakes if stake.stake > 0] def get_stake_info_for_coldkey( @@ -1369,7 +1351,7 @@ def get_subnets(self, block: Optional[int] = None) -> list[int]: def get_total_stake_for_coldkey( self, ss58_address: str, block: Optional[int] = None - ) -> "Balance": + ) -> Balance: """ Returns the total stake held on a coldkey. @@ -1390,7 +1372,7 @@ def get_total_stake_for_coldkey( def get_total_stake_for_coldkeys( self, *ss58_addresses: str, block: Optional[int] = None - ) -> dict[str, "Balance"]: + ) -> dict[str, Balance]: """ Returns the total stake held on multiple coldkeys. @@ -1422,7 +1404,7 @@ def get_total_stake_for_coldkeys( def get_total_stake_for_hotkey( self, ss58_address: str, block: Optional[int] = None - ) -> "Balance": + ) -> Balance: """ Returns the total stake held on a hotkey. @@ -1443,7 +1425,7 @@ def get_total_stake_for_hotkey( def get_total_stake_for_hotkeys( self, *ss58_addresses: str, block: Optional[int] = None - ) -> dict[str, "Balance"]: + ) -> dict[str, Balance]: """ Returns the total stake held on hotkeys. @@ -1484,8 +1466,8 @@ def get_total_subnets(self, block: Optional[int] = None) -> Optional[int]: return getattr(result, "value", None) def get_transfer_fee( - self, wallet: "Wallet", dest: str, value: Union["Balance", float, int] - ) -> "Balance": + self, wallet: "Wallet", dest: str, value: Union[Balance, float, int] + ) -> Balance: """ Calculates the transaction fee for transferring tokens from a wallet to a specified destination address. This function simulates the transfer to estimate the associated cost, taking into account the current network @@ -1891,9 +1873,7 @@ def neurons_lite( hex_bytes_result = self.query_runtime_api( runtime_api="NeuronInfoRuntimeApi", method="get_neurons_lite", - params=[ - netuid - ], # TODO check to see if this can accept more than one at a time + params=[netuid], block=block, ) @@ -1933,7 +1913,7 @@ def query_identity(self, key: str, block: Optional[int] = None) -> dict: except TypeError: return {} - def recycle(self, netuid: int, block: Optional[int] = None) -> Optional["Balance"]: + def recycle(self, netuid: int, block: Optional[int] = None) -> Optional[Balance]: """ Retrieves the 'Burn' hyperparameter for a specified subnet. The 'Burn' parameter represents the amount of Tao that is effectively recycled within the Bittensor network. @@ -1951,24 +1931,19 @@ def recycle(self, netuid: int, block: Optional[int] = None) -> Optional["Balance call = self.get_hyperparameter(param_name="Burn", netuid=netuid, block=block) return None if call is None else Balance.from_rao(int(call)) - def subnet( - self, netuid: int, block_number: Optional[int] = None - ) -> Optional[DynamicInfo]: + def subnet(self, netuid: int, block: Optional[int] = None) -> Optional[DynamicInfo]: """ Retrieves the subnet information for a single subnet in the network. Args: netuid (int): The unique identifier of the subnet. - block_number (Optional[int]): The block number to query the subnet information from. + block (Optional[int]): The block number to query the subnet information from. Returns: Optional[DynamicInfo]: A DynamicInfo object, containing detailed information about a subnet. """ - if block_number is not None: - block_hash = self.get_block_hash(block_number) - else: - block_hash = None + block_hash = self.determine_block_hash(block) query = self.substrate.runtime_call( "SubnetInfoRuntimeApi", @@ -1976,7 +1951,7 @@ def subnet( params=[netuid], block_hash=block_hash, ) - subnet = DynamicInfo.from_vec_u8(bytes.fromhex(query.decode()[2:])) # type: ignore + subnet = DynamicInfo.from_vec_u8(hex_to_bytes(query.decode())) # type: ignore return subnet def subnet_exists(self, netuid: int, block: Optional[int] = None) -> bool: @@ -2066,12 +2041,24 @@ def wait_for_block(self, block: Optional[int] = None): >>> subtensor.wait_for_block() # Waits for next block >>> subtensor.wait_for_block(block=1234) # Waits for specific block """ - current_block = self.get_current_block() - target_block = block if block is not None else current_block + 1 - while current_block < target_block: - time.sleep(1) # Sleep for 1 second before checking again - current_block = self.get_current_block() + def handler(block_data: dict): + logging.debug( + f'reached block {block_data["header"]["number"]}. Waiting for block {target_block}' + ) + if block_data["header"]["number"] >= target_block: + return True + + current_block = self.substrate.get_block() + current_block_hash = current_block.get("header", {}).get("hash") + if block is not None: + target_block = block + else: + target_block = current_block["header"]["number"] + 1 + + self.substrate._get_block_handler( + current_block_hash, header_only=True, subscription_handler=handler + ) return True def weights( @@ -2775,7 +2762,7 @@ def transfer( self, wallet: "Wallet", dest: str, - amount: Union["Balance", float], + amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, transfer_all: bool = False, diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index 8ed504429b..0b65a90bb2 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -1,4 +1,4 @@ -from typing import Union, Optional, TypedDict +from typing import Union, TypedDict from scalecodec import ScaleType @@ -213,7 +213,7 @@ def __abs__(self): return Balance.from_rao(abs(self.rao)) @staticmethod - def from_float(amount: float, netuid: Optional[int] = 0): + def from_float(amount: float, netuid: int = 0): """ Given tao, return :func:`Balance` object with rao(``int``) and tao(``float``), where rao = int(tao*pow(10,9)) Args: @@ -223,11 +223,11 @@ def from_float(amount: float, netuid: Optional[int] = 0): Returns: A Balance object representing the given amount. """ - rao = int(amount * pow(10, 9)) - return Balance(rao).set_unit(netuid) + rao_ = int(amount * pow(10, 9)) + return Balance(rao_).set_unit(netuid) @staticmethod - def from_tao(amount: float, netuid: Optional[int] = 0): + def from_tao(amount: float, netuid: int = 0): """ Given tao, return Balance object with rao(``int``) and tao(``float``), where rao = int(tao*pow(10,9)) @@ -238,11 +238,11 @@ def from_tao(amount: float, netuid: Optional[int] = 0): Returns: A Balance object representing the given amount. """ - rao = int(amount * pow(10, 9)) - return Balance(rao).set_unit(netuid) + rao_ = int(amount * pow(10, 9)) + return Balance(rao_).set_unit(netuid) @staticmethod - def from_rao(amount: int, netuid: Optional[int] = 0): + def from_rao(amount: int, netuid: int = 0): """ Given rao, return Balance object with rao(``int``) and tao(``float``), where rao = int(tao*pow(10,9)) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index ccf1c44589..5b9368814d 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -12,7 +12,7 @@ def test_methods_comparable(mocker): async_subtensor = AsyncSubtensor(_mock=True) # methods which lives in subtensor only - excluded_subtensor_methods = ["wait_for_block"] + excluded_subtensor_methods = [] # methods which lives in async subtensor only excluded_async_subtensor_methods = ["initialize"]