From 871ac9e0bdcf24e7d1b96424b36fee4e81de14d7 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 14:00:23 -0700 Subject: [PATCH 01/49] `set_children_extrinsic` and `root_set_pending_childkey_cooldown_extrinsic` --- bittensor/core/async_subtensor.py | 45 +++++++++------- bittensor/core/extrinsics/asyncex/children.py | 53 +++++++++++++------ bittensor/core/extrinsics/children.py | 49 +++++++++++------ bittensor/core/subtensor.py | 43 ++++++++------- migration.md | 15 ++++-- .../extrinsics/asyncex/test_children.py | 9 ++-- tests/unit_tests/extrinsics/test_children.py | 7 +-- 7 files changed, 137 insertions(+), 84 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 347e97af36..2055b5ebe7 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4870,8 +4870,9 @@ async def reveal_weights( and be rejected. You can think of it as an expiration date for the transaction. Returns: - tuple[bool, str]: `True` if the weight revelation is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. This function allows subnet validators to reveal their previously committed weight vector. @@ -4908,25 +4909,27 @@ async def root_set_pending_childkey_cooldown( self, wallet: "Wallet", cooldown: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> tuple[bool, str]: """Sets the pending childkey cooldown. - Arguments: + Parameters: wallet: bittensor wallet instance. cooldown: the number of blocks to setting pending childkey cooldown. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `False`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is - `False`. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. Note: This operation can only be successfully performed if your wallet has root privileges. """ @@ -4934,9 +4937,10 @@ async def root_set_pending_childkey_cooldown( subtensor=self, wallet=wallet, cooldown=cooldown, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) # TODO: remove `block_hash` argument @@ -4979,29 +4983,30 @@ async def set_children( hotkey: str, netuid: int, children: list[tuple[float, str]], + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - raise_error: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Allows a coldkey to set children-keys. - Arguments: + Parameters: wallet: bittensor wallet instance. hotkey: The `SS58` address of the neuron's hotkey. netuid: The netuid value. children: A list of children with their proportions. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. Raises: DuplicateChild: There are duplicates in the list of children. @@ -5022,10 +5027,10 @@ async def set_children( hotkey=hotkey, netuid=netuid, children=children, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - raise_error=raise_error, - period=period, ) async def set_delegate_take( diff --git a/bittensor/core/extrinsics/asyncex/children.py b/bittensor/core/extrinsics/asyncex/children.py index 46853642fe..7476ef8fb8 100644 --- a/bittensor/core/extrinsics/asyncex/children.py +++ b/bittensor/core/extrinsics/asyncex/children.py @@ -12,30 +12,31 @@ async def set_children_extrinsic( hotkey: str, netuid: int, children: list[tuple[float, str]], - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - raise_error: bool = False, period: Optional[int] = None, -): + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, +) -> tuple[bool, str]: """ Allows a coldkey to set children-keys. - Arguments: - subtensor: bittensor subtensor. + Parameters: + subtensor: The Subtensor client instance used for blockchain interaction. wallet: bittensor wallet instance. hotkey: The ``SS58`` address of the neuron's hotkey. netuid: The netuid value. children: A list of children with their proportions. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the operation, - and the second element is a message providing additional information. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. Raises: DuplicateChild: There are duplicates in the list of children. @@ -75,10 +76,10 @@ async def set_children_extrinsic( success, message = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - raise_error=raise_error, - period=period, ) if not wait_for_finalization and not wait_for_inclusion: @@ -94,12 +95,29 @@ async def root_set_pending_childkey_cooldown_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", cooldown: int, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """ - Allows a coldkey to set children-keys. + Allows a root coldkey to set children-keys. + + Parameters: + subtensor: The Subtensor client instance used for blockchain interaction. + wallet: The wallet used to sign the extrinsic (must be unlocked). + cooldown: The cooldown period in blocks. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + + Returns: + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ unlock = unlock_key(wallet) @@ -122,9 +140,10 @@ async def root_set_pending_childkey_cooldown_extrinsic( success, message = await subtensor.sign_and_send_extrinsic( call=sudo_call, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/extrinsics/children.py b/bittensor/core/extrinsics/children.py index dd91fbe97b..5f7c1a9d84 100644 --- a/bittensor/core/extrinsics/children.py +++ b/bittensor/core/extrinsics/children.py @@ -12,30 +12,31 @@ def set_children_extrinsic( hotkey: str, netuid: int, children: list[tuple[float, str]], - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - raise_error: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ): """ Allows a coldkey to set children-keys. - Arguments: - subtensor: bittensor subtensor. + Parameters: + subtensor: The Subtensor client instance used for blockchain interaction. wallet: bittensor wallet instance. hotkey: The ``SS58`` address of the neuron's hotkey. netuid: The netuid value. children: A list of children with their proportions. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the operation, - and the second element is a message providing additional information. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. Raises: DuplicateChild: There are duplicates in the list of children. @@ -74,10 +75,10 @@ def set_children_extrinsic( success, message = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - raise_error=raise_error, - period=period, ) if not wait_for_finalization and not wait_for_inclusion: @@ -93,12 +94,29 @@ def root_set_pending_childkey_cooldown_extrinsic( subtensor: "Subtensor", wallet: "Wallet", cooldown: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """ - Allows a coldkey to set children-keys. + Allows a root coldkey to set children-keys. + + Parameters: + subtensor: The Subtensor client instance used for blockchain interaction. + wallet: The wallet used to sign the extrinsic (must be unlocked). + cooldown: The cooldown period in blocks. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + + Returns: + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ unlock = unlock_key(wallet) @@ -120,9 +138,10 @@ def root_set_pending_childkey_cooldown_extrinsic( success, message = subtensor.sign_and_send_extrinsic( call=sudo_call, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index a6c83dc96b..495c391b5e 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3773,25 +3773,27 @@ def root_set_pending_childkey_cooldown( self, wallet: "Wallet", cooldown: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> tuple[bool, str]: """Sets the pending childkey cooldown. - Arguments: + Parameters: wallet: bittensor wallet instance. cooldown: the number of blocks to setting pending childkey cooldown. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``False``. period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + 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. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. Note: This operation can only be successfully performed if your wallet has root privileges. """ @@ -3799,9 +3801,10 @@ def root_set_pending_childkey_cooldown( subtensor=self, wallet=wallet, cooldown=cooldown, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def set_children( @@ -3810,30 +3813,30 @@ def set_children( hotkey: str, netuid: int, children: list[tuple[float, str]], + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - raise_error: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Allows a coldkey to set children-keys. - Arguments: + Parameters: wallet: bittensor wallet instance. hotkey: The ``SS58`` address of the neuron's hotkey. netuid: The netuid value. children: A list of children with their proportions. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. - + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ return set_children_extrinsic( subtensor=self, @@ -3841,10 +3844,10 @@ def set_children( hotkey=hotkey, netuid=netuid, children=children, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - raise_error=raise_error, - period=period, ) def set_delegate_take( @@ -3861,7 +3864,7 @@ def set_delegate_take( Sets the delegate 'take' percentage for a neuron identified by its hotkey. The 'take' represents the percentage of rewards that the delegate claims from its nominators' stakes. - Arguments: + Parameters: wallet (bittensor_wallet.Wallet): bittensor wallet instance. hotkey_ss58 (str): The ``SS58`` address of the neuron's hotkey. take (float): Percentage reward for the delegate. diff --git a/migration.md b/migration.md index 25edf20b71..16582ec6b0 100644 --- a/migration.md +++ b/migration.md @@ -1,7 +1,7 @@ # Plan ## Extrinsics and related -1. Standardize parameter order across all extrinsics and related calls. Pass extrinsic-specific arguments first (e.g., wallet, hotkey, netuid, amount), followed by optional general flags (e.g., wait_for_inclusion, wait_for_finalization) +1. ✅ Standardize parameter order across all extrinsics and related calls. Pass extrinsic-specific arguments first (e.g., wallet, hotkey, netuid, amount), followed by optional general flags (e.g., wait_for_inclusion, wait_for_finalization)
Example @@ -35,9 +35,9 @@ allow_partial_stake: bool = False, safe_staking: bool = False, period: Optional[int] = None, + raise_error: bool = True, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - raise_error: bool = True, ) -> bool: ```
@@ -48,8 +48,8 @@ - Ease of processing - This class should contain success, message, and optionally data and logs. (to save all logs during the extrinsic) -3. Set `wait_for_inclusion` and `wait_for_finalization` to `True` by default in extrinsics and their related calls. Then we will guarantee the correct/expected extrinsic call response is consistent with the chain response. If the user changes those values, then it is the user's responsibility. -4. Make the internal logic of extrinsics the same. There are extrinsics that are slightly different in implementation. +3. ✅ Set `wait_for_inclusion` and `wait_for_finalization` to `True` by default in extrinsics and their related calls. Then we will guarantee the correct/expected extrinsic call response is consistent with the chain response. If the user changes those values, then it is the user's responsibility. +4. ✅ Make the internal logic of extrinsics the same. There are extrinsics that are slightly different in implementation. 5. Since SDK is not a responsible tool, try to remove all calculations inside extrinsics that do not affect the result, but are only used in logging. Actually, this should be applied not to extrinsics only but for all codebase. @@ -159,4 +159,9 @@ It must include: - [x] `._do_set_root_weights` logic is included in the main code `.set_root_weights_extrinsic` - [x] `._do_transfer` logic is included in the main code `.transfer_extrinsic` - [x] `dest` parameter has been renamed to `destination` in `transfer_extrinsic` function and `subtensor.transfer` method. -- [x]] obsolete extrinsic `set_root_weights_extrinsic` removed. Also related subtensor calls `subtensor.set_root_weights_extrinsic` removed too. \ No newline at end of file +- [x]] obsolete extrinsic `set_root_weights_extrinsic` removed. Also related subtensor calls `subtensor.set_root_weights_extrinsic` removed too. + +# Standardize parameter order is applied for (extrinsics and related calls): +Note: `raise_error` parameter is included in the list of parameters and parameters order is standardized. +- [x] `.set_children_extrinsic` and `.root_set_pending_childkey_cooldown_extrinsic`. `subtensor.set_children` and `subtensor.root_set_pending_childkey_cooldown` methods. +- \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/asyncex/test_children.py b/tests/unit_tests/extrinsics/asyncex/test_children.py index da107dfad4..48c0940170 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_children.py +++ b/tests/unit_tests/extrinsics/asyncex/test_children.py @@ -50,10 +50,10 @@ async def test_set_children_extrinsic(subtensor, mocker, fake_wallet): mocked_sign_and_send_extrinsic.assert_awaited_once_with( call=substrate.compose_call.return_value, wallet=fake_wallet, - wait_for_inclusion=True, - wait_for_finalization=False, period=None, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert success is True @@ -86,9 +86,10 @@ async def test_root_set_pending_childkey_cooldown_extrinsic( mocked_sign_and_send_extrinsic.assert_awaited_once_with( call=substrate.compose_call.return_value, wallet=fake_wallet, - wait_for_inclusion=True, - wait_for_finalization=False, period=None, + raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert success is True assert "Success" in message diff --git a/tests/unit_tests/extrinsics/test_children.py b/tests/unit_tests/extrinsics/test_children.py index 2af3517fff..12bb96f09b 100644 --- a/tests/unit_tests/extrinsics/test_children.py +++ b/tests/unit_tests/extrinsics/test_children.py @@ -46,10 +46,10 @@ def test_set_children_extrinsic(subtensor, mocker, fake_wallet): mocked_sign_and_send_extrinsic.assert_called_once_with( call=subtensor.substrate.compose_call.return_value, wallet=fake_wallet, - wait_for_inclusion=True, - wait_for_finalization=False, period=None, raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert success is True @@ -78,9 +78,10 @@ def test_root_set_pending_childkey_cooldown_extrinsic(subtensor, mocker, fake_wa mocked_sign_and_send_extrinsic.assert_called_once_with( call=subtensor.substrate.compose_call.return_value, wallet=fake_wallet, + period=None, + raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, - period=None, ) assert success is True assert "Success" in message From 3547a93c3452fa2370c6347d9dfe2f9845c4debb Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 15:16:08 -0700 Subject: [PATCH 02/49] `.commit_reveal_extrinsic` and `subtensor.set_weights` + tests --- bittensor/core/async_subtensor.py | 37 ++++++------ .../core/extrinsics/asyncex/commit_reveal.py | 34 ++++++----- bittensor/core/extrinsics/commit_reveal.py | 32 ++++++----- bittensor/core/subtensor.py | 57 +++++++++++-------- migration.md | 7 ++- .../extrinsics/asyncex/test_commit_reveal.py | 3 + .../extrinsics/test_commit_reveal.py | 3 + tests/unit_tests/test_subtensor.py | 2 + 8 files changed, 103 insertions(+), 72 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 2055b5ebe7..8ea2bc31f3 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5169,12 +5169,14 @@ async def set_weights( netuid: int, uids: Union[NDArray[np.int64], "torch.LongTensor", list], weights: Union[NDArray[np.float32], "torch.FloatTensor", list], + block_time: float = 12.0, + commit_reveal_version: int = 4, + max_retries: int = 5, version_key: int = version_as_int, + period: Optional[int] = 8, + raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - max_retries: int = 5, - block_time: float = 12.0, - period: Optional[int] = 8, ): """ Sets the weight vector for a neuron acting as a validator, specifying the weights assigned to subnet miners @@ -5184,26 +5186,27 @@ async def set_weights( work. These weight vectors are used by the Yuma Consensus algorithm to compute emissions for both validators and miners. - Arguments: + Parameters: wallet: The wallet associated with the subnet validator setting the weights. netuid: The unique identifier of the subnet. uids: The list of subnet miner neuron UIDs that the weights are being set for. weights: The corresponding weights to be set for each UID, representing the validator's evaluation of each miner's performance. - version_key: Version key for compatibility with the network. Default is int representation of - the Bittensor version. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `False`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is - `False`. - max_retries: The number of maximum attempts to set weights. Default is `5`. - block_time: The number of seconds for block duration. Default is 12.0 seconds. + block_time: The number of seconds for block duration. + commit_reveal_version: The version of the chain commit-reveal protocol to use. + max_retries: The number of maximum attempts to set weights. + version_key: Version key for compatibility with the network. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. Default is 8. + and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: `True` if the setting of weights is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. This function is crucial in the Yuma Consensus mechanism, where each validator's weight vector contributes to the overall weight matrix used to calculate emissions and maintain network consensus. @@ -5249,11 +5252,13 @@ async def _blocks_weight_limit() -> bool: netuid=netuid, uids=uids, weights=weights, + block_time=block_time, + commit_reveal_version=commit_reveal_version, version_key=version_key, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - block_time=block_time, - period=period, ) retries += 1 return success, message diff --git a/bittensor/core/extrinsics/asyncex/commit_reveal.py b/bittensor/core/extrinsics/asyncex/commit_reveal.py index 78a7ef4824..fd1bd00f53 100644 --- a/bittensor/core/extrinsics/asyncex/commit_reveal.py +++ b/bittensor/core/extrinsics/asyncex/commit_reveal.py @@ -22,34 +22,37 @@ async def commit_reveal_extrinsic( netuid: int, uids: Union[NDArray[np.int64], "torch.LongTensor", list], weights: Union[NDArray[np.float32], "torch.FloatTensor", list], + block_time: Union[int, float] = 12.0, + commit_reveal_version: int = 4, version_key: int = version_as_int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - block_time: Union[int, float] = 12.0, - period: Optional[int] = None, - commit_reveal_version: int = 4, ) -> tuple[bool, str]: """ Commits and reveals weights for a given subtensor and wallet with provided uids and weights. - Arguments: - subtensor: The AsyncSubtensor instance. + Parameters: + subtensor: The Subtensor instance. wallet: The wallet to use for committing and revealing. netuid: The id of the network. uids: The uids to commit. weights: The weights associated with the uids. - version_key: The version key to use for committing and revealing. Default is version_as_int. - wait_for_inclusion: Whether to wait for the inclusion of the transaction. Default is False. - wait_for_finalization: Whether to wait for the finalization of the transaction. Default is False. - block_time (float): The number of seconds for block duration. Default is 12.0 seconds. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. - commit_reveal_version: The version of the chain commit-reveal protocol to use. Default is ``4``. + block_time: The number of seconds for block duration. + commit_reveal_version: The version of the chain commit-reveal protocol to use. + version_key: The version key to use for committing and revealing. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure, and the second - element is a message associated with the result + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ try: uids, weights = convert_and_normalize_weights_and_uids(uids, weights) @@ -96,6 +99,7 @@ async def commit_reveal_extrinsic( wait_for_finalization=wait_for_finalization, sign_with="hotkey", period=period, + raise_error=raise_error, ) if not success: diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 2f83229b33..997b13b410 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -22,34 +22,37 @@ def commit_reveal_extrinsic( netuid: int, uids: Union[NDArray[np.int64], "torch.LongTensor", list], weights: Union[NDArray[np.float32], "torch.FloatTensor", list], + block_time: Union[int, float] = 12.0, + commit_reveal_version: int = 4, version_key: int = version_as_int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - block_time: Union[int, float] = 12.0, - period: Optional[int] = None, - commit_reveal_version: int = 4, ) -> tuple[bool, str]: """ Commits and reveals weights for a given subtensor and wallet with provided uids and weights. - Arguments: + Parameters: subtensor: The Subtensor instance. wallet: The wallet to use for committing and revealing. netuid: The id of the network. uids: The uids to commit. weights: The weights associated with the uids. - version_key: The version key to use for committing and revealing. Default is version_as_int. - wait_for_inclusion: Whether to wait for the inclusion of the transaction. Default is False. - wait_for_finalization: Whether to wait for the finalization of the transaction. Default is False. - block_time (float): The number of seconds for block duration. Default is 12.0 seconds. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. - If the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. - commit_reveal_version: The version of the chain commit-reveal protocol to use. Default is ``4``. + block_time: The number of seconds for block duration. + commit_reveal_version: The version of the chain commit-reveal protocol to use. + version_key: The version key to use for committing and revealing. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure, and the second - element is a message associated with the result + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ try: uids, weights = convert_and_normalize_weights_and_uids(uids, weights) @@ -96,6 +99,7 @@ def commit_reveal_extrinsic( wait_for_finalization=wait_for_finalization, sign_with="hotkey", period=period, + raise_error=raise_error, ) if not success: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 495c391b5e..ab0caede95 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3985,40 +3985,47 @@ def set_weights( netuid: int, uids: Union[NDArray[np.int64], "torch.LongTensor", list], weights: Union[NDArray[np.float32], "torch.FloatTensor", list], + block_time: float = 12.0, + commit_reveal_version: int = 4, + max_retries: int = 5, version_key: int = version_as_int, + period: Optional[int] = 8, + raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - max_retries: int = 5, - block_time: float = 12.0, - period: Optional[int] = 8, ) -> tuple[bool, str]: """ - Sets the interneuronal weights for the specified neuron. This process involves specifying the influence or - trust a neuron places on other neurons in the network, which is a fundamental aspect of Bittensor's - decentralized learning architecture. + Sets the interneuronal weights for the specified neuron. This process involves specifying the influence or trust + a neuron places on other neurons in the network, which is a fundamental aspect of Bittensor's decentralized + learning architecture. - Arguments: - wallet: The wallet associated with the neuron setting the weights. + Parameters: + wallet: The wallet associated with the subnet validator setting the weights. netuid: The unique identifier of the subnet. - uids: The list of neuron UIDs that the weights are being set for. - weights: The corresponding weights to be set for each UID. - version_key: Version key for compatibility with the network. Default is int representation of a Bittensor - version. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is ``False``. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is ``False``. - max_retries: The number of maximum attempts to set weights. Default is ``5``. - block_time: The number of seconds for block duration. Default is 12.0 seconds. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's + uids: The list of subnet miner neuron UIDs that the weights are being set for. + weights: The corresponding weights to be set for each UID, representing the validator's evaluation of each + miner's performance. + block_time: The number of seconds for block duration. + commit_reveal_version: The version of the chain commit-reveal protocol to use. + max_retries: The number of maximum attempts to set weights. + version_key: Version key for compatibility with the network. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. Default is 8. + and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple: - `True` if the setting of weights is successful, `False` otherwise. - `msg` is a string value describing the success or potential error. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. This function is crucial in shaping the network's collective intelligence, where each neuron's learning and - contribution are influenced by the weights it sets towards others. + contribution are influenced by the weights it sets towards others. + + Notes: + See """ def _blocks_weight_limit() -> bool: @@ -4051,11 +4058,13 @@ def _blocks_weight_limit() -> bool: netuid=netuid, uids=uids, weights=weights, + block_time=block_time, + commit_reveal_version=commit_reveal_version, version_key=version_key, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - block_time=block_time, - period=period, ) retries += 1 return success, message diff --git a/migration.md b/migration.md index 16582ec6b0..85ac84c77b 100644 --- a/migration.md +++ b/migration.md @@ -106,7 +106,8 @@ rename this variable in documentation. 11. Remove `bittensor.utils.version.version_checking` 12. Find and process all `TODOs` across the entire code base. If in doubt, discuss each one with the team separately. SDK has 29 TODOs. -13. ✅ The SDK is dropping support for `Python 3.9` starting with this release.~~ +13. ✅ The SDK is dropping support for `Python 3.9` starting with this release. +14. Remove `Default is` and `Default to` in docstrings bc parameters enough. ## New features 1. Add `bittensor.utils.hex_to_ss58` function. SDK still doesn't have it. (Probably inner import `from scalecodec import ss58_encode, ss58_decode`) @@ -159,9 +160,9 @@ It must include: - [x] `._do_set_root_weights` logic is included in the main code `.set_root_weights_extrinsic` - [x] `._do_transfer` logic is included in the main code `.transfer_extrinsic` - [x] `dest` parameter has been renamed to `destination` in `transfer_extrinsic` function and `subtensor.transfer` method. -- [x]] obsolete extrinsic `set_root_weights_extrinsic` removed. Also related subtensor calls `subtensor.set_root_weights_extrinsic` removed too. +- [x] obsolete extrinsic `set_root_weights_extrinsic` removed. Also related subtensor calls `subtensor.set_root_weights_extrinsic` removed too. # Standardize parameter order is applied for (extrinsics and related calls): Note: `raise_error` parameter is included in the list of parameters and parameters order is standardized. - [x] `.set_children_extrinsic` and `.root_set_pending_childkey_cooldown_extrinsic`. `subtensor.set_children` and `subtensor.root_set_pending_childkey_cooldown` methods. -- \ No newline at end of file +- [x] `.commit_reveal_extrinsic` and `subtensor.set_weights` diff --git a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py index 7b2a74aeb9..7e46e436be 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py @@ -121,6 +121,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_torch( wait_for_finalization=True, sign_with="hotkey", period=None, + raise_error=False, ) @@ -176,6 +177,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_numpy( wait_for_finalization=False, sign_with="hotkey", period=None, + raise_error=False, ) @@ -234,6 +236,7 @@ async def test_commit_reveal_v3_extrinsic_response_false( wait_for_finalization=True, sign_with="hotkey", period=None, + raise_error=False, ) diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index b0c2da977c..e9490bcbc4 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -119,6 +119,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( wait_for_finalization=True, sign_with="hotkey", period=None, + raise_error=False, ) @@ -173,6 +174,7 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy( wait_for_finalization=False, sign_with="hotkey", period=None, + raise_error=False, ) @@ -230,6 +232,7 @@ def test_commit_reveal_v3_extrinsic_response_false( wait_for_finalization=True, sign_with="hotkey", period=None, + raise_error=False, ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 76a211b782..5f3125a3f5 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3180,11 +3180,13 @@ def test_set_weights_with_commit_reveal_enabled(subtensor, fake_wallet, mocker): netuid=fake_netuid, uids=fake_uids, weights=fake_weights, + commit_reveal_version=4, version_key=subtensor_module.version_as_int, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, block_time=12.0, period=8, + raise_error=False, ) assert result == mocked_commit_reveal_v3_extrinsic.return_value From 049810b17b29ebf26fe115d5d4977eb7a9e3d3fb Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 15:24:32 -0700 Subject: [PATCH 03/49] `wait_for_inclusion = True` and `wait_for_finalization = True` for `subtensor.set_weights` since this is extrinsic --- bittensor/core/async_subtensor.py | 4 ++-- bittensor/core/subtensor.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 8ea2bc31f3..1c6f81ca54 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5175,8 +5175,8 @@ async def set_weights( version_key: int = version_as_int, period: Optional[int] = 8, raise_error: bool = False, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ): """ Sets the weight vector for a neuron acting as a validator, specifying the weights assigned to subnet miners diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index ab0caede95..bc61f660b6 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3991,8 +3991,8 @@ def set_weights( version_key: int = version_as_int, period: Optional[int] = 8, raise_error: bool = False, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """ Sets the interneuronal weights for the specified neuron. This process involves specifying the influence or trust From 07105bce9de3530d91a264c3ae7f73b319e16a8d Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 15:27:44 -0700 Subject: [PATCH 04/49] test --- tests/unit_tests/test_async_subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index d75c5c3604..a267f8c95f 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2799,8 +2799,8 @@ async def test_set_weights_success(subtensor, fake_wallet, mocker): netuid=fake_netuid, uids=fake_uids, version_key=async_subtensor.version_as_int, - wait_for_finalization=False, - wait_for_inclusion=False, + wait_for_finalization=True, + wait_for_inclusion=True, weights=fake_weights, period=8, ) From 0ef19bf1c326855c554cd69063ff9b7c87637675 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 15:39:50 -0700 Subject: [PATCH 05/49] `.add_liquidity_extrinsic` and `subtensor.add_liquidity` --- bittensor/core/async_subtensor.py | 20 +++++++------- .../core/extrinsics/asyncex/liquidity.py | 27 ++++++++++--------- bittensor/core/extrinsics/liquidity.py | 27 ++++++++++--------- bittensor/core/subtensor.py | 20 +++++++------- migration.md | 1 + .../extrinsics/asyncex/test_liquidity.py | 3 ++- tests/unit_tests/extrinsics/test_liquidity.py | 3 ++- tests/unit_tests/test_async_subtensor.py | 3 ++- tests/unit_tests/test_subtensor.py | 3 ++- 9 files changed, 61 insertions(+), 46 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 1c6f81ca54..9cc096419d 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4370,26 +4370,27 @@ async def add_liquidity( price_low: Balance, price_high: Balance, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """ Adds liquidity to the specified price range. - Arguments: + Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. liquidity: The amount of liquidity to be added. price_low: The lower bound of the price tick range. In TAO. price_high: The upper bound of the price tick range. In TAO. - hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to - `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. + hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -4397,7 +4398,7 @@ async def add_liquidity( - False and an error message if the submission fails or the wallet cannot be unlocked. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call ``toggle_user_liquidity`` - method to enable/disable user liquidity. + method to enable/disable user liquidity. """ return await add_liquidity_extrinsic( subtensor=self, @@ -4407,9 +4408,10 @@ async def add_liquidity( price_low=price_low, price_high=price_high, hotkey=hotkey, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def add_stake_multiple( diff --git a/bittensor/core/extrinsics/asyncex/liquidity.py b/bittensor/core/extrinsics/asyncex/liquidity.py index 8c41e1b66b..d9a6fec11b 100644 --- a/bittensor/core/extrinsics/asyncex/liquidity.py +++ b/bittensor/core/extrinsics/asyncex/liquidity.py @@ -18,26 +18,28 @@ async def add_liquidity_extrinsic( price_low: Balance, price_high: Balance, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """ Adds liquidity to the specified price range. - Arguments: + Parameters: subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. liquidity: The amount of liquidity to be added. price_low: The lower bound of the price tick range. price_high: The upper bound of the price tick range. - hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: @@ -73,6 +75,7 @@ async def add_liquidity_extrinsic( wait_for_finalization=wait_for_finalization, use_nonce=True, period=period, + raise_error=raise_error, ) @@ -89,7 +92,7 @@ async def modify_liquidity_extrinsic( ) -> tuple[bool, str]: """Modifies liquidity in liquidity position by adding or removing liquidity from it. - Arguments: + Parameters: subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. @@ -147,7 +150,7 @@ async def remove_liquidity_extrinsic( ) -> tuple[bool, str]: """Remove liquidity and credit balances back to wallet's hotkey stake. - Arguments: + Parameters: subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. @@ -202,7 +205,7 @@ async def toggle_user_liquidity_extrinsic( ) -> tuple[bool, str]: """Allow to toggle user liquidity for specified subnet. - Arguments: + Parameters: subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. diff --git a/bittensor/core/extrinsics/liquidity.py b/bittensor/core/extrinsics/liquidity.py index 96e502692c..62a0d0e0e9 100644 --- a/bittensor/core/extrinsics/liquidity.py +++ b/bittensor/core/extrinsics/liquidity.py @@ -18,26 +18,28 @@ def add_liquidity_extrinsic( price_low: Balance, price_high: Balance, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """ Adds liquidity to the specified price range. - Arguments: + Parameters: subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. liquidity: The amount of liquidity to be added. price_low: The lower bound of the price tick range. price_high: The upper bound of the price tick range. - hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: @@ -73,6 +75,7 @@ def add_liquidity_extrinsic( wait_for_finalization=wait_for_finalization, use_nonce=True, period=period, + raise_error=raise_error, ) @@ -89,7 +92,7 @@ def modify_liquidity_extrinsic( ) -> tuple[bool, str]: """Modifies liquidity in liquidity position by adding or removing liquidity from it. - Arguments: + Parameters: subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. @@ -147,7 +150,7 @@ def remove_liquidity_extrinsic( ) -> tuple[bool, str]: """Remove liquidity and credit balances back to wallet's hotkey stake. - Arguments: + Parameters: subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. @@ -202,7 +205,7 @@ def toggle_user_liquidity_extrinsic( ) -> tuple[bool, str]: """Allow to toggle user liquidity for specified subnet. - Arguments: + Parameters: subtensor: The Subtensor client instance used for blockchain interaction. wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index bc61f660b6..9c1d40882d 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3201,26 +3201,27 @@ def add_liquidity( price_low: Balance, price_high: Balance, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """ Adds liquidity to the specified price range. - Arguments: + Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. liquidity: The amount of liquidity to be added. price_low: The lower bound of the price tick range. In TAO. price_high: The upper bound of the price tick range. In TAO. - hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to - `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. + hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -3228,7 +3229,7 @@ def add_liquidity( - False and an error message if the submission fails or the wallet cannot be unlocked. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity` - method to enable/disable user liquidity. + method to enable/disable user liquidity. """ return add_liquidity_extrinsic( subtensor=self, @@ -3238,9 +3239,10 @@ def add_liquidity( price_low=price_low, price_high=price_high, hotkey=hotkey, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def add_stake_multiple( diff --git a/migration.md b/migration.md index 85ac84c77b..69da29591f 100644 --- a/migration.md +++ b/migration.md @@ -166,3 +166,4 @@ It must include: Note: `raise_error` parameter is included in the list of parameters and parameters order is standardized. - [x] `.set_children_extrinsic` and `.root_set_pending_childkey_cooldown_extrinsic`. `subtensor.set_children` and `subtensor.root_set_pending_childkey_cooldown` methods. - [x] `.commit_reveal_extrinsic` and `subtensor.set_weights` +- [x] `.add_liquidity_extrinsic` and `subtensor.add_liquidity` diff --git a/tests/unit_tests/extrinsics/asyncex/test_liquidity.py b/tests/unit_tests/extrinsics/asyncex/test_liquidity.py index ae780e6c4b..2d90ceaf0f 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_liquidity.py +++ b/tests/unit_tests/extrinsics/asyncex/test_liquidity.py @@ -43,9 +43,10 @@ async def test_add_liquidity_extrinsic(subtensor, fake_wallet, mocker): call=mocked_compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, use_nonce=True, period=None, + raise_error=False, ) assert result == mocked_sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/extrinsics/test_liquidity.py b/tests/unit_tests/extrinsics/test_liquidity.py index 7d3942909e..c626ce0b3c 100644 --- a/tests/unit_tests/extrinsics/test_liquidity.py +++ b/tests/unit_tests/extrinsics/test_liquidity.py @@ -41,9 +41,10 @@ def test_add_liquidity_extrinsic(subtensor, fake_wallet, mocker): call=mocked_compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, use_nonce=True, period=None, + raise_error=False, ) assert result == mocked_sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index a267f8c95f..dc37a8670b 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3698,8 +3698,9 @@ async def test_add_liquidity(subtensor, fake_wallet, mocker): price_high=Balance.from_tao(130).rao, hotkey=None, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 5f3125a3f5..1d5fd377be 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3958,8 +3958,9 @@ def test_add_liquidity(subtensor, fake_wallet, mocker): price_high=Balance.from_tao(130).rao, hotkey=None, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value From f695bdfd424f7d8a6aa0418e1019084c19f5737a Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 16:10:55 -0700 Subject: [PATCH 06/49] `.modify_liquidity_extrinsic` and `subtensor.modify_liquidity` + `.remove_liquidity_extrinsic` and `subtensor.remove_liquidity` + `.toggle_user_liquidity_extrinsic` and `subtensor.toggle_user_liquidity` --- bittensor/core/async_subtensor.py | 61 +++++++++++-------- .../core/extrinsics/asyncex/liquidity.py | 57 +++++++++-------- bittensor/core/extrinsics/liquidity.py | 59 ++++++++++-------- bittensor/core/subtensor.py | 55 +++++++++-------- migration.md | 3 + .../extrinsics/asyncex/test_liquidity.py | 9 ++- tests/unit_tests/extrinsics/test_liquidity.py | 9 ++- tests/unit_tests/test_async_subtensor.py | 9 ++- tests/unit_tests/test_subtensor.py | 9 ++- 9 files changed, 159 insertions(+), 112 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 9cc096419d..f224bb7e22 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4585,24 +4585,25 @@ async def modify_liquidity( position_id: int, liquidity_delta: Balance, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Modifies liquidity in liquidity position by adding or removing liquidity from it. - Arguments: + Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative). - hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to - `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. + hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -4636,7 +4637,7 @@ async def modify_liquidity( ) Note: Modifying is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity` - to enable/disable user liquidity. + to enable/disable user liquidity. """ return await modify_liquidity_extrinsic( subtensor=self, @@ -4645,9 +4646,10 @@ async def modify_liquidity( position_id=position_id, liquidity_delta=liquidity_delta, hotkey=hotkey, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def move_stake( @@ -4798,23 +4800,24 @@ async def remove_liquidity( netuid: int, position_id: int, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Remove liquidity and credit balances back to wallet's hotkey stake. - Arguments: + Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. - hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to - `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. + hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -4823,7 +4826,7 @@ async def remove_liquidity( Note: - Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity` - extrinsic to enable/disable user liquidity. + extrinsic to enable/disable user liquidity. - To get the `position_id` use `get_liquidity_list` method. """ return await remove_liquidity_extrinsic( @@ -4832,9 +4835,10 @@ async def remove_liquidity( netuid=netuid, position_id=position_id, hotkey=hotkey, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def reveal_weights( @@ -5437,21 +5441,23 @@ async def toggle_user_liquidity( wallet: "Wallet", netuid: int, enable: bool, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Allow to toggle user liquidity for specified subnet. - Arguments: + Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. enable: Boolean indicating whether to enable user liquidity. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -5465,9 +5471,10 @@ async def toggle_user_liquidity( wallet=wallet, netuid=netuid, enable=enable, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def transfer( diff --git a/bittensor/core/extrinsics/asyncex/liquidity.py b/bittensor/core/extrinsics/asyncex/liquidity.py index d9a6fec11b..fc98f46631 100644 --- a/bittensor/core/extrinsics/asyncex/liquidity.py +++ b/bittensor/core/extrinsics/asyncex/liquidity.py @@ -47,7 +47,7 @@ async def add_liquidity_extrinsic( - False and an error message if the submission fails or the wallet cannot be unlocked. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call - `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. + `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) @@ -86,9 +86,10 @@ async def modify_liquidity_extrinsic( position_id: int, liquidity_delta: Balance, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Modifies liquidity in liquidity position by adding or removing liquidity from it. @@ -99,11 +100,12 @@ async def modify_liquidity_extrinsic( position_id: The id of the position record in the pool. liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative). hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: @@ -135,6 +137,7 @@ async def modify_liquidity_extrinsic( wait_for_finalization=wait_for_finalization, use_nonce=True, period=period, + raise_error=raise_error, ) @@ -144,9 +147,10 @@ async def remove_liquidity_extrinsic( netuid: int, position_id: int, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Remove liquidity and credit balances back to wallet's hotkey stake. @@ -156,19 +160,20 @@ async def remove_liquidity_extrinsic( netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: - True and a success message if the extrinsic is successfully submitted or processed. - False and an error message if the submission fails or the wallet cannot be unlocked. - Note: Adding is allowed even when user liquidity is enabled in specified subnet. - Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. + Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call + `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) @@ -191,6 +196,7 @@ async def remove_liquidity_extrinsic( wait_for_finalization=wait_for_finalization, use_nonce=True, period=period, + raise_error=raise_error, ) @@ -199,9 +205,10 @@ async def toggle_user_liquidity_extrinsic( wallet: "Wallet", netuid: int, enable: bool, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Allow to toggle user liquidity for specified subnet. @@ -210,11 +217,12 @@ async def toggle_user_liquidity_extrinsic( wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. enable: Boolean indicating whether to enable user liquidity. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: @@ -237,4 +245,5 @@ async def toggle_user_liquidity_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) diff --git a/bittensor/core/extrinsics/liquidity.py b/bittensor/core/extrinsics/liquidity.py index 62a0d0e0e9..dbb974082a 100644 --- a/bittensor/core/extrinsics/liquidity.py +++ b/bittensor/core/extrinsics/liquidity.py @@ -47,7 +47,7 @@ def add_liquidity_extrinsic( - False and an error message if the submission fails or the wallet cannot be unlocked. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call - `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. + `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) @@ -86,9 +86,10 @@ def modify_liquidity_extrinsic( position_id: int, liquidity_delta: Balance, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Modifies liquidity in liquidity position by adding or removing liquidity from it. @@ -99,11 +100,12 @@ def modify_liquidity_extrinsic( position_id: The id of the position record in the pool. liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative). hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: @@ -111,7 +113,7 @@ def modify_liquidity_extrinsic( - False and an error message if the submission fails or the wallet cannot be unlocked. Note: Modifying is allowed even when user liquidity is enabled in specified subnet. Call - `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. + `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) @@ -135,6 +137,7 @@ def modify_liquidity_extrinsic( wait_for_finalization=wait_for_finalization, use_nonce=True, period=period, + raise_error=raise_error, ) @@ -144,9 +147,10 @@ def remove_liquidity_extrinsic( netuid: int, position_id: int, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Remove liquidity and credit balances back to wallet's hotkey stake. @@ -156,19 +160,20 @@ def remove_liquidity_extrinsic( netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: - True and a success message if the extrinsic is successfully submitted or processed. - False and an error message if the submission fails or the wallet cannot be unlocked. - Note: Adding is allowed even when user liquidity is enabled in specified subnet. - Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. + Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call + `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) @@ -191,6 +196,7 @@ def remove_liquidity_extrinsic( wait_for_finalization=wait_for_finalization, use_nonce=True, period=period, + raise_error=raise_error, ) @@ -199,9 +205,10 @@ def toggle_user_liquidity_extrinsic( wallet: "Wallet", netuid: int, enable: bool, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Allow to toggle user liquidity for specified subnet. @@ -210,11 +217,12 @@ def toggle_user_liquidity_extrinsic( wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. enable: Boolean indicating whether to enable user liquidity. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: @@ -237,4 +245,5 @@ def toggle_user_liquidity_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 9c1d40882d..8929859462 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3420,24 +3420,25 @@ def modify_liquidity( position_id: int, liquidity_delta: Balance, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Modifies liquidity in liquidity position by adding or removing liquidity from it. - Arguments: + Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. liquidity_delta: The amount of liquidity to be added or removed (add if positive or remove if negative). - hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to - `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. + hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -3471,7 +3472,7 @@ def modify_liquidity( ) Note: Modifying is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity` - to enable/disable user liquidity. + to enable/disable user liquidity. """ return modify_liquidity_extrinsic( subtensor=self, @@ -3480,9 +3481,10 @@ def modify_liquidity( position_id=position_id, liquidity_delta=liquidity_delta, hotkey=hotkey, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def move_stake( @@ -3634,23 +3636,24 @@ def remove_liquidity( netuid: int, position_id: int, hotkey: Optional[str] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Remove liquidity and credit balances back to wallet's hotkey stake. - Arguments: + Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. position_id: The id of the position record in the pool. - hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. Defaults to - `None`. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. + hotkey: The hotkey with staked TAO in Alpha. If not passed then the wallet hotkey is used. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -3659,7 +3662,7 @@ def remove_liquidity( Note: - Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity` - extrinsic to enable/disable user liquidity. + extrinsic to enable/disable user liquidity. - To get the `position_id` use `get_liquidity_list` method. """ return remove_liquidity_extrinsic( @@ -3668,9 +3671,10 @@ def remove_liquidity( netuid=netuid, position_id=position_id, hotkey=hotkey, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def reveal_weights( @@ -4244,21 +4248,23 @@ def toggle_user_liquidity( wallet: "Wallet", netuid: int, enable: bool, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Allow to toggle user liquidity for specified subnet. - Arguments: + Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. enable: Boolean indicating whether to enable user liquidity. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to False. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -4272,9 +4278,10 @@ def toggle_user_liquidity( wallet=wallet, netuid=netuid, enable=enable, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def transfer( diff --git a/migration.md b/migration.md index 69da29591f..a071676a01 100644 --- a/migration.md +++ b/migration.md @@ -167,3 +167,6 @@ Note: `raise_error` parameter is included in the list of parameters and paramete - [x] `.set_children_extrinsic` and `.root_set_pending_childkey_cooldown_extrinsic`. `subtensor.set_children` and `subtensor.root_set_pending_childkey_cooldown` methods. - [x] `.commit_reveal_extrinsic` and `subtensor.set_weights` - [x] `.add_liquidity_extrinsic` and `subtensor.add_liquidity` +- [x] `.modify_liquidity_extrinsic` and `subtensor.modify_liquidity` +- [x] `.remove_liquidity_extrinsic` and `subtensor.remove_liquidity` +- [x] `.toggle_user_liquidity_extrinsic` and `subtensor.toggle_user_liquidity` diff --git a/tests/unit_tests/extrinsics/asyncex/test_liquidity.py b/tests/unit_tests/extrinsics/asyncex/test_liquidity.py index 2d90ceaf0f..c572a518a9 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_liquidity.py +++ b/tests/unit_tests/extrinsics/asyncex/test_liquidity.py @@ -88,9 +88,10 @@ async def test_modify_liquidity_extrinsic(subtensor, fake_wallet, mocker): call=mocked_compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, use_nonce=True, period=None, + raise_error=False, ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -129,9 +130,10 @@ async def test_remove_liquidity_extrinsic(subtensor, fake_wallet, mocker): call=mocked_compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, use_nonce=True, period=None, + raise_error=False, ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -169,7 +171,8 @@ async def test_toggle_user_liquidity_extrinsic(subtensor, fake_wallet, mocker): call=mocked_compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/extrinsics/test_liquidity.py b/tests/unit_tests/extrinsics/test_liquidity.py index c626ce0b3c..ce60b34bd3 100644 --- a/tests/unit_tests/extrinsics/test_liquidity.py +++ b/tests/unit_tests/extrinsics/test_liquidity.py @@ -85,9 +85,10 @@ def test_modify_liquidity_extrinsic(subtensor, fake_wallet, mocker): call=mocked_compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, use_nonce=True, period=None, + raise_error=False, ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -125,9 +126,10 @@ def test_remove_liquidity_extrinsic(subtensor, fake_wallet, mocker): call=mocked_compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, use_nonce=True, period=None, + raise_error=False, ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -164,7 +166,8 @@ def test_toggle_user_liquidity_extrinsic(subtensor, fake_wallet, mocker): call=mocked_compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index dc37a8670b..a14b8afa72 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3732,8 +3732,9 @@ async def test_modify_liquidity(subtensor, fake_wallet, mocker): liquidity_delta=Balance.from_tao(150), hotkey=None, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value @@ -3763,8 +3764,9 @@ async def test_remove_liquidity(subtensor, fake_wallet, mocker): position_id=position_id, hotkey=None, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value @@ -3793,8 +3795,9 @@ async def test_toggle_user_liquidity(subtensor, fake_wallet, mocker): netuid=netuid, enable=enable, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 1d5fd377be..f5565caf7b 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3991,8 +3991,9 @@ def test_modify_liquidity(subtensor, fake_wallet, mocker): liquidity_delta=Balance.from_tao(150), hotkey=None, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value @@ -4021,8 +4022,9 @@ def test_remove_liquidity(subtensor, fake_wallet, mocker): position_id=position_id, hotkey=None, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value @@ -4050,8 +4052,9 @@ def test_toggle_user_liquidity(subtensor, fake_wallet, mocker): netuid=netuid, enable=enable, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value From 6993732b3e1a52de30d72c561539d7158fe24bfb Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 17:13:00 -0700 Subject: [PATCH 07/49] `.transfer_stake_extrinsic` and `subtensor.transfer_stake` --- bittensor/core/async_subtensor.py | 21 ++++--- .../core/extrinsics/asyncex/move_stake.py | 49 +++++++++------- bittensor/core/extrinsics/move_stake.py | 57 ++++++++++--------- bittensor/core/subtensor.py | 35 ++++++------ migration.md | 11 +++- tests/unit_tests/test_subtensor_extended.py | 3 +- 6 files changed, 101 insertions(+), 75 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index f224bb7e22..2507b740cb 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5527,25 +5527,27 @@ async def transfer_stake( origin_netuid: int, destination_netuid: int, amount: Balance, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Transfers stake from one subnet to another while changing the coldkey owner. - Arguments: + Parameters: wallet: The wallet to transfer stake from. destination_coldkey_ss58: The destination coldkey SS58 address. hotkey_ss58: The hotkey SS58 address associated with the stake. origin_netuid: The source subnet UID. destination_netuid: The destination subnet UID. amount: Amount to transfer. - wait_for_inclusion: If true, waits for inclusion before returning. - wait_for_finalization: If true, waits for finalization before returning. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: success: True if the transfer was successful. @@ -5559,9 +5561,10 @@ async def transfer_stake( origin_netuid=origin_netuid, destination_netuid=destination_netuid, amount=amount, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def unstake( diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index d52f86f868..e72b6dda40 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -45,26 +45,28 @@ async def transfer_stake_extrinsic( origin_netuid: int, destination_netuid: int, amount: Balance, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Transfers stake from one coldkey to another in the Bittensor network. - Args: - subtensor (AsyncSubtensor): The subtensor instance to interact with the blockchain. - wallet (Wallet): The wallet containing the coldkey to authorize the transfer. - destination_coldkey_ss58 (str): SS58 address of the destination coldkey. - hotkey_ss58 (str): SS58 address of the hotkey associated with the stake. - origin_netuid (int): Network UID of the origin subnet. - destination_netuid (int): Network UID of the destination subnet. - amount (Balance): The amount of stake to transfer as a `Balance` object. - wait_for_inclusion (bool): If True, waits for transaction inclusion in a block. Defaults to `True`. - wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to `False`. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: The subtensor instance to interact with the blockchain. + wallet: The wallet containing the coldkey to authorize the transfer. + destination_coldkey_ss58: SS58 address of the destination coldkey. + hotkey_ss58: SS58 address of the hotkey associated with the stake. + origin_netuid: Network UID of the origin subnet. + destination_netuid: Network UID of the destination subnet. + amount: The amount of stake to transfer as a `Balance` object. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: bool: True if the transfer was successful, False otherwise. @@ -108,12 +110,13 @@ async def transfer_stake_extrinsic( }, ) - success, err_msg = await subtensor.sign_and_send_extrinsic( + success, message = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: @@ -141,7 +144,7 @@ async def transfer_stake_extrinsic( return True else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + logging.error(f":cross_mark: [red]Failed[/red]: {message}") return False except Exception as e: @@ -156,12 +159,13 @@ async def swap_stake_extrinsic( origin_netuid: int, destination_netuid: int, amount: Balance, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Swaps stake from one subnet to another for a given hotkey in the Bittensor network. @@ -302,10 +306,11 @@ async def move_stake_extrinsic( destination_hotkey: str, destination_netuid: int, amount: Balance, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - period: Optional[int] = None, move_all_stake: bool = False, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Moves stake from one hotkey to another within subnets in the Bittensor network. diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index f3aae363ce..29067c7c70 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -41,27 +41,29 @@ def transfer_stake_extrinsic( hotkey_ss58: str, origin_netuid: int, destination_netuid: int, - amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, + amount: Balance, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Transfers stake from one subnet to another while changing the coldkey owner. - Args: - subtensor (Subtensor): Subtensor instance. - wallet (bittensor.wallet): The wallet to transfer stake from. - destination_coldkey_ss58 (str): The destination coldkey SS58 address. - hotkey_ss58 (str): The hotkey SS58 address associated with the stake. - origin_netuid (int): The source subnet UID. - destination_netuid (int): The destination subnet UID. - amount (Union[Balance, float, int]): Amount to transfer. - wait_for_inclusion (bool): If true, waits for inclusion before returning. - wait_for_finalization (bool): If true, waits for finalization before returning. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: The subtensor instance to interact with the blockchain. + wallet: The wallet containing the coldkey to authorize the transfer. + destination_coldkey_ss58: SS58 address of the destination coldkey. + hotkey_ss58: SS58 address of the hotkey associated with the stake. + origin_netuid: Network UID of the origin subnet. + destination_netuid: Network UID of the destination subnet. + amount: The amount of stake to transfer as a `Balance` object. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: success (bool): True if the transfer was successful. @@ -105,12 +107,13 @@ def transfer_stake_extrinsic( }, ) - success, err_msg = subtensor.sign_and_send_extrinsic( + success, message = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: @@ -121,7 +124,7 @@ def transfer_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_netuid=origin_netuid, @@ -138,7 +141,7 @@ def transfer_stake_extrinsic( return True else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + logging.error(f":cross_mark: [red]Failed[/red]: {message}") return False except Exception as e: @@ -152,13 +155,14 @@ def swap_stake_extrinsic( hotkey_ss58: str, origin_netuid: int, destination_netuid: int, - amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, + amount: Balance, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -297,11 +301,12 @@ def move_stake_extrinsic( origin_netuid: int, destination_hotkey: str, destination_netuid: int, - amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - period: Optional[int] = None, + amount: Balance, move_all_stake: bool = False, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Moves stake to a different hotkey and/or subnet while keeping the same coldkey owner. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 8929859462..d29d20fe8f 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4336,28 +4336,30 @@ def transfer_stake( origin_netuid: int, destination_netuid: int, amount: Balance, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Transfers stake from one subnet to another while changing the coldkey owner. - Args: - wallet (bittensor.wallet): The wallet to transfer stake from. - destination_coldkey_ss58 (str): The destination coldkey SS58 address. - hotkey_ss58 (str): The hotkey SS58 address associated with the stake. - origin_netuid (int): The source subnet UID. - destination_netuid (int): The destination subnet UID. - amount (Union[Balance, float, int]): Amount to transfer. - wait_for_inclusion (bool): If true, waits for inclusion before returning. - wait_for_finalization (bool): If true, waits for finalization before returning. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: The wallet to transfer stake from. + destination_coldkey_ss58: The destination coldkey SS58 address. + hotkey_ss58: The hotkey SS58 address associated with the stake. + origin_netuid: The source subnet UID. + destination_netuid: The destination subnet UID. + amount: Amount to transfer. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - success (bool): True if the transfer was successful. + success: True if the transfer was successful. """ amount = check_and_convert_to_balance(amount) return transfer_stake_extrinsic( @@ -4368,9 +4370,10 @@ def transfer_stake( origin_netuid=origin_netuid, destination_netuid=destination_netuid, amount=amount, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def unstake( diff --git a/migration.md b/migration.md index a071676a01..8c7c1cd459 100644 --- a/migration.md +++ b/migration.md @@ -163,10 +163,19 @@ It must include: - [x] obsolete extrinsic `set_root_weights_extrinsic` removed. Also related subtensor calls `subtensor.set_root_weights_extrinsic` removed too. # Standardize parameter order is applied for (extrinsics and related calls): -Note: `raise_error` parameter is included in the list of parameters and parameters order is standardized. + +These parameters will now exist in all extrinsics and related calls (default values could be different depends by extrinsic): + +```py +period: Optional[int] = None, +raise_error: bool = False, +wait_for_inclusion: bool = False, +wait_for_finalization: bool = False, +``` - [x] `.set_children_extrinsic` and `.root_set_pending_childkey_cooldown_extrinsic`. `subtensor.set_children` and `subtensor.root_set_pending_childkey_cooldown` methods. - [x] `.commit_reveal_extrinsic` and `subtensor.set_weights` - [x] `.add_liquidity_extrinsic` and `subtensor.add_liquidity` - [x] `.modify_liquidity_extrinsic` and `subtensor.modify_liquidity` - [x] `.remove_liquidity_extrinsic` and `subtensor.remove_liquidity` - [x] `.toggle_user_liquidity_extrinsic` and `subtensor.toggle_user_liquidity` +- [x] `.transfer_stake_extrinsic` and `subtensor.transfer_stake` diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index 1a53e4e7ba..162d0af8d7 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -15,6 +15,7 @@ from bittensor.utils import U16_MAX, U64_MAX from bittensor.utils.balance import Balance from tests.helpers.helpers import assert_submit_signed_extrinsic +from bittensor.core.extrinsics import move_stake @pytest.fixture @@ -1436,7 +1437,7 @@ def test_transfer_stake_error( "destination_netuid": 1, "alpha_amount": 1, }, - wait_for_finalization=False, + wait_for_finalization=True, wait_for_inclusion=True, ) From 7005ada04214e405e54f15ba0425e597141c1b6f Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 17:35:46 -0700 Subject: [PATCH 08/49] `.swap_stake_extrinsic` and `subtensor.swap_stake` --- bittensor/core/async_subtensor.py | 26 +++++----- .../core/extrinsics/asyncex/move_stake.py | 35 +++++++------ bittensor/core/extrinsics/move_stake.py | 33 +++++++------ bittensor/core/subtensor.py | 49 ++++++++++--------- migration.md | 1 + tests/unit_tests/test_subtensor.py | 2 + tests/unit_tests/test_subtensor_extended.py | 2 +- 7 files changed, 80 insertions(+), 68 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 2507b740cb..5695d180b8 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5379,36 +5379,37 @@ async def swap_stake( origin_netuid: int, destination_netuid: int, amount: Balance, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. Like subnet hopping - same owner, same hotkey, just changing which subnet the stake is in. - Arguments: + Parameters: wallet: The wallet to swap stake from. hotkey_ss58: The SS58 address of the hotkey whose stake is being swapped. origin_netuid: The netuid from which stake is removed. destination_netuid: The netuid to which stake is added. amount: The amount to swap. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - safe_staking: If true, enables price safety checks to protect against fluctuating prices. The swap will only - execute if the price ratio between subnets doesn't exceed the rate tolerance. Default is False. + safe_staking: If true, enables price safety checks to protect against fluctuating prices. The swap + will only execute if the price ratio between subnets doesn't exceed the rate tolerance. allow_partial_stake: If true and safe_staking is enabled, allows partial stake swaps when the full amount - would exceed the price threshold. If false, the entire swap fails if it would exceed the threshold. - Default is False. + would exceed the price tolerance. If false, the entire swap fails if it would exceed the tolerance. rate_tolerance: The maximum allowed increase in the price ratio between subnets (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when - safe_staking is True. Default is 0.005. + safe_staking is True. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: success: True if the extrinsic was successful. @@ -5428,12 +5429,13 @@ async def swap_stake( origin_netuid=origin_netuid, destination_netuid=destination_netuid, amount=amount, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) async def toggle_user_liquidity( diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index e72b6dda40..68edea571b 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -170,24 +170,26 @@ async def swap_stake_extrinsic( """ Swaps stake from one subnet to another for a given hotkey in the Bittensor network. - Args: - subtensor (AsyncSubtensor): The subtensor instance to interact with the blockchain. - wallet (Wallet): The wallet containing the coldkey to authorize the swap. - hotkey_ss58 (str): SS58 address of the hotkey associated with the stake. - origin_netuid (int): Network UID of the origin subnet. - destination_netuid (int): Network UID of the destination subnet. - amount (Balance): The amount of stake to swap as a `Balance` object. - wait_for_inclusion (bool): If True, waits for transaction inclusion in a block. Defaults to True. - wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to False. - safe_staking (bool): If true, enables price safety checks to protect against price impact. - allow_partial_stake (bool): If true, allows partial stake swaps when the full amount would exceed the price tolerance. - rate_tolerance (float): Maximum allowed increase in a price ratio (0.005 = 0.5%). - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: Subtensor instance. + wallet: The wallet to swap stake from. + hotkey_ss58: The hotkey SS58 address associated with the stake. + origin_netuid: The source subnet UID. + destination_netuid: The destination subnet UID. + amount: Amount to swap. + safe_staking: If true, enables price safety checks to protect against price impact. + allow_partial_stake: If true, allows partial stake swaps when the full amount would exceed the price tolerance. + rate_tolerance: Maximum allowed increase in a price ratio (0.005 = 0.5%). + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + Returns: - bool: True if the swap was successful, False otherwise. + success (bool): True if the swap was successful. """ amount.set_unit(netuid=origin_netuid) @@ -258,6 +260,7 @@ async def swap_stake_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index 29067c7c70..f2037509f8 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -167,21 +167,23 @@ def swap_stake_extrinsic( """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. - Args: - subtensor (Subtensor): Subtensor instance. - wallet (bittensor.wallet): The wallet to swap stake from. - hotkey_ss58 (str): The hotkey SS58 address associated with the stake. - origin_netuid (int): The source subnet UID. - destination_netuid (int): The destination subnet UID. - amount (Union[Balance, float]): Amount to swap. - wait_for_inclusion (bool): If true, waits for inclusion before returning. - wait_for_finalization (bool): If true, waits for finalization before returning. - safe_staking (bool): If true, enables price safety checks to protect against price impact. - allow_partial_stake (bool): If true, allows partial stake swaps when the full amount would exceed the price tolerance. - rate_tolerance (float): Maximum allowed increase in a price ratio (0.005 = 0.5%). - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: Subtensor instance. + wallet: The wallet to swap stake from. + hotkey_ss58: The hotkey SS58 address associated with the stake. + origin_netuid: The source subnet UID. + destination_netuid: The destination subnet UID. + amount: Amount to swap. + safe_staking: If true, enables price safety checks to protect against price impact. + allow_partial_stake: If true, allows partial stake swaps when the full amount would exceed the price tolerance. + rate_tolerance: Maximum allowed increase in a price ratio (0.005 = 0.5%). + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + Returns: success (bool): True if the swap was successful. @@ -254,6 +256,7 @@ def swap_stake_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index d29d20fe8f..78a48e5939 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4185,40 +4185,40 @@ def swap_stake( origin_netuid: int, destination_netuid: int, amount: Balance, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. Like subnet hopping - same owner, same hotkey, just changing which subnet the stake is in. - Args: - wallet (bittensor.wallet): The wallet to swap stake from. - hotkey_ss58 (str): The SS58 address of the hotkey whose stake is being swapped. - origin_netuid (int): The netuid from which stake is removed. - destination_netuid (int): The netuid to which stake is added. - amount (Union[Balance, float]): The amount to swap. - 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. - safe_staking (bool): If true, enables price safety checks to protect against fluctuating prices. The swap + Parameters: + wallet: The wallet to swap stake from. + hotkey_ss58: The SS58 address of the hotkey whose stake is being swapped. + origin_netuid: The netuid from which stake is removed. + destination_netuid: The netuid to which stake is added. + amount: The amount to swap. + safe_staking: If true, enables price safety checks to protect against fluctuating prices. The swap will only execute if the price ratio between subnets doesn't exceed the rate tolerance. - Default is False. - allow_partial_stake (bool): If true and safe_staking is enabled, allows partial stake swaps when - the full amount would exceed the price tolerance. If false, the entire swap fails if it would - exceed the tolerance. Default is False. - rate_tolerance (float): The maximum allowed increase in the price ratio between subnets - (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used - when safe_staking is True. Default is 0.005. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + allow_partial_stake: If true and safe_staking is enabled, allows partial stake swaps when the full amount + would exceed the price tolerance. If false, the entire swap fails if it would exceed the tolerance. + rate_tolerance: The maximum allowed increase in the price ratio between subnets + (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when + safe_staking is True. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): True if the extrinsic was successful. + success: True if the extrinsic was successful. The price ratio for swap_stake in safe mode is calculated as: origin_subnet_price / destination_subnet_price When safe_staking is enabled, the swap will only execute if: @@ -4235,12 +4235,13 @@ def swap_stake( origin_netuid=origin_netuid, destination_netuid=destination_netuid, amount=amount, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) def toggle_user_liquidity( diff --git a/migration.md b/migration.md index 8c7c1cd459..490c6b5d77 100644 --- a/migration.md +++ b/migration.md @@ -179,3 +179,4 @@ wait_for_finalization: bool = False, - [x] `.remove_liquidity_extrinsic` and `subtensor.remove_liquidity` - [x] `.toggle_user_liquidity_extrinsic` and `subtensor.toggle_user_liquidity` - [x] `.transfer_stake_extrinsic` and `subtensor.transfer_stake` +- [x] `.swap_stake_extrinsic` and `subtensor.swap_stake` \ No newline at end of file diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index f5565caf7b..17539a8232 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3056,6 +3056,7 @@ def test_swap_stake_success(mocker, subtensor, fake_wallet): allow_partial_stake=False, rate_tolerance=0.005, period=None, + raise_error=False, ) assert result == mock_swap_stake_extrinsic.return_value @@ -3101,6 +3102,7 @@ def test_swap_stake_with_safe_staking(mocker, subtensor, fake_wallet): allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, period=None, + raise_error=False, ) assert result == mock_swap_stake_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index 162d0af8d7..8765e0046f 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -1086,7 +1086,7 @@ def test_swap_stake(mock_substrate, subtensor, fake_wallet, mocker): "alpha_amount": 999, }, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, ) From e49d89d1342ebd35fdded4dbfd172c2a38005b85 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 17:55:31 -0700 Subject: [PATCH 09/49] `.move_stake_extrinsic` and `subtensor.move_stake` + renamed parameters --- bittensor/core/async_subtensor.py | 33 ++++++------ .../core/extrinsics/asyncex/move_stake.py | 54 ++++++++++--------- bittensor/core/extrinsics/move_stake.py | 38 ++++++------- bittensor/core/subtensor.py | 47 ++++++++-------- migration.md | 6 ++- tests/unit_tests/test_subtensor_extended.py | 22 ++++---- 6 files changed, 107 insertions(+), 93 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 5695d180b8..6a4dffc590 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4655,32 +4655,34 @@ async def modify_liquidity( async def move_stake( self, wallet: "Wallet", - origin_hotkey: str, + origin_hotkey_ss58: str, origin_netuid: int, - destination_hotkey: str, + destination_hotkey_ss58: str, destination_netuid: int, amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - period: Optional[int] = None, move_all_stake: bool = False, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Moves stake to a different hotkey and/or subnet. Arguments: wallet: The wallet to move stake from. - origin_hotkey: The SS58 address of the source hotkey. + origin_hotkey_ss58: The SS58 address of the source hotkey. origin_netuid: The netuid of the source subnet. - destination_hotkey: The SS58 address of the destination hotkey. + destination_hotkey_ss58: The SS58 address of the destination hotkey. destination_netuid: The netuid of the destination subnet. amount: Amount of stake to move. + move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. - move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. Returns: success: True if the stake movement was successful. @@ -4689,15 +4691,16 @@ async def move_stake( return await move_stake_extrinsic( subtensor=self, wallet=wallet, - origin_hotkey=origin_hotkey, + origin_hotkey_ss58=origin_hotkey_ss58, origin_netuid=origin_netuid, - destination_hotkey=destination_hotkey, + destination_hotkey_ss58=destination_hotkey_ss58, destination_netuid=destination_netuid, amount=amount, + move_all_stake=move_all_stake, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, - move_all_stake=move_all_stake, ) async def register( diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index 68edea571b..53018613c9 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -304,9 +304,9 @@ async def swap_stake_extrinsic( async def move_stake_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", - origin_hotkey: str, + origin_hotkey_ss58: str, origin_netuid: int, - destination_hotkey: str, + destination_hotkey_ss58: str, destination_netuid: int, amount: Balance, move_all_stake: bool = False, @@ -318,23 +318,24 @@ async def move_stake_extrinsic( """ Moves stake from one hotkey to another within subnets in the Bittensor network. - Args: - subtensor: The subtensor instance to interact with the blockchain. - wallet: The wallet containing the coldkey to authorize the move. - origin_hotkey: SS58 address of the origin hotkey associated with the stake. - origin_netuid: Network UID of the origin subnet. - destination_hotkey: SS58 address of the destination hotkey. - destination_netuid: Network UID of the destination subnet. - amount: The amount of stake to move as a `Balance` object. - wait_for_inclusion: If True, waits for transaction inclusion in a block. Defaults to True. - wait_for_finalization: If True, waits for transaction finalization. Defaults to False. + Parameters: + subtensor: Subtensor instance. + wallet: The wallet to move stake from. + origin_hotkey_ss58: The SS58 address of the source hotkey. + origin_netuid: The netuid of the source subnet. + destination_hotkey_ss58: The SS58 address of the destination hotkey. + destination_netuid: The netuid of the destination subnet. + amount: Amount to move. + move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the move was successful, False otherwise. + success: True if the move was successful. Otherwise, False. """ if not amount and not move_all_stake: logging.error( @@ -345,19 +346,19 @@ async def move_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( subtensor=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_hotkey_ss58=origin_hotkey_ss58, + destination_hotkey_ss58=destination_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 move_all_stake: amount = stake_in_origin elif stake_in_origin < amount: logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {origin_hotkey}. " + f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {origin_hotkey_ss58}. " f"Stake: {stake_in_origin}, amount: {amount}" ) return False @@ -366,7 +367,7 @@ async def move_stake_extrinsic( try: logging.info( - f"Moving stake from hotkey [blue]{origin_hotkey}[/blue] to hotkey [blue]{destination_hotkey}[/blue]\n" + f"Moving stake from hotkey [blue]{origin_hotkey_ss58}[/blue] to hotkey [blue]{destination_hotkey_ss58}[/blue]\n" f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " f"[yellow]{destination_netuid}[/yellow]" ) @@ -374,20 +375,21 @@ async def move_stake_extrinsic( call_module="SubtensorModule", call_function="move_stake", call_params={ - "origin_hotkey": origin_hotkey, + "origin_hotkey": origin_hotkey_ss58, "origin_netuid": origin_netuid, - "destination_hotkey": destination_hotkey, + "destination_hotkey": destination_hotkey_ss58, "destination_netuid": destination_netuid, "alpha_amount": amount.rao, }, ) - success, err_msg = await subtensor.sign_and_send_extrinsic( + success, message = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: @@ -399,8 +401,8 @@ async def move_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = await _get_stake_in_origin_and_dest( subtensor=subtensor, - origin_hotkey_ss58=origin_hotkey, - destination_hotkey_ss58=destination_hotkey, + origin_hotkey_ss58=origin_hotkey_ss58, + destination_hotkey_ss58=destination_hotkey_ss58, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, destination_coldkey_ss58=wallet.coldkeypub.ss58_address, origin_netuid=origin_netuid, @@ -415,7 +417,7 @@ async def move_stake_extrinsic( return True else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + logging.error(f":cross_mark: [red]Failed[/red]: {message}") return False except Exception as e: diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index f2037509f8..cbef3c8117 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -300,9 +300,9 @@ def swap_stake_extrinsic( def move_stake_extrinsic( subtensor: "Subtensor", wallet: "Wallet", - origin_hotkey: str, + origin_hotkey_ss58: str, origin_netuid: int, - destination_hotkey: str, + destination_hotkey_ss58: str, destination_netuid: int, amount: Balance, move_all_stake: bool = False, @@ -314,20 +314,21 @@ def move_stake_extrinsic( """ Moves stake to a different hotkey and/or subnet while keeping the same coldkey owner. - Args: + Parameters: subtensor: Subtensor instance. wallet: The wallet to move stake from. - origin_hotkey: The SS58 address of the source hotkey. + origin_hotkey_ss58: The SS58 address of the source hotkey. origin_netuid: The netuid of the source subnet. - destination_hotkey: The SS58 address of the destination hotkey. + destination_hotkey_ss58: The SS58 address of the destination hotkey. destination_netuid: The netuid of the destination subnet. amount: Amount to move. - wait_for_inclusion: If true, waits for inclusion before returning. - wait_for_finalization: If true, waits for finalization before returning. + move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: success: True if the move was successful. Otherwise, False. @@ -341,8 +342,8 @@ def move_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = _get_stake_in_origin_and_dest( subtensor=subtensor, - origin_hotkey_ss58=origin_hotkey, - destination_hotkey_ss58=destination_hotkey, + origin_hotkey_ss58=origin_hotkey_ss58, + destination_hotkey_ss58=destination_hotkey_ss58, origin_netuid=origin_netuid, destination_netuid=destination_netuid, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -353,7 +354,7 @@ def move_stake_extrinsic( elif stake_in_origin < amount: logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {origin_hotkey}. " + f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {origin_hotkey_ss58}. " f"Stake: {stake_in_origin}, amount: {amount}" ) return False @@ -362,27 +363,28 @@ def move_stake_extrinsic( try: logging.info( - f"Moving stake from hotkey [blue]{origin_hotkey}[/blue] to hotkey [blue]{destination_hotkey}[/blue]\n" + f"Moving stake from hotkey [blue]{origin_hotkey_ss58}[/blue] to hotkey [blue]{destination_hotkey_ss58}[/blue]\n" f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid [yellow]{destination_netuid}[/yellow]" ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", call_function="move_stake", call_params={ - "origin_hotkey": origin_hotkey, + "origin_hotkey": origin_hotkey_ss58, "origin_netuid": origin_netuid, - "destination_hotkey": destination_hotkey, + "destination_hotkey": destination_hotkey_ss58, "destination_netuid": destination_netuid, "alpha_amount": amount.rao, }, ) - success, err_msg = subtensor.sign_and_send_extrinsic( + success, message = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: @@ -394,8 +396,8 @@ def move_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = _get_stake_in_origin_and_dest( subtensor=subtensor, - origin_hotkey_ss58=origin_hotkey, - destination_hotkey_ss58=destination_hotkey, + origin_hotkey_ss58=origin_hotkey_ss58, + destination_hotkey_ss58=destination_hotkey_ss58, origin_netuid=origin_netuid, destination_netuid=destination_netuid, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -410,7 +412,7 @@ def move_stake_extrinsic( return True else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + logging.error(f":cross_mark: [red]Failed[/red]: {message}") return False except Exception as e: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 78a48e5939..ea75da65f8 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3490,49 +3490,52 @@ def modify_liquidity( def move_stake( self, wallet: "Wallet", - origin_hotkey: str, + origin_hotkey_ss58: str, origin_netuid: int, - destination_hotkey: str, + destination_hotkey_ss58: str, destination_netuid: int, amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - period: Optional[int] = None, move_all_stake: bool = False, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Moves stake to a different hotkey and/or subnet. - Args: - wallet (bittensor.wallet): The wallet to move stake from. - origin_hotkey (str): The SS58 address of the source hotkey. - origin_netuid (int): The netuid of the source subnet. - destination_hotkey (str): The SS58 address of the destination hotkey. - destination_netuid (int): The netuid of the destination subnet. - amount (Balance): Amount of stake to move. - 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. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: The wallet to move stake from. + origin_hotkey_ss58: The SS58 address of the source hotkey. + origin_netuid: The netuid of the source subnet. + destination_hotkey_ss58: The SS58 address of the destination hotkey. + destination_netuid: The netuid of the destination subnet. + amount: Amount of stake to move. move_all_stake: If true, moves all stake from the source hotkey to the destination hotkey. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - success (bool): True if the stake movement was successful. + success: True if the stake movement was successful. """ amount = check_and_convert_to_balance(amount) return move_stake_extrinsic( subtensor=self, wallet=wallet, - origin_hotkey=origin_hotkey, + origin_hotkey_ss58=origin_hotkey_ss58, origin_netuid=origin_netuid, - destination_hotkey=destination_hotkey, + destination_hotkey_ss58=destination_hotkey_ss58, destination_netuid=destination_netuid, amount=amount, + move_all_stake=move_all_stake, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, - move_all_stake=move_all_stake, ) def register( diff --git a/migration.md b/migration.md index 490c6b5d77..12532f857d 100644 --- a/migration.md +++ b/migration.md @@ -179,4 +179,8 @@ wait_for_finalization: bool = False, - [x] `.remove_liquidity_extrinsic` and `subtensor.remove_liquidity` - [x] `.toggle_user_liquidity_extrinsic` and `subtensor.toggle_user_liquidity` - [x] `.transfer_stake_extrinsic` and `subtensor.transfer_stake` -- [x] `.swap_stake_extrinsic` and `subtensor.swap_stake` \ No newline at end of file +- [x] `.swap_stake_extrinsic` and `subtensor.swap_stake` +- [x] `.move_stake_extrinsic` and `subtensor.move_stake` +- [x] `.move_stake_extrinsic` has renamed parameters: + - `origin_hotkey` to `origin_hotkey_ss58` + - `destination_hotkey` to `destination_hotkey_ss58` diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index 8765e0046f..ddb93dd529 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -696,10 +696,10 @@ def test_last_drand_round(mock_substrate, subtensor): ) def test_move_stake(mock_substrate, subtensor, fake_wallet, wait): success = subtensor.move_stake( - fake_wallet, - origin_hotkey="origin_hotkey", + wallet=fake_wallet, + origin_hotkey_ss58="origin_hotkey", origin_netuid=1, - destination_hotkey="destination_hotkey", + destination_hotkey_ss58="destination_hotkey", destination_netuid=2, amount=Balance(1), wait_for_finalization=wait, @@ -730,9 +730,9 @@ def test_move_stake_insufficient_stake(mock_substrate, subtensor, fake_wallet, m success = subtensor.move_stake( fake_wallet, - origin_hotkey="origin_hotkey", + origin_hotkey_ss58="origin_hotkey", origin_netuid=1, - destination_hotkey="destination_hotkey", + destination_hotkey_ss58="destination_hotkey", destination_netuid=2, amount=Balance(1), ) @@ -750,9 +750,9 @@ def test_move_stake_error(mock_substrate, subtensor, fake_wallet, mocker): success = subtensor.move_stake( fake_wallet, - origin_hotkey="origin_hotkey", + origin_hotkey_ss58="origin_hotkey", origin_netuid=1, - destination_hotkey="destination_hotkey", + destination_hotkey_ss58="destination_hotkey", destination_netuid=2, amount=Balance(1), ) @@ -771,7 +771,7 @@ def test_move_stake_error(mock_substrate, subtensor, fake_wallet, mocker): "destination_netuid": 2, "alpha_amount": 1, }, - wait_for_finalization=False, + wait_for_finalization=True, wait_for_inclusion=True, ) @@ -781,9 +781,9 @@ def test_move_stake_exception(mock_substrate, subtensor, fake_wallet): success = subtensor.move_stake( fake_wallet, - origin_hotkey="origin_hotkey", + origin_hotkey_ss58="origin_hotkey", origin_netuid=1, - destination_hotkey="destination_hotkey", + destination_hotkey_ss58="destination_hotkey", destination_netuid=2, amount=Balance(1), ) @@ -802,7 +802,7 @@ def test_move_stake_exception(mock_substrate, subtensor, fake_wallet): "destination_netuid": 2, "alpha_amount": 1, }, - wait_for_finalization=False, + wait_for_finalization=True, wait_for_inclusion=True, ) From e546c49d6ae3db5ae4c627750e17eb5977668d85 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 18:05:28 -0700 Subject: [PATCH 10/49] `.burned_register_extrinsic` and `subtensor.burned_register` --- bittensor/core/async_subtensor.py | 22 ++++++++------- .../core/extrinsics/asyncex/registration.py | 17 +++++------- bittensor/core/extrinsics/registration.py | 17 +++++------- bittensor/core/subtensor.py | 27 ++++++++++--------- migration.md | 1 + tests/unit_tests/test_async_subtensor.py | 4 +-- tests/unit_tests/test_subtensor_extended.py | 4 +-- 7 files changed, 45 insertions(+), 47 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 6a4dffc590..87efece5f3 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4455,26 +4455,27 @@ async def burned_register( self, wallet: "Wallet", netuid: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling TAO tokens, allowing them to be re-mined by performing work on the network. - Arguments: + Args: wallet: The wallet associated with the neuron to be registered. netuid: The unique identifier of the subnet. - wait_for_inclusion: Waits for the transaction to be included in a block. Defaults to - ``False``. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. Returns: - bool: `True` if the registration is successful, False otherwise. + bool: ``True`` if the registration is successful, False otherwise. """ async with self: if netuid == 0: @@ -4490,9 +4491,10 @@ async def burned_register( subtensor=self, wallet=wallet, netuid=netuid, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def commit_weights( diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 051c1badf8..a97b5e57ee 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -23,29 +23,26 @@ async def burned_register_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", netuid: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """Registers the wallet to chain by recycling TAO. - Args: + Parameters: subtensor: Subtensor instance. wallet: Bittensor wallet object. netuid: The ``netuid`` of the subnet to register on. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning ``True``, - or returns ``False`` if the extrinsic fails to be finalized within the timeout. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - raise_error: raises the relevant exception rather than returning `False` if unsuccessful. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is ``True``. + success: True if the extrinsic was successful. Otherwise, False. """ block_hash = await subtensor.substrate.get_chain_head() if not await subtensor.subnet_exists(netuid, block_hash=block_hash): diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index 730aeee2e6..e9949e0e2e 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -23,29 +23,26 @@ def burned_register_extrinsic( subtensor: "Subtensor", wallet: "Wallet", netuid: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """Registers the wallet to chain by recycling TAO. - Args: + Parameters: subtensor: Subtensor instance. wallet: Bittensor wallet object. netuid: The ``netuid`` of the subnet to register on. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning ``True``, - or returns ``False`` if the extrinsic fails to be finalized within the timeout. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - raise_error: raises the relevant exception rather than returning `False` if unsuccessful. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is ``True``. + success: True if the extrinsic was successful. Otherwise, False. """ block = subtensor.get_current_block() if not subtensor.subnet_exists(netuid, block=block): diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index ea75da65f8..b824fad069 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3291,24 +3291,24 @@ def burned_register( self, wallet: "Wallet", netuid: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling - TAO tokens, allowing them to be re-mined by performing work on the network. + TAO tokens, allowing them to be re-mined by performing work on the network. Args: - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron to be registered. - netuid (int): The unique identifier of the subnet. - wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. Defaults to - `False`. - wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. - Defaults to `True`. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + wallet: The wallet associated with the neuron to be registered. + netuid: The unique identifier of the subnet. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: bool: ``True`` if the registration is successful, False otherwise. @@ -3327,9 +3327,10 @@ def burned_register( subtensor=self, wallet=wallet, netuid=netuid, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def commit_weights( diff --git a/migration.md b/migration.md index 12532f857d..953d33d41b 100644 --- a/migration.md +++ b/migration.md @@ -184,3 +184,4 @@ wait_for_finalization: bool = False, - [x] `.move_stake_extrinsic` has renamed parameters: - `origin_hotkey` to `origin_hotkey_ss58` - `destination_hotkey` to `destination_hotkey_ss58` +- [x] `.burned_register_extrinsic` and `subtensor.burned_register` \ No newline at end of file diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index a14b8afa72..8c3603b285 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -203,7 +203,7 @@ async def test_burned_register(mock_substrate, subtensor, fake_wallet, mocker): "hotkey": fake_wallet.hotkey.ss58_address, }, wait_for_finalization=True, - wait_for_inclusion=False, + wait_for_inclusion=True, ) @@ -244,7 +244,7 @@ async def test_burned_register_on_root(mock_substrate, subtensor, fake_wallet, m "hotkey": fake_wallet.hotkey.ss58_address, }, wait_for_finalization=True, - wait_for_inclusion=False, + wait_for_inclusion=True, ) diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index ddb93dd529..ba535d1a3e 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -200,7 +200,7 @@ def test_burned_register(mock_substrate, subtensor, fake_wallet, mocker): "hotkey": fake_wallet.hotkey.ss58_address, }, wait_for_finalization=True, - wait_for_inclusion=False, + wait_for_inclusion=True, ) @@ -237,7 +237,7 @@ def test_burned_register_on_root(mock_substrate, subtensor, fake_wallet, mocker) "hotkey": fake_wallet.hotkey.ss58_address, }, wait_for_finalization=True, - wait_for_inclusion=False, + wait_for_inclusion=True, ) From bdbd1ba778e06f20afed269c36efa7e2ad1657b5 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 18:08:56 -0700 Subject: [PATCH 11/49] fix `test_move_stake_*` e2e tests --- tests/e2e_tests/test_staking.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index f9f86adc5b..08527ba8a7 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -1383,9 +1383,9 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): assert subtensor.staking.move_stake( wallet=alice_wallet, - origin_hotkey=alice_wallet.hotkey.ss58_address, + origin_hotkey_ss58=alice_wallet.hotkey.ss58_address, origin_netuid=alice_subnet_netuid, - destination_hotkey=bob_wallet.hotkey.ss58_address, + destination_hotkey_ss58=bob_wallet.hotkey.ss58_address, destination_netuid=bob_subnet_netuid, amount=stakes[0].stake, wait_for_finalization=True, @@ -1461,9 +1461,9 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): assert subtensor.staking.move_stake( wallet=dave_wallet, - origin_hotkey=dave_wallet.hotkey.ss58_address, + origin_hotkey_ss58=dave_wallet.hotkey.ss58_address, origin_netuid=bob_subnet_netuid, - destination_hotkey=bob_wallet.hotkey.ss58_address, + destination_hotkey_ss58=bob_wallet.hotkey.ss58_address, destination_netuid=bob_subnet_netuid, wait_for_inclusion=True, wait_for_finalization=True, @@ -1554,9 +1554,9 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ assert await async_subtensor.staking.move_stake( wallet=alice_wallet, - origin_hotkey=alice_wallet.hotkey.ss58_address, + origin_hotkey_ss58=alice_wallet.hotkey.ss58_address, origin_netuid=alice_subnet_netuid, - destination_hotkey=bob_wallet.hotkey.ss58_address, + destination_hotkey_ss58=bob_wallet.hotkey.ss58_address, destination_netuid=bob_subnet_netuid, amount=stakes[0].stake, wait_for_finalization=True, @@ -1635,9 +1635,9 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ assert await async_subtensor.staking.move_stake( wallet=dave_wallet, - origin_hotkey=dave_wallet.hotkey.ss58_address, + origin_hotkey_ss58=dave_wallet.hotkey.ss58_address, origin_netuid=bob_subnet_netuid, - destination_hotkey=bob_wallet.hotkey.ss58_address, + destination_hotkey_ss58=bob_wallet.hotkey.ss58_address, destination_netuid=bob_subnet_netuid, wait_for_inclusion=True, wait_for_finalization=True, From 97a677a60c193d1f8810ced374764fa62ad57174 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 18:12:21 -0700 Subject: [PATCH 12/49] docstring refactoring --- bittensor/core/async_subtensor.py | 5 +++++ bittensor/core/subtensor.py | 16 ++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 87efece5f3..60bca89c7e 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4422,6 +4422,7 @@ async def add_stake_multiple( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Adds stakes to multiple neurons identified by their hotkey SS58 addresses. @@ -4434,6 +4435,9 @@ async def add_stake_multiple( amounts: Corresponding amounts of TAO to stake for each hotkey. wait_for_inclusion: Waits for the transaction to be included in a block. Defaults to `True`. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Defaults to `False`. + period: The number of blocks during which the transaction will remain valid after it's + submitted. If the transaction is not included in a block within that number of blocks, it will expire + and be rejected. You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the staking is successful for all specified neurons, ``False`` otherwise. @@ -4449,6 +4453,7 @@ async def add_stake_multiple( amounts=amounts, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def burned_register( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index b824fad069..6e5370d4af 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3259,14 +3259,14 @@ def add_stake_multiple( Adds stakes to multiple neurons identified by their hotkey SS58 addresses. This bulk operation allows for efficient staking across different neurons from a single wallet. - 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[int]): List of network UIDs to stake to. - 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. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's + Parameters: + wallet: The wallet used for staking. + hotkey_ss58s: List of ``SS58`` addresses of hotkeys to stake to. + netuids: List of network UIDs to stake to. + amounts: Corresponding amounts of TAO to stake for each hotkey. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. From 31cce625510c22658514eacc14dbf638253e91d1 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 18:22:15 -0700 Subject: [PATCH 13/49] `.register_subnet_extrinsic` and `subtensor.register_subnet` --- bittensor/core/async_subtensor.py | 21 +++++++++-------- .../core/extrinsics/asyncex/registration.py | 23 +++++++++++-------- bittensor/core/extrinsics/registration.py | 23 +++++++++++-------- bittensor/core/subtensor.py | 23 ++++++++++--------- migration.md | 3 ++- 5 files changed, 51 insertions(+), 42 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 60bca89c7e..aa407e8e86 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4776,22 +4776,22 @@ async def register( async def register_subnet( self: "AsyncSubtensor", wallet: "Wallet", + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor network. - Arguments: + Parameters: wallet: The wallet to be used for subnet registration. - wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, - os `False` if the extrinsic fails to enter the block within the timeout. Default is `False`. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning - true, or returns false if the extrinsic fails to be finalized within the timeout. Default is `False`. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: bool: True if the subnet registration was successful, False otherwise. @@ -4799,9 +4799,10 @@ async def register_subnet( return await register_subnet_extrinsic( subtensor=self, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def remove_liquidity( diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index a97b5e57ee..f50b4d0e22 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -136,21 +136,23 @@ async def burned_register_extrinsic( async def register_subnet_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Registers a new subnetwork on the Bittensor blockchain asynchronously. - Args: - subtensor (AsyncSubtensor): The async subtensor interface to send the extrinsic. - wallet (Wallet): The wallet to be used for subnet registration. - wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning true. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: The subtensor interface to send the extrinsic. + wallet: The wallet to be used for subnet registration. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: bool: True if the subnet registration was successful, False otherwise. @@ -179,6 +181,7 @@ async def register_subnet_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index e9949e0e2e..82f7314cb4 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -128,21 +128,23 @@ def burned_register_extrinsic( def register_subnet_extrinsic( subtensor: "Subtensor", wallet: "Wallet", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Registers a new subnetwork on the Bittensor blockchain. - Args: - subtensor (Subtensor): The subtensor interface to send the extrinsic. - wallet (Wallet): The wallet to be used for subnet registration. - wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning true. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: The subtensor interface to send the extrinsic. + wallet: The wallet to be used for subnet registration. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: bool: True if the subnet registration was successful, False otherwise. @@ -171,6 +173,7 @@ def register_subnet_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 6e5370d4af..10bde6e8be 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3606,22 +3606,22 @@ def register( def register_subnet( self, wallet: "Wallet", + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor network. - Args: - wallet (bittensor_wallet.Wallet): The wallet to be used for subnet registration. - 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. Default is `False`. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. Default is `True`. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: The wallet to be used for subnet registration. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: bool: True if the subnet registration was successful, False otherwise. @@ -3629,9 +3629,10 @@ def register_subnet( return register_subnet_extrinsic( subtensor=self, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def remove_liquidity( diff --git a/migration.md b/migration.md index 953d33d41b..766b8adbfd 100644 --- a/migration.md +++ b/migration.md @@ -184,4 +184,5 @@ wait_for_finalization: bool = False, - [x] `.move_stake_extrinsic` has renamed parameters: - `origin_hotkey` to `origin_hotkey_ss58` - `destination_hotkey` to `destination_hotkey_ss58` -- [x] `.burned_register_extrinsic` and `subtensor.burned_register` \ No newline at end of file +- [x] `.burned_register_extrinsic` and `subtensor.burned_register` +- [x] `.register_subnet_extrinsic` and `subtensor.register_subnet` \ No newline at end of file From 22e0c0a14fcdf66dd082dd3e78a70d320cb8f1aa Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 18:31:35 -0700 Subject: [PATCH 14/49] fix registration subnets in e2e tests --- bittensor/core/async_subtensor.py | 2 +- bittensor/core/subtensor.py | 2 +- tests/e2e_tests/test_commitment.py | 2 +- tests/e2e_tests/test_delegate.py | 14 +++----- tests/e2e_tests/test_dendrite.py | 10 +++--- tests/e2e_tests/test_hotkeys.py | 8 ++--- tests/e2e_tests/test_incentive.py | 6 ++-- tests/e2e_tests/test_metagraph.py | 10 +++--- tests/e2e_tests/test_reveal_commitments.py | 4 +-- tests/e2e_tests/test_staking.py | 40 ++++++++++----------- tests/e2e_tests/test_subnets.py | 12 ++----- tests/e2e_tests/test_subtensor_functions.py | 4 +-- 12 files changed, 48 insertions(+), 66 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index aa407e8e86..e3ef9279ec 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4778,7 +4778,7 @@ async def register_subnet( wallet: "Wallet", period: Optional[int] = None, raise_error: bool = False, - wait_for_inclusion: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ) -> bool: """ diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 10bde6e8be..f495cd8199 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3608,7 +3608,7 @@ def register_subnet( wallet: "Wallet", period: Optional[int] = None, raise_error: bool = False, - wait_for_inclusion: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ) -> bool: """ diff --git a/tests/e2e_tests/test_commitment.py b/tests/e2e_tests/test_commitment.py index 860601f075..d16c9a7104 100644 --- a/tests/e2e_tests/test_commitment.py +++ b/tests/e2e_tests/test_commitment.py @@ -16,7 +16,7 @@ def test_commitment(subtensor, alice_wallet, dave_wallet): dave_subnet_netuid = 2 - assert subtensor.subnets.register_subnet(dave_wallet, True, True) + assert subtensor.subnets.register_subnet(dave_wallet) assert subtensor.subnets.subnet_exists(dave_subnet_netuid), ( "Subnet wasn't created successfully" ) diff --git a/tests/e2e_tests/test_delegate.py b/tests/e2e_tests/test_delegate.py index a63bfaab39..1b5c136c04 100644 --- a/tests/e2e_tests/test_delegate.py +++ b/tests/e2e_tests/test_delegate.py @@ -663,11 +663,7 @@ def test_nominator_min_required_stake(subtensor, alice_wallet, bob_wallet, dave_ alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 # Register a subnet, netuid 2 - assert subtensor.subnets.register_subnet( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ), "Subnet wasn't created" + assert subtensor.subnets.register_subnet(alice_wallet), "Subnet wasn't created" # Verify subnet created successfully assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -752,11 +748,9 @@ async def test_nominator_min_required_stake_async( alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 # Register a subnet, netuid 2 - assert await async_subtensor.subnets.register_subnet( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ), "Subnet wasn't created" + assert await async_subtensor.subnets.register_subnet(alice_wallet), ( + "Subnet wasn't created" + ) # Verify subnet created successfully assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( diff --git a/tests/e2e_tests/test_dendrite.py b/tests/e2e_tests/test_dendrite.py index 841e349557..c4a8085444 100644 --- a/tests/e2e_tests/test_dendrite.py +++ b/tests/e2e_tests/test_dendrite.py @@ -35,9 +35,7 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 # Register a subnet, netuid 2 - assert subtensor.subnets.register_subnet(alice_wallet, True, True), ( - "Subnet wasn't created." - ) + assert subtensor.subnets.register_subnet(alice_wallet), "Subnet wasn't created." # Verify subnet created successfully assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -168,9 +166,9 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 # Register a subnet, netuid 2 - assert await async_subtensor.subnets.register_subnet( - alice_wallet, wait_for_inclusion=True, wait_for_finalization=True - ), "Subnet wasn't created" + assert await async_subtensor.subnets.register_subnet(alice_wallet), ( + "Subnet wasn't created" + ) # Verify subnet created successfully assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index 47c2b94ce6..f91d876da6 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -34,7 +34,7 @@ def test_hotkeys(subtensor, alice_wallet, dave_wallet): logging.console.info("Testing [green]test_hotkeys[/green].") dave_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 - assert subtensor.subnets.register_subnet(dave_wallet, True, True) + assert subtensor.subnets.register_subnet(dave_wallet) assert subtensor.subnets.subnet_exists(dave_subnet_netuid), ( f"Subnet #{dave_subnet_netuid} does not exist." ) @@ -90,7 +90,7 @@ async def test_hotkeys_async(async_subtensor, alice_wallet, dave_wallet): logging.console.info("Testing [green]test_hotkeys_async[/green].") dave_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 - assert await async_subtensor.subnets.register_subnet(dave_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(dave_wallet) assert await async_subtensor.subnets.subnet_exists(dave_subnet_netuid), ( f"Subnet #{dave_subnet_netuid} does not exist." ) @@ -165,7 +165,7 @@ def test_children(subtensor, alice_wallet, bob_wallet, dave_wallet): == "Success with `root_set_pending_childkey_cooldown_extrinsic` response." ) - assert subtensor.subnets.register_subnet(dave_wallet, True, True) + assert subtensor.subnets.register_subnet(dave_wallet) assert subtensor.subnets.subnet_exists(dave_subnet_netuid), ( f"Subnet #{dave_subnet_netuid} does not exist." ) @@ -490,7 +490,7 @@ async def test_children_async(async_subtensor, alice_wallet, bob_wallet, dave_wa == "Success with `root_set_pending_childkey_cooldown_extrinsic` response." ) - assert await async_subtensor.subnets.register_subnet(dave_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(dave_wallet) assert await async_subtensor.subnets.subnet_exists(dave_subnet_netuid), ( f"Subnet #{dave_subnet_netuid} does not exist." ) diff --git a/tests/e2e_tests/test_incentive.py b/tests/e2e_tests/test_incentive.py index 70cda9b48b..0cee9beb8f 100644 --- a/tests/e2e_tests/test_incentive.py +++ b/tests/e2e_tests/test_incentive.py @@ -35,9 +35,7 @@ async def test_incentive(subtensor, templates, alice_wallet, bob_wallet): alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 # Register root as Alice - the subnet owner and validator - assert subtensor.subnets.register_subnet(alice_wallet, True, True), ( - "Subnet wasn't created" - ) + assert subtensor.subnets.register_subnet(alice_wallet), "Subnet wasn't created" # Verify subnet created successfully assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -208,7 +206,7 @@ async def test_incentive_async(async_subtensor, templates, alice_wallet, bob_wal alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 # Register root as Alice - the subnet owner and validator - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True), ( + assert await async_subtensor.subnets.register_subnet(alice_wallet), ( "Subnet wasn't created" ) diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index fba4e6fea7..6f7279abc4 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -56,7 +56,7 @@ def test_metagraph(subtensor, alice_wallet, bob_wallet, dave_wallet): alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 logging.console.info("Register the subnet through Alice") - assert subtensor.subnets.register_subnet(alice_wallet, True, True), ( + assert subtensor.subnets.register_subnet(alice_wallet), ( "Unable to register the subnet" ) @@ -375,7 +375,7 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): logging.console.info("Testing [blue]test_metagraph_info[/blue]") alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 - assert subtensor.subnets.register_subnet(alice_wallet, True, True) + assert subtensor.subnets.register_subnet(alice_wallet) metagraph_info = subtensor.metagraphs.get_metagraph_info(netuid=1, block=1) @@ -635,7 +635,7 @@ async def test_metagraph_info_async(async_subtensor, alice_wallet, bob_wallet): logging.console.info("Testing [blue]test_metagraph_info_async[/blue]") alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(alice_wallet) metagraph_info = await async_subtensor.metagraphs.get_metagraph_info( netuid=1, block=1 @@ -900,7 +900,7 @@ def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): logging.console.info("Testing [blue]test_metagraph_info_with_indexes[/blue]") alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 - assert subtensor.subnets.register_subnet(alice_wallet, True, True) + assert subtensor.subnets.register_subnet(alice_wallet) field_indices = [ SelectiveMetagraphIndex.Name, @@ -1136,7 +1136,7 @@ async def test_metagraph_info_with_indexes_async( logging.console.info("Testing [blue]test_metagraph_info_with_indexes_async[/blue]") alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(alice_wallet) field_indices = [ SelectiveMetagraphIndex.Name, diff --git a/tests/e2e_tests/test_reveal_commitments.py b/tests/e2e_tests/test_reveal_commitments.py index 97b60373fd..840d3cdf1d 100644 --- a/tests/e2e_tests/test_reveal_commitments.py +++ b/tests/e2e_tests/test_reveal_commitments.py @@ -38,7 +38,7 @@ def test_set_reveal_commitment(subtensor, alice_wallet, bob_wallet): logging.console.info("Testing Drand encrypted commitments.") # Register subnet as Alice - assert subtensor.subnets.register_subnet(alice_wallet, True, True), ( + assert subtensor.subnets.register_subnet(alice_wallet), ( "Unable to register the subnet" ) @@ -152,7 +152,7 @@ async def test_set_reveal_commitment(async_subtensor, alice_wallet, bob_wallet): logging.console.info("Testing Drand encrypted commitments.") # Register subnet as Alice - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True), ( + assert await async_subtensor.subnets.register_subnet(alice_wallet), ( "Unable to register the subnet" ) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 08527ba8a7..c51272ef00 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -29,7 +29,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 # Register root as Alice - the subnet owner and validator - assert subtensor.subnets.register_subnet(alice_wallet, True, True) + assert subtensor.subnets.register_subnet(alice_wallet) # Verify subnet created successfully assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -205,7 +205,7 @@ async def test_single_operation_async(async_subtensor, alice_wallet, bob_wallet) alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 # Register root as Alice - the subnet owner and validator - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(alice_wallet) # Verify subnet created successfully assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -694,7 +694,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 # Register root as Alice - the subnet owner and validator - assert subtensor.extrinsics.register_subnet(alice_wallet, True, True) + assert subtensor.extrinsics.register_subnet(alice_wallet) # Verify subnet created successfully assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -893,7 +893,7 @@ async def test_safe_staking_scenarios_async( alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 # Register root as Alice - the subnet owner and validator - assert await async_subtensor.extrinsics.register_subnet(alice_wallet, True, True) + assert await async_subtensor.extrinsics.register_subnet(alice_wallet) # Verify subnet created successfully assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -1093,12 +1093,12 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): # Create new subnet (netuid 2) and register Alice origin_netuid = 2 - assert subtensor.subnets.register_subnet(bob_wallet, True, True) + assert subtensor.subnets.register_subnet(bob_wallet) assert subtensor.subnets.subnet_exists(origin_netuid), ( "Subnet wasn't created successfully" ) dest_netuid = 3 - assert subtensor.subnets.register_subnet(bob_wallet, True, True) + assert subtensor.subnets.register_subnet(bob_wallet) assert subtensor.subnets.subnet_exists(dest_netuid), ( "Subnet wasn't created successfully" ) @@ -1213,12 +1213,12 @@ async def test_safe_swap_stake_scenarios_async( # Create new subnet (netuid 2) and register Alice origin_netuid = 2 - assert await async_subtensor.subnets.register_subnet(bob_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(bob_wallet) assert await async_subtensor.subnets.subnet_exists(origin_netuid), ( "Subnet wasn't created successfully" ) dest_netuid = 3 - assert await async_subtensor.subnets.register_subnet(bob_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(bob_wallet) assert await async_subtensor.subnets.subnet_exists(dest_netuid), ( "Subnet wasn't created successfully" ) @@ -1328,7 +1328,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): logging.console.info("Testing [blue]test_move_stake[/blue]") alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 - assert subtensor.subnets.register_subnet(alice_wallet, True, True) + assert subtensor.subnets.register_subnet(alice_wallet) assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1360,7 +1360,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): ] bob_subnet_netuid = subtensor.subnets.get_total_subnets() # 3 - subtensor.subnets.register_subnet(bob_wallet, True, True) + subtensor.subnets.register_subnet(bob_wallet) assert subtensor.subnets.subnet_exists(bob_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1493,7 +1493,7 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ logging.console.info("Testing [blue]test_move_stake_async[/blue]") alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(alice_wallet) assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1529,7 +1529,7 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ ] bob_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 3 - await async_subtensor.subnets.register_subnet(bob_wallet, True, True) + await async_subtensor.subnets.register_subnet(bob_wallet) assert await async_subtensor.subnets.subnet_exists(bob_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1666,7 +1666,7 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 - assert subtensor.subnets.register_subnet(alice_wallet, True, True) + assert subtensor.subnets.register_subnet(alice_wallet) assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1715,7 +1715,7 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): assert bob_stakes == [] dave_subnet_netuid = subtensor.subnets.get_total_subnets() # 3 - subtensor.subnets.register_subnet(dave_wallet, True, True) + subtensor.subnets.register_subnet(dave_wallet) assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) @@ -1799,7 +1799,7 @@ async def test_transfer_stake_async( alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(alice_wallet) assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1850,7 +1850,7 @@ async def test_transfer_stake_async( assert bob_stakes == [] dave_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 3 - await async_subtensor.subnets.register_subnet(dave_wallet, True, True) + await async_subtensor.subnets.register_subnet(dave_wallet) assert await async_wait_to_start_call( async_subtensor, dave_wallet, dave_subnet_netuid @@ -1942,7 +1942,7 @@ def test_unstaking_with_limit( # Register first SN alice_subnet_netuid_2 = subtensor.subnets.get_total_subnets() # 2 - assert subtensor.subnets.register_subnet(alice_wallet, True, True) + assert subtensor.subnets.register_subnet(alice_wallet) assert subtensor.subnets.subnet_exists(alice_subnet_netuid_2), ( "Subnet wasn't created successfully" ) @@ -1966,7 +1966,7 @@ def test_unstaking_with_limit( # Register second SN alice_subnet_netuid_3 = subtensor.subnets.get_total_subnets() # 3 - assert subtensor.subnets.register_subnet(alice_wallet, True, True) + assert subtensor.subnets.register_subnet(alice_wallet) assert subtensor.subnets.subnet_exists(alice_subnet_netuid_3), ( "Subnet wasn't created successfully" ) @@ -2070,7 +2070,7 @@ async def test_unstaking_with_limit_async( # Register first SN alice_subnet_netuid_2 = await async_subtensor.subnets.get_total_subnets() # 2 - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(alice_wallet) assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid_2), ( "Subnet wasn't created successfully" ) @@ -2096,7 +2096,7 @@ async def test_unstaking_with_limit_async( # Register second SN alice_subnet_netuid_3 = await async_subtensor.subnets.get_total_subnets() # 3 - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True) + assert await async_subtensor.subnets.register_subnet(alice_wallet) assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid_3), ( "Subnet wasn't created successfully" ) diff --git a/tests/e2e_tests/test_subnets.py b/tests/e2e_tests/test_subnets.py index 8b28206fdc..89028d2d06 100644 --- a/tests/e2e_tests/test_subnets.py +++ b/tests/e2e_tests/test_subnets.py @@ -14,11 +14,7 @@ def test_subnets(subtensor, alice_wallet): subnets = subtensor.subnets.all_subnets() assert len(subnets) == 2 - subtensor.subnets.register_subnet( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + subtensor.subnets.register_subnet(alice_wallet) subnets = subtensor.subnets.all_subnets() assert len(subnets) == 3 @@ -50,11 +46,7 @@ async def test_subnets_async(async_subtensor, alice_wallet): subnets = await async_subtensor.subnets.all_subnets() assert len(subnets) == 2 - assert await async_subtensor.subnets.register_subnet( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + assert await async_subtensor.subnets.register_subnet(alice_wallet) subnets = await async_subtensor.subnets.all_subnets() assert len(subnets) == 3 diff --git a/tests/e2e_tests/test_subtensor_functions.py b/tests/e2e_tests/test_subtensor_functions.py index 11342caec5..669b929d3f 100644 --- a/tests/e2e_tests/test_subtensor_functions.py +++ b/tests/e2e_tests/test_subtensor_functions.py @@ -68,7 +68,7 @@ async def test_subtensor_extrinsics(subtensor, templates, alice_wallet, bob_wall pre_subnet_creation_cost = subtensor.subnets.get_subnet_burn_cost() # Register subnet - assert subtensor.subnets.register_subnet(alice_wallet, True, True), ( + assert subtensor.subnets.register_subnet(alice_wallet), ( "Unable to register the subnet" ) @@ -259,7 +259,7 @@ async def test_subtensor_extrinsics_async( pre_subnet_creation_cost = await async_subtensor.subnets.get_subnet_burn_cost() # Register subnet - assert await async_subtensor.subnets.register_subnet(alice_wallet, True, True), ( + assert await async_subtensor.subnets.register_subnet(alice_wallet), ( "Unable to register the subnet" ) From 8b0ae8986c3dbf69481f46025766071cff256cc0 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 18:48:25 -0700 Subject: [PATCH 15/49] fix registration subnets in e2e tests --- bittensor/core/subtensor.py | 4 ++-- tests/helpers/helpers.py | 2 +- tests/unit_tests/test_subtensor_extended.py | 12 +++++------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index f495cd8199..25be51f2ed 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3543,7 +3543,7 @@ def register( self, wallet: "Wallet", netuid: int, - wait_for_inclusion: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, max_allowed_attempts: int = 3, output_in_place: bool = True, @@ -3752,7 +3752,7 @@ def reveal_weights( def root_register( self, wallet: "Wallet", - wait_for_inclusion: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, period: Optional[int] = None, ) -> bool: diff --git a/tests/helpers/helpers.py b/tests/helpers/helpers.py index eccf716811..6f94196273 100644 --- a/tests/helpers/helpers.py +++ b/tests/helpers/helpers.py @@ -84,7 +84,7 @@ def assert_submit_signed_extrinsic( call_params: Optional[dict] = None, era: Optional[dict] = None, nonce: Optional[int] = None, - wait_for_inclusion: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ): substrate.compose_call.assert_called_with( diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index ba535d1a3e..1f0449cc19 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -1206,15 +1206,13 @@ def test_register_subnet(mock_substrate, subtensor, fake_wallet, mocker, success is_success=success, ) - result = subtensor.register_subnet( - fake_wallet, - ) + result = subtensor.register_subnet(fake_wallet) assert result is success assert_submit_signed_extrinsic( - mock_substrate, - fake_wallet.coldkey, + substrate=mock_substrate, + keypair=fake_wallet.coldkey, call_module="SubtensorModule", call_function="register_network", call_params={ @@ -1264,8 +1262,8 @@ def test_root_register(mock_substrate, subtensor, fake_wallet, mocker): ) assert_submit_signed_extrinsic( - mock_substrate, - fake_wallet.coldkey, + substrate=mock_substrate, + keypair=fake_wallet.coldkey, call_module="SubtensorModule", call_function="root_register", call_params={ From f792c4e5cf35ef1038023aab2879bc6aadd50ff9 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 19:04:33 -0700 Subject: [PATCH 16/49] `.register_extrinsic` and `subtensor.register` --- bittensor/core/async_subtensor.py | 45 ++++++++-------- .../core/extrinsics/asyncex/registration.py | 44 +++++++-------- bittensor/core/extrinsics/registration.py | 40 +++++++------- bittensor/core/subtensor.py | 54 ++++++++++--------- migration.md | 3 +- .../extrinsics/asyncex/test_registration.py | 3 ++ tests/unit_tests/test_async_subtensor.py | 7 +-- 7 files changed, 104 insertions(+), 92 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index e3ef9279ec..bd2a098299 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4714,8 +4714,6 @@ async def register( self: "AsyncSubtensor", wallet: "Wallet", netuid: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, max_allowed_attempts: int = 3, output_in_place: bool = False, cuda: bool = False, @@ -4725,33 +4723,37 @@ async def register( update_interval: Optional[int] = None, log_verbose: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ): """ - Registers a neuron on the Bittensor network using the provided wallet. + Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. Registration is a critical step for a neuron to become an active participant in the network, enabling it to stake, set weights, and receive incentives. - Arguments: + Parameters: wallet: The wallet associated with the neuron to be registered. - netuid: unique identifier of the subnet. - wait_for_inclusion: Waits for the transaction to be included in a block. Defaults to `False`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Defaults to + netuid: The unique identifier of the subnet. max_allowed_attempts: Maximum number of attempts to register the wallet. - output_in_place: If true, prints the progress of the proof of work to the console in-place. Meaning - the progress is printed on the same lines. Defaults to `True`. - cuda: If `true`, the wallet should be registered using CUDA device(s). Defaults to `False`. - dev_id: The CUDA device id to use, or a list of device ids. Defaults to `0` (zero). - tpb: The number of threads per block (CUDA). Default to `256`. - num_processes: The number of processes to use to register. Default to `None`. - update_interval: The number of nonces to solve between updates. Default to `None`. - log_verbose: If `true`, the registration process will log more information. Default to `False`. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + output_in_place: If true, prints the progress of the proof of work to the console in-place. Meaning the + progress is printed on the same lines. + cuda: If ``true``, the wallet should be registered using CUDA device(s). + dev_id: The CUDA device id to use, or a list of device ids. + tpb: The number of threads per block (CUDA). + num_processes: The number of processes to use to register. + update_interval: The number of nonces to solve between updates. + log_verbose: If ``true``, the registration process will log more information. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: `True` if the registration is successful, False otherwise. + bool: ``True`` if the registration is successful, False otherwise. This function facilitates the entry of new neurons into the network, supporting the decentralized growth and scalability of the Bittensor ecosystem. @@ -4760,8 +4762,6 @@ async def register( subtensor=self, wallet=wallet, netuid=netuid, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, max_allowed_attempts=max_allowed_attempts, tpb=tpb, update_interval=update_interval, @@ -4771,6 +4771,9 @@ async def register( output_in_place=output_in_place, log_verbose=log_verbose, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) async def register_subnet( diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index f50b4d0e22..bbb1afa1d7 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -201,8 +201,6 @@ async def register_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", netuid: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, max_allowed_attempts: int = 3, output_in_place: bool = True, cuda: bool = False, @@ -212,33 +210,36 @@ async def register_extrinsic( update_interval: Optional[int] = None, log_verbose: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Registers the wallet to the chain. - - Args: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): initialized AsyncSubtensor object to use for chain - interactions - wallet (bittensor_wallet.Wallet): Bittensor wallet object. - netuid (int): The ``netuid`` of the subnet to register on. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - max_allowed_attempts (int): Maximum number of attempts to register the wallet. - output_in_place (bool): Whether the POW solving should be outputted to the console as it goes along. - cuda (bool): If `True`, the wallet should be registered using CUDA device(s). + """Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. + + Registration is a critical step for a neuron to become an active participant in the network, enabling it to stake, + set weights, and receive incentives. + + Parameters: + subtensor: Subtensor object to use for chain interactions + wallet: Bittensor wallet object. + netuid: The ``netuid`` of the subnet to register on. + max_allowed_attempts: Maximum number of attempts to register the wallet. + output_in_place: Whether the POW solving should be outputted to the console as it goes along. + cuda: If `True`, the wallet should be registered using CUDA device(s). dev_id: The CUDA device id to use, or a list of device ids. tpb: The number of threads per block (CUDA). num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the - response is `True`. + bool: True if the subnet registration was successful, False otherwise. """ block_hash = await subtensor.substrate.get_chain_head() logging.debug("[magenta]Checking subnet status... [/magenta]") @@ -343,6 +344,7 @@ async def register_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not success: diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index 82f7314cb4..4d9af329ee 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -193,8 +193,6 @@ def register_extrinsic( subtensor: "Subtensor", wallet: "Wallet", netuid: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, max_allowed_attempts: int = 3, output_in_place: bool = True, cuda: bool = False, @@ -204,32 +202,33 @@ def register_extrinsic( update_interval: Optional[int] = None, log_verbose: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Registers the wallet to the chain. - - Args: - subtensor (bittensor.core.subtensor.Subtensor): Subtensor object to use for chain interactions - wallet (bittensor_wallet.Wallet): Bittensor wallet object. - netuid (int): The ``netuid`` of the subnet to register on. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - max_allowed_attempts (int): Maximum number of attempts to register the wallet. - output_in_place (bool): Whether the POW solving should be outputted to the console as it goes along. - cuda (bool): If `True`, the wallet should be registered using CUDA device(s). + """Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. + + Parameters: + subtensor: Subtensor object to use for chain interactions + wallet: Bittensor wallet object. + netuid: The ``netuid`` of the subnet to register on. + max_allowed_attempts: Maximum number of attempts to register the wallet. + output_in_place: Whether the POW solving should be outputted to the console as it goes along. + cuda: If `True`, the wallet should be registered using CUDA device(s). dev_id: The CUDA device id to use, or a list of device ids. tpb: The number of threads per block (CUDA). num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the - response is `True`. + bool: True if the subnet registration was successful, False otherwise. """ logging.debug("[magenta]Checking subnet status... [/magenta]") @@ -339,6 +338,7 @@ def register_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not success: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 25be51f2ed..d0286c5639 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3543,8 +3543,6 @@ def register( self, wallet: "Wallet", netuid: int, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = True, max_allowed_attempts: int = 3, output_in_place: bool = True, cuda: bool = False, @@ -3554,44 +3552,45 @@ def register( update_interval: Optional[int] = None, log_verbose: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ - Registers a neuron on the Bittensor network using the provided wallet. + Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. Registration is a critical step for a neuron to become an active participant in the network, enabling it to - stake, set weights, and receive incentives. + stake, set weights, and receive incentives. - Args: - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron to be registered. - netuid (int): The unique identifier of the subnet. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Defaults to `False`. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Defaults to - `True`. - max_allowed_attempts (int): Maximum number of attempts to register the wallet. - output_in_place (bool): If true, prints the progress of the proof of work to the console in-place. Meaning - the progress is printed on the same lines. Defaults to `True`. - cuda (bool): If ``true``, the wallet should be registered using CUDA device(s). Defaults to `False`. - dev_id (Union[List[int], int]): The CUDA device id to use, or a list of device ids. Defaults to `0` (zero). - tpb (int): The number of threads per block (CUDA). Default to `256`. - num_processes (Optional[int]): The number of processes to use to register. Default to `None`. - update_interval (Optional[int]): The number of nonces to solve between updates. Default to `None`. - log_verbose (bool): If ``true``, the registration process will log more information. Default to `False`. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: The wallet associated with the neuron to be registered. + netuid: The unique identifier of the subnet. + max_allowed_attempts: Maximum number of attempts to register the wallet. + output_in_place: If true, prints the progress of the proof of work to the console in-place. Meaning the + progress is printed on the same lines. + cuda: If ``true``, the wallet should be registered using CUDA device(s). + dev_id: The CUDA device id to use, or a list of device ids. + tpb: The number of threads per block (CUDA). + num_processes: The number of processes to use to register. + update_interval: The number of nonces to solve between updates. + log_verbose: If ``true``, the registration process will log more information. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: bool: ``True`` if the registration is successful, False otherwise. - This function facilitates the entry of new neurons into the network, supporting the decentralized - growth and scalability of the Bittensor ecosystem. + This function facilitates the entry of new neurons into the network, supporting the decentralized growth and + scalability of the Bittensor ecosystem. """ return register_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, max_allowed_attempts=max_allowed_attempts, tpb=tpb, update_interval=update_interval, @@ -3601,6 +3600,9 @@ def register( output_in_place=output_in_place, log_verbose=log_verbose, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) def register_subnet( diff --git a/migration.md b/migration.md index 766b8adbfd..7fdf0bea04 100644 --- a/migration.md +++ b/migration.md @@ -185,4 +185,5 @@ wait_for_finalization: bool = False, - `origin_hotkey` to `origin_hotkey_ss58` - `destination_hotkey` to `destination_hotkey_ss58` - [x] `.burned_register_extrinsic` and `subtensor.burned_register` -- [x] `.register_subnet_extrinsic` and `subtensor.register_subnet` \ No newline at end of file +- [x] `.register_subnet_extrinsic` and `subtensor.register_subnet` +- [x] `.register_extrinsic` and `subtensor.register` \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index 181394a8bb..df4608390a 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -59,6 +59,7 @@ async def test_register_extrinsic_success(subtensor, fake_wallet, mocker): wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) mocked_is_hotkey_registered.assert_called_once_with( netuid=1, hotkey_ss58="hotkey_ss58" @@ -124,6 +125,7 @@ async def test_register_extrinsic_success_with_cuda(subtensor, fake_wallet, mock wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) mocked_is_hotkey_registered.assert_called_once_with( netuid=1, hotkey_ss58="hotkey_ss58" @@ -281,6 +283,7 @@ async def is_stale_side_effect(*_, **__): wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) assert result is False diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 8c3603b285..b45a4b6e2c 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2624,6 +2624,7 @@ async def test_register_success(subtensor, fake_wallet, mocker): # Asserts mocked_register_extrinsic.assert_awaited_once_with( + wallet=fake_wallet, cuda=False, dev_id=0, log_verbose=False, @@ -2634,10 +2635,10 @@ async def test_register_success(subtensor, fake_wallet, mocker): subtensor=subtensor, tpb=256, update_interval=None, - wait_for_finalization=True, - wait_for_inclusion=False, - wallet=fake_wallet, period=None, + raise_error=False, + wait_for_finalization=True, + wait_for_inclusion=True, ) assert result == mocked_register_extrinsic.return_value From de1607b285589ac53490f05e895497337dee98a4 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 19:11:26 -0700 Subject: [PATCH 17/49] fix tests related with async register --- tests/e2e_tests/test_metagraph.py | 18 +++++------------- tests/e2e_tests/test_set_weights.py | 16 ++++++---------- tests/e2e_tests/test_staking.py | 12 ++---------- tests/unit_tests/test_subtensor_extended.py | 4 +--- 4 files changed, 14 insertions(+), 36 deletions(-) diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 6f7279abc4..7fc6c85d8c 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -216,9 +216,9 @@ async def test_metagraph_async(async_subtensor, alice_wallet, bob_wallet, dave_w alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 logging.console.info("Register the subnet through Alice") - assert await async_subtensor.subnets.register_subnet( - alice_wallet, True, True - ), "Unable to register the subnet" + assert await async_subtensor.subnets.register_subnet(alice_wallet), ( + "Unable to register the subnet" + ) logging.console.info("Verify subnet was created successfully") assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -594,11 +594,7 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): ] alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 3 - assert subtensor.subnets.register_subnet( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + assert subtensor.subnets.register_subnet(alice_wallet) block = subtensor.chain.get_current_block() metagraph_info = subtensor.metagraphs.get_metagraph_info( @@ -860,11 +856,7 @@ async def test_metagraph_info_async(async_subtensor, alice_wallet, bob_wallet): ] alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 3 - assert await async_subtensor.subnets.register_subnet( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + assert await async_subtensor.subnets.register_subnet(alice_wallet) block = await async_subtensor.chain.get_current_block() metagraph_info = await async_subtensor.metagraphs.get_metagraph_info( diff --git a/tests/e2e_tests/test_set_weights.py b/tests/e2e_tests/test_set_weights.py index 0e153b804e..4024a34e5d 100644 --- a/tests/e2e_tests/test_set_weights.py +++ b/tests/e2e_tests/test_set_weights.py @@ -54,11 +54,9 @@ def test_set_weights_uses_next_nonce(subtensor, alice_wallet): for netuid in netuids: # Register the subnets - assert subtensor.subnets.register_subnet( - wallet=alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ), "Unable to register the subnet" + assert subtensor.subnets.register_subnet(alice_wallet), ( + "Unable to register the subnet" + ) # Verify all subnets created successfully assert subtensor.subnets.subnet_exists(netuid), ( @@ -222,11 +220,9 @@ async def test_set_weights_uses_next_nonce_async(async_subtensor, alice_wallet): for netuid in netuids: # Register the subnets - assert await async_subtensor.subnets.register_subnet( - wallet=alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ), "Unable to register the subnet" + assert await async_subtensor.subnets.register_subnet(alice_wallet), ( + "Unable to register the subnet" + ) # Verify all subnets created successfully assert await async_subtensor.subnets.subnet_exists(netuid), ( diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index c51272ef00..3fad46d386 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -396,11 +396,7 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): ] for _ in netuids: - subtensor.subnets.register_subnet( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + subtensor.subnets.register_subnet(alice_wallet) # make sure we passed start_call limit for both subnets for netuid in netuids: @@ -547,11 +543,7 @@ async def test_batch_operations_async(async_subtensor, alice_wallet, bob_wallet) ] for _ in netuids: - await async_subtensor.subnets.register_subnet( - wallet=alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + await async_subtensor.subnets.register_subnet(alice_wallet) # make sure we passed start_call limit for both subnets for netuid in netuids: diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index 1f0449cc19..01a365bd31 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -1228,9 +1228,7 @@ def test_register_subnet_insufficient_funds( mocker.patch.object(subtensor, "get_balance", return_value=Balance(0)) mocker.patch.object(subtensor, "get_subnet_burn_cost", return_value=Balance(10)) - success = subtensor.register_subnet( - fake_wallet, - ) + success = subtensor.register_subnet(fake_wallet) assert success is False From 3789e644d7adce90b9d953636a6dbd9571ad7655 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 19:21:06 -0700 Subject: [PATCH 18/49] `.set_subnet_identity_extrinsic` and `subtensor.set_subnet_identity` --- bittensor/core/async_subtensor.py | 26 ++++++----- .../core/extrinsics/asyncex/registration.py | 46 ++++++++++--------- bittensor/core/extrinsics/registration.py | 46 ++++++++++--------- bittensor/core/subtensor.py | 32 +++++++------ migration.md | 3 +- .../extrinsics/asyncex/test_registration.py | 4 +- .../extrinsics/test_registration.py | 6 ++- tests/unit_tests/test_async_subtensor.py | 5 +- tests/unit_tests/test_subtensor.py | 5 +- 9 files changed, 96 insertions(+), 77 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index bd2a098299..8c0a60b1fe 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5142,29 +5142,30 @@ async def set_subnet_identity( wallet: "Wallet", netuid: int, subnet_identity: SubnetIdentity, - wait_for_inclusion: bool = False, + period: Optional[int] = 8, + raise_error: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Sets the identity of a subnet for a specific wallet and network. - Arguments: + Parameters: wallet: The wallet instance that will authorize the transaction. netuid: The unique ID of the network on which the operation takes place. - subnet_identity: The identity data of the subnet including attributes like name, GitHub - repository, contact, URL, discord, description, and any additional metadata. - wait_for_inclusion: Indicates if the function should wait for the transaction to be included in the - block. - wait_for_finalization: Indicates if the function should wait for the transaction to reach - finalization. + subnet_identity: The identity data of the subnet including attributes like name, GitHub repository, contact, + URL, discord, description, and any additional metadata. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ return await set_subnet_identity_extrinsic( subtensor=self, @@ -5178,9 +5179,10 @@ async def set_subnet_identity( discord=subnet_identity.discord, description=subnet_identity.description, additional=subnet_identity.additional, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def set_weights( diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index bbb1afa1d7..2e67368be5 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -407,34 +407,37 @@ async def set_subnet_identity_extrinsic( discord: str, description: str, additional: str, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """ Set the identity information for a given subnet. - Arguments: - subtensor (AsyncSubtensor): An instance of the Subtensor class to interact with the blockchain. - wallet (Wallet): A wallet instance used to sign and submit the extrinsic. - netuid (int): The unique ID for the subnet. - subnet_name (str): The name of the subnet to assign the identity information. - github_repo (str): URL of the GitHub repository related to the subnet. - subnet_contact (str): Subnet's contact information, e.g., email or contact link. - subnet_url (str): The URL of the subnet's primary web portal. - logo_url (str): The URL of the logo's primary web portal. - discord (str): Discord server or contact for the subnet. - description (str): A textual description of the subnet. - additional (str): Any additional metadata or information related to the subnet. - wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). - wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: An instance of the Subtensor class to interact with the blockchain. + wallet: A wallet instance used to sign and submit the extrinsic. + netuid: The unique ID for the subnet. + subnet_name: The name of the subnet to assign the identity information. + github_repo: URL of the GitHub repository related to the subnet. + subnet_contact: Subnet's contact information, e.g., email or contact link. + subnet_url: The URL of the subnet's primary web portal. + logo_url: The URL of the logo's primary web portal. + discord: Discord server or contact for the subnet. + description: A textual description of the subnet. + additional: Any additional metadata or information related to the subnet. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: A tuple where the first element indicates success or failure (True/False), and the second - element contains a descriptive message. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ if not (unlock := unlock_key(wallet)).success: @@ -464,6 +467,7 @@ async def set_subnet_identity_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index 4d9af329ee..6f667ff7b6 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -401,34 +401,37 @@ def set_subnet_identity_extrinsic( discord: str, description: str, additional: str, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """ Set the identity information for a given subnet. - Arguments: - subtensor (Subtensor): An instance of the Subtensor class to interact with the blockchain. - wallet (Wallet): A wallet instance used to sign and submit the extrinsic. - netuid (int): The unique ID for the subnet. - subnet_name (str): The name of the subnet to assign the identity information. - github_repo (str): URL of the GitHub repository related to the subnet. - subnet_contact (str): Subnet's contact information, e.g., email or contact link. - subnet_url (str): The URL of the subnet's primary web portal. - logo_url (str): The URL of the logo's primary web portal. - discord (str): Discord server or contact for the subnet. - description (str): A textual description of the subnet. - additional (str): Any additional metadata or information related to the subnet. - wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). - wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: An instance of the Subtensor class to interact with the blockchain. + wallet: A wallet instance used to sign and submit the extrinsic. + netuid: The unique ID for the subnet. + subnet_name: The name of the subnet to assign the identity information. + github_repo: URL of the GitHub repository related to the subnet. + subnet_contact: Subnet's contact information, e.g., email or contact link. + subnet_url: The URL of the subnet's primary web portal. + logo_url: The URL of the logo's primary web portal. + discord: Discord server or contact for the subnet. + description: A textual description of the subnet. + additional: Any additional metadata or information related to the subnet. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: A tuple where the first element indicates success or failure (True/False), and the second - element contains a descriptive message. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ if not (unlock := unlock_key(wallet)).success: @@ -458,6 +461,7 @@ def set_subnet_identity_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index d0286c5639..3b3be9f0f9 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3951,29 +3951,30 @@ def set_subnet_identity( wallet: "Wallet", netuid: int, subnet_identity: SubnetIdentity, - wait_for_inclusion: bool = False, + period: Optional[int] = 8, + raise_error: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Sets the identity of a subnet for a specific wallet and network. - Arguments: - wallet (Wallet): The wallet instance that will authorize the transaction. - netuid (int): The unique ID of the network on which the operation takes place. - subnet_identity (SubnetIdentity): The identity data of the subnet including attributes like name, GitHub - repository, contact, URL, discord, description, and any additional metadata. - wait_for_inclusion (bool): Indicates if the function should wait for the transaction to be included in the - block. - wait_for_finalization (bool): Indicates if the function should wait for the transaction to reach - finalization. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's + Parameters: + wallet: The wallet instance that will authorize the transaction. + netuid: The unique ID of the network on which the operation takes place. + subnet_identity: The identity data of the subnet including attributes like name, GitHub repository, contact, + URL, discord, description, and any additional metadata. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ return set_subnet_identity_extrinsic( subtensor=self, @@ -3987,9 +3988,10 @@ def set_subnet_identity( discord=subnet_identity.discord, description=subnet_identity.description, additional=subnet_identity.additional, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def set_weights( diff --git a/migration.md b/migration.md index 7fdf0bea04..ac7c2ef09d 100644 --- a/migration.md +++ b/migration.md @@ -186,4 +186,5 @@ wait_for_finalization: bool = False, - `destination_hotkey` to `destination_hotkey_ss58` - [x] `.burned_register_extrinsic` and `subtensor.burned_register` - [x] `.register_subnet_extrinsic` and `subtensor.register_subnet` -- [x] `.register_extrinsic` and `subtensor.register` \ No newline at end of file +- [x] `.register_extrinsic` and `subtensor.register` +- [x] `.set_subnet_identity_extrinsic` and `subtensor.set_subnet_identity` \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index df4608390a..03f00aab82 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -345,9 +345,10 @@ async def test_set_subnet_identity_extrinsic_is_success(subtensor, fake_wallet, mocked_sign_and_send_extrinsic.assert_awaited_once_with( call=mocked_compose_call.return_value, wallet=fake_wallet, - wait_for_inclusion=False, + wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) assert result == (True, "Identities for subnet 123 are set.") @@ -416,6 +417,7 @@ async def test_set_subnet_identity_extrinsic_is_failed(subtensor, fake_wallet, m wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) assert result == ( diff --git a/tests/unit_tests/extrinsics/test_registration.py b/tests/unit_tests/extrinsics/test_registration.py index 97b0fb785f..796c3d45f0 100644 --- a/tests/unit_tests/extrinsics/test_registration.py +++ b/tests/unit_tests/extrinsics/test_registration.py @@ -270,9 +270,10 @@ def test_set_subnet_identity_extrinsic_is_success(mock_subtensor, mock_wallet, m mocked_sign_and_send_extrinsic.assert_called_once_with( call=mocked_compose_call.return_value, wallet=mock_wallet, - wait_for_inclusion=False, + wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) assert result == (True, "Identities for subnet 123 are set.") @@ -335,9 +336,10 @@ def test_set_subnet_identity_extrinsic_is_failed(mock_subtensor, mock_wallet, mo mocked_sign_and_send_extrinsic.assert_called_once_with( call=mocked_compose_call.return_value, wallet=mock_wallet, - wait_for_inclusion=False, + wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) assert result == ( diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index b45a4b6e2c..19c0024961 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3003,9 +3003,10 @@ async def test_set_subnet_identity(mocker, subtensor, fake_wallet): discord=fake_subnet_identity.discord, description=fake_subnet_identity.description, additional=fake_subnet_identity.additional, + period=8, + raise_error=False, wait_for_finalization=True, - wait_for_inclusion=False, - period=None, + wait_for_inclusion=True, ) assert result == mocked_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 17539a8232..20c8f151d9 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3245,9 +3245,10 @@ def test_set_subnet_identity(mocker, subtensor, fake_wallet): discord=fake_subnet_identity.discord, description=fake_subnet_identity.description, additional=fake_subnet_identity.additional, + period=8, + raise_error=False, wait_for_finalization=True, - wait_for_inclusion=False, - period=None, + wait_for_inclusion=True, ) assert result == mocked_extrinsic.return_value From db123b6a49c1e8389c1c2c8bc607068fbceb213b Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 19:28:29 -0700 Subject: [PATCH 19/49] update `subtensor.subnets.burned_register` call --- tests/e2e_tests/test_commitment.py | 6 +- tests/e2e_tests/test_delegate.py | 8 --- tests/e2e_tests/test_hotkeys.py | 6 +- tests/e2e_tests/test_metagraph.py | 12 +--- tests/e2e_tests/test_reveal_commitments.py | 5 +- tests/e2e_tests/test_staking.py | 64 ++-------------------- 6 files changed, 17 insertions(+), 84 deletions(-) diff --git a/tests/e2e_tests/test_commitment.py b/tests/e2e_tests/test_commitment.py index d16c9a7104..d31bf739ef 100644 --- a/tests/e2e_tests/test_commitment.py +++ b/tests/e2e_tests/test_commitment.py @@ -31,12 +31,12 @@ def test_commitment(subtensor, alice_wallet, dave_wallet): ) assert subtensor.subnets.burned_register( - alice_wallet, + wallet=alice_wallet, netuid=dave_subnet_netuid, ) uid = subtensor.subnets.get_uid_for_hotkey_on_subnet( - alice_wallet.hotkey.ss58_address, + hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=dave_subnet_netuid, ) @@ -110,7 +110,7 @@ async def test_commitment_async(async_subtensor, alice_wallet, dave_wallet): ) assert await sub.subnets.burned_register( - alice_wallet, + wallet=alice_wallet, netuid=dave_subnet_netuid, ) diff --git a/tests/e2e_tests/test_delegate.py b/tests/e2e_tests/test_delegate.py index 1b5c136c04..1a36389b57 100644 --- a/tests/e2e_tests/test_delegate.py +++ b/tests/e2e_tests/test_delegate.py @@ -677,15 +677,11 @@ def test_nominator_min_required_stake(subtensor, alice_wallet, bob_wallet, dave_ subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) success = subtensor.staking.add_stake( @@ -766,15 +762,11 @@ async def test_nominator_min_required_stake_async( await async_subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) await async_subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) success = await async_subtensor.staking.add_stake( diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index f91d876da6..f9fde7a37f 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -60,8 +60,8 @@ def test_hotkeys(subtensor, alice_wallet, dave_wallet): is False ) - subtensor.subnets.burned_register( - alice_wallet, + assert subtensor.subnets.burned_register( + wallet=alice_wallet, netuid=dave_subnet_netuid, ) @@ -119,7 +119,7 @@ async def test_hotkeys_async(async_subtensor, alice_wallet, dave_wallet): ) assert await async_subtensor.subnets.burned_register( - alice_wallet, + wallet=alice_wallet, netuid=dave_subnet_netuid, ) diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 7fc6c85d8c..570eefc460 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -560,8 +560,6 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): assert subtensor.subnets.burned_register( bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) metagraph_info = subtensor.metagraphs.get_metagraph_info(netuid=alice_subnet_netuid) @@ -820,8 +818,6 @@ async def test_metagraph_info_async(async_subtensor, alice_wallet, bob_wallet): assert await async_subtensor.subnets.burned_register( bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) metagraph_info = await async_subtensor.metagraphs.get_metagraph_info( @@ -996,10 +992,8 @@ def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) assert subtensor.subnets.burned_register( - bob_wallet, + wallet=bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) fields = [ @@ -1234,10 +1228,8 @@ async def test_metagraph_info_with_indexes_async( ) assert await async_subtensor.subnets.burned_register( - bob_wallet, + wallet=bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) fields = [ diff --git a/tests/e2e_tests/test_reveal_commitments.py b/tests/e2e_tests/test_reveal_commitments.py index 840d3cdf1d..9ad12eaa76 100644 --- a/tests/e2e_tests/test_reveal_commitments.py +++ b/tests/e2e_tests/test_reveal_commitments.py @@ -46,7 +46,8 @@ def test_set_reveal_commitment(subtensor, alice_wallet, bob_wallet): # Register Bob's neuron assert subtensor.subnets.burned_register( - bob_wallet, alice_subnet_netuid, True, True + wallet=bob_wallet, + netuid=alice_subnet_netuid, ), "Bob's neuron was not register." # Verify subnet 2 created successfully @@ -162,7 +163,7 @@ async def test_set_reveal_commitment(async_subtensor, alice_wallet, bob_wallet): # Register Bob's neuron assert await async_subtensor.subnets.burned_register( - bob_wallet, alice_subnet_netuid, True, True + bob_wallet, alice_subnet_netuid ), "Bob's neuron was not register." # Verify subnet 2 created successfully diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 3fad46d386..27b3468c89 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -41,15 +41,11 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): subtensor.subnets.burned_register( wallet=alice_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) logging.console.success(f"Alice is registered in subnet {alice_subnet_netuid}") subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) logging.console.success(f"Bob is registered in subnet {alice_subnet_netuid}") @@ -219,15 +215,11 @@ async def test_single_operation_async(async_subtensor, alice_wallet, bob_wallet) await async_subtensor.subnets.burned_register( wallet=alice_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) logging.console.success(f"Alice is registered in subnet {alice_subnet_netuid}") await async_subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) logging.console.success(f"Bob is registered in subnet {alice_subnet_netuid}") @@ -404,10 +396,8 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): for netuid in netuids: subtensor.subnets.burned_register( - bob_wallet, - netuid, - wait_for_inclusion=True, - wait_for_finalization=True, + wallet=bob_wallet, + netuid=netuid, ) for netuid in netuids: @@ -553,8 +543,6 @@ async def test_batch_operations_async(async_subtensor, alice_wallet, bob_wallet) await async_subtensor.subnets.burned_register( wallet=bob_wallet, netuid=netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) for netuid in netuids: @@ -1103,14 +1091,10 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): subtensor.subnets.burned_register( wallet=alice_wallet, netuid=origin_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) subtensor.subnets.burned_register( wallet=alice_wallet, netuid=dest_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) # Add initial stake to swap from @@ -1223,14 +1207,10 @@ async def test_safe_swap_stake_scenarios_async( await async_subtensor.subnets.burned_register( wallet=alice_wallet, netuid=origin_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) await async_subtensor.subnets.burned_register( wallet=alice_wallet, netuid=dest_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) # Add initial stake to swap from @@ -1362,15 +1342,11 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert subtensor.staking.move_stake( @@ -1533,15 +1509,11 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ await async_subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) await async_subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert await async_subtensor.staking.move_stake( @@ -1666,10 +1638,8 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) subtensor.subnets.burned_register( - alice_wallet, + wallet=alice_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert subtensor.staking.add_stake( @@ -1712,10 +1682,8 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) subtensor.subnets.burned_register( - bob_wallet, + wallet=bob_wallet, netuid=dave_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert subtensor.staking.transfer_stake( @@ -1801,10 +1769,8 @@ async def test_transfer_stake_async( ) await async_subtensor.subnets.burned_register( - alice_wallet, + wallet=alice_wallet, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert await async_subtensor.staking.add_stake( @@ -1849,10 +1815,8 @@ async def test_transfer_stake_async( ) await async_subtensor.subnets.burned_register( - bob_wallet, + wallet=bob_wallet, netuid=dave_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert await async_subtensor.staking.transfer_stake( @@ -1945,15 +1909,11 @@ def test_unstaking_with_limit( assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid_2, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid_2, - wait_for_inclusion=True, - wait_for_finalization=True, ) # Register second SN @@ -1969,15 +1929,11 @@ def test_unstaking_with_limit( assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid_3, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid_3, - wait_for_inclusion=True, - wait_for_finalization=True, ) # Check Bob's stakes are empty. @@ -2075,15 +2031,11 @@ async def test_unstaking_with_limit_async( assert await async_subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid_2, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert await async_subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid_2, - wait_for_inclusion=True, - wait_for_finalization=True, ) # Register second SN @@ -2099,15 +2051,11 @@ async def test_unstaking_with_limit_async( assert await async_subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid_3, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert await async_subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid_3, - wait_for_inclusion=True, - wait_for_finalization=True, ) # Check Bob's stakes are empty. From db71ccd52354d8119098b309a4eb89718301fda8 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 19:36:31 -0700 Subject: [PATCH 20/49] oops, period=None --- bittensor/core/async_subtensor.py | 2 +- bittensor/core/subtensor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 8c0a60b1fe..e739016207 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5142,7 +5142,7 @@ async def set_subnet_identity( wallet: "Wallet", netuid: int, subnet_identity: SubnetIdentity, - period: Optional[int] = 8, + period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 3b3be9f0f9..e4c99eb31f 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3951,7 +3951,7 @@ def set_subnet_identity( wallet: "Wallet", netuid: int, subnet_identity: SubnetIdentity, - period: Optional[int] = 8, + period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, From c6043fed46fc2ad6ced81694027197ebe6497fcb Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 19:39:33 -0700 Subject: [PATCH 21/49] =?UTF-8?q?oops=20again=20-=20need=20to=20take=20a?= =?UTF-8?q?=20rest=20=F0=9F=98=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/unit_tests/test_async_subtensor.py | 2 +- tests/unit_tests/test_subtensor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 19c0024961..ecc791bfaa 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3003,7 +3003,7 @@ async def test_set_subnet_identity(mocker, subtensor, fake_wallet): discord=fake_subnet_identity.discord, description=fake_subnet_identity.description, additional=fake_subnet_identity.additional, - period=8, + period=None, raise_error=False, wait_for_finalization=True, wait_for_inclusion=True, diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 20c8f151d9..9c8103f1a2 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3245,7 +3245,7 @@ def test_set_subnet_identity(mocker, subtensor, fake_wallet): discord=fake_subnet_identity.discord, description=fake_subnet_identity.description, additional=fake_subnet_identity.additional, - period=8, + period=None, raise_error=False, wait_for_finalization=True, wait_for_inclusion=True, From dcfd78968a029c396ee59da0e77687df9c9f9386 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 21:21:43 -0700 Subject: [PATCH 22/49] `.root_register_extrinsic`, `subtensor.burned_register` and `subtensor.root_register` --- bittensor/core/async_subtensor.py | 31 ++++++++++----------- bittensor/core/extrinsics/asyncex/root.py | 27 ++++++++++--------- bittensor/core/extrinsics/root.py | 33 ++++++++++++----------- bittensor/core/subtensor.py | 27 ++++++++++--------- migration.md | 3 ++- tests/unit_tests/extrinsics/test_root.py | 1 + 6 files changed, 65 insertions(+), 57 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index e739016207..571e20ad50 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4469,7 +4469,7 @@ async def burned_register( Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling TAO tokens, allowing them to be re-mined by performing work on the network. - Args: + Parameters: wallet: The wallet associated with the neuron to be registered. netuid: The unique identifier of the subnet. period: The number of blocks during which the transaction will remain valid after it's submitted. If the @@ -4487,9 +4487,10 @@ async def burned_register( return await root_register_extrinsic( subtensor=self, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) return await burned_register_extrinsic( @@ -4967,34 +4968,34 @@ async def root_set_pending_childkey_cooldown( async def root_register( self, wallet: "Wallet", - block_hash: Optional[str] = None, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> bool: """ Register neuron by recycling some TAO. - Arguments: - wallet: Bittensor wallet instance. - block_hash: This argument will be removed in Bittensor v10 - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `False`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is - `False`. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: The wallet associated with the neuron to be registered. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - `True` if registration was successful, otherwise `False`. + bool: ``True`` if the registration is successful, False otherwise. """ return await root_register_extrinsic( subtensor=self, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def set_children( diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index bab6f0b230..bb318cdb90 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -37,26 +37,26 @@ async def _get_limits(subtensor: "AsyncSubtensor") -> tuple[int, float]: async def root_register_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> bool: - """Registers the wallet to the root network. + """ + Registers the neuron to the root network. Arguments: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The AsyncSubtensor object - wallet (bittensor_wallet.Wallet): Bittensor wallet object. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + subtensor: Subtensor instance to interact with the blockchain. + wallet: Bittensor Wallet instance. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, - the response is `True`. + bool: True if the subnet registration was successful, False otherwise. """ netuid = 0 logging.info( @@ -114,6 +114,7 @@ async def root_register_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not success: diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index 1606bf76da..161ca53fd0 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -38,26 +38,26 @@ def _get_limits(subtensor: "Subtensor") -> tuple[int, float]: def root_register_extrinsic( subtensor: "Subtensor", wallet: "Wallet", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Registers the wallet to the root network. - - Arguments: - subtensor (bittensor.core.subtensor.Subtensor): The Subtensor object - wallet (bittensor_wallet.Wallet): Bittensor wallet object. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + """ + Registers the neuron to the root network. + + Parameters: + subtensor: Subtensor instance to interact with the blockchain. + wallet: Bittensor Wallet instance. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the - response is `True`. + bool: True if the subnet registration was successful, False otherwise. """ netuid = 0 logging.info( @@ -113,6 +113,7 @@ def root_register_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not success: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index e4c99eb31f..52aea8887e 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3300,7 +3300,7 @@ def burned_register( Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling TAO tokens, allowing them to be re-mined by performing work on the network. - Args: + Parameters: wallet: The wallet associated with the neuron to be registered. netuid: The unique identifier of the subnet. period: The number of blocks during which the transaction will remain valid after it's submitted. If the @@ -3318,9 +3318,10 @@ def burned_register( return root_register_extrinsic( subtensor=self, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) return burned_register_extrinsic( @@ -3754,32 +3755,34 @@ def reveal_weights( def root_register( self, wallet: "Wallet", + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> bool: """ Register neuron by recycling some TAO. - Arguments: + Parameters: wallet (bittensor_wallet.Wallet): Bittensor wallet instance. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``False``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - `True` if registration was successful, otherwise `False`. + bool: ``True`` if the registration is successful, False otherwise. """ return root_register_extrinsic( subtensor=self, wallet=wallet, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def root_set_pending_childkey_cooldown( diff --git a/migration.md b/migration.md index ac7c2ef09d..a26722daab 100644 --- a/migration.md +++ b/migration.md @@ -187,4 +187,5 @@ wait_for_finalization: bool = False, - [x] `.burned_register_extrinsic` and `subtensor.burned_register` - [x] `.register_subnet_extrinsic` and `subtensor.register_subnet` - [x] `.register_extrinsic` and `subtensor.register` -- [x] `.set_subnet_identity_extrinsic` and `subtensor.set_subnet_identity` \ No newline at end of file +- [x] `.set_subnet_identity_extrinsic` and `subtensor.set_subnet_identity` +- [x] `.root_register_extrinsic`, `subtensor.burned_register` and `subtensor.root_register` \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/test_root.py b/tests/unit_tests/extrinsics/test_root.py index 5a16a10909..4a4c11e8e8 100644 --- a/tests/unit_tests/extrinsics/test_root.py +++ b/tests/unit_tests/extrinsics/test_root.py @@ -104,6 +104,7 @@ def test_root_register_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=None, + raise_error=False, ) From 1596818e2d455eeabf8ef8375130b1b494258eeb Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 21:58:10 -0700 Subject: [PATCH 23/49] `.serve_extrinsic`, `.serve_axon_extrinsic` and `subtensor.serve_axon` --- bittensor/core/async_subtensor.py | 33 ++-- bittensor/core/extrinsics/asyncex/serving.py | 100 ++++++------ bittensor/core/extrinsics/serving.py | 151 +++++++------------ bittensor/core/subtensor.py | 35 +++-- migration.md | 4 +- tests/unit_tests/extrinsics/test_serving.py | 106 +++++++------ tests/unit_tests/test_subtensor.py | 133 +--------------- 7 files changed, 219 insertions(+), 343 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 571e20ad50..ae0ec9a4a2 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5319,27 +5319,31 @@ async def serve_axon( self, netuid: int, axon: "Axon", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, certificate: Optional[Certificate] = None, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, ) -> bool: """ - Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. This function is used to - set up the Axon, a key component of a neuron that handles incoming queries and data processing tasks. + Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. - Arguments: + This function is used to set up the Axon, a key component of a neuron that handles incoming queries and data + processing tasks. + + Parameters: netuid: The unique identifier of the subnetwork. axon: The Axon instance to be registered for serving. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `False`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is `True`. - certificate: Certificate to use for TLS. If `None`, no TLS will be used. Defaults to `None`. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. + certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + period: The number of blocks during which the transaction will remain valid after it's + submitted. If the transaction is not included in a block within that number of blocks, it will expire + and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - bool: `True` if the Axon serve registration is successful, False otherwise. + bool: ``True`` if the Axon serve registration is successful, False otherwise. By registering an Axon, the neuron becomes an active part of the network's distributed computing infrastructure, contributing to the collective intelligence of Bittensor. @@ -5348,10 +5352,11 @@ async def serve_axon( subtensor=self, netuid=netuid, axon=axon, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, certificate=certificate, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) async def start_call( diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index ffd952e049..e8084b023f 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -76,35 +76,34 @@ async def serve_extrinsic( netuid: int, placeholder1: int = 0, placeholder2: int = 0, - wait_for_inclusion: bool = False, - wait_for_finalization=True, certificate: Optional[Certificate] = None, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Subscribes a Bittensor endpoint to the subtensor chain. - - Args: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Subtensor instance object. - wallet (bittensor_wallet.Wallet): Bittensor wallet object. - ip (str): Endpoint host port i.e., ``192.122.31.4``. - port (int): Endpoint port number i.e., ``9221``. - protocol (int): An ``int`` representation of the protocol. - netuid (int): The network uid to serve on. - placeholder1 (int): A placeholder for future use. - placeholder2 (int): A placeholder for future use. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. - Defaults to ``None``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + """ + Subscribes a Bittensor endpoint to the subtensor chain. + + Parameters: + subtensor: Subtensor instance object. + wallet: Bittensor wallet object. + ip: Endpoint host port i.e., ``192.122.31.4``. + port: Endpoint port number i.e., ``9221``. + protocol: An ``int`` representation of the protocol. + netuid: The network uid to serve on. + placeholder1: A placeholder for future use. + placeholder2: A placeholder for future use. + certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is ``True``. + bool: True if the subnet registration was successful, False otherwise. """ # Decrypt hotkey if not (unlock := unlock_key(wallet, "hotkey")).success: @@ -141,13 +140,25 @@ async def serve_extrinsic( f"Serving axon with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] -> " f"[green]{subtensor.network}:{netuid}[/green]" ) - success, message = await do_serve_axon( - subtensor=subtensor, + + if params.certificate is None: + call_function = "serve_axon" + else: + call_function = "serve_axon_tls" + + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function=call_function, + call_params=params.dict(), + ) + success, message = await subtensor.sign_and_send_extrinsic( + call=call, wallet=wallet, - call_params=params, - wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", period=period, + raise_error=raise_error, ) if success: @@ -165,30 +176,30 @@ async def serve_axon_extrinsic( subtensor: "AsyncSubtensor", netuid: int, axon: "Axon", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, certificate: Optional[Certificate] = None, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, ) -> bool: - """Serves the axon to the network. + """ + Serves the axon to the network. - Args: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Subtensor instance object. + Parameters: + subtensor: AsyncSubtensor instance object. netuid (int): The ``netuid`` being served on. axon (bittensor.core.axon.Axon): Axon to serve. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is ``True``. + bool: True if the subnet registration was successful, False otherwise. """ if not (unlock := unlock_key(axon.wallet, "hotkey")).success: logging.error(unlock.message) @@ -219,10 +230,11 @@ async def serve_axon_extrinsic( port=external_port, protocol=4, netuid=netuid, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, certificate=certificate, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) return serve_success diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index 09903d763e..6a8a85f00c 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -16,56 +16,6 @@ from bittensor.core.subtensor import Subtensor -def do_serve_axon( - subtensor: "Subtensor", - wallet: "Wallet", - call_params: "AxonServeCallParams", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, - period: Optional[int] = None, -) -> tuple[bool, str]: - """ - Internal method to submit a serve axon transaction to the Bittensor blockchain. This method creates and submits a - transaction, enabling a neuron's ``Axon`` to serve requests on the network. - - Args: - subtensor (bittensor.core.subtensor.Subtensor): Subtensor instance object. - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron. - call_params (bittensor.core.types.AxonServeCallParams): Parameters required for the serve axon call. - 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. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. - - Returns: - tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. - - This function is crucial for initializing and announcing a neuron's ``Axon`` service on the network, enhancing the - decentralized computation capabilities of Bittensor. - """ - if call_params.certificate is None: - call_function = "serve_axon" - else: - call_function = "serve_axon_tls" - - call = subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function=call_function, - call_params=call_params.dict(), - ) - - success, message = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - sign_with="hotkey", - period=period, - ) - return success, message - - def serve_extrinsic( subtensor: "Subtensor", wallet: "Wallet", @@ -75,35 +25,34 @@ def serve_extrinsic( netuid: int, placeholder1: int = 0, placeholder2: int = 0, - wait_for_inclusion: bool = False, - wait_for_finalization=True, certificate: Optional[Certificate] = None, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Subscribes a Bittensor endpoint to the subtensor chain. - - Args: - subtensor (bittensor.core.subtensor.Subtensor): Subtensor instance object. - wallet (bittensor_wallet.Wallet): Bittensor wallet object. - ip (str): Endpoint host port i.e., ``192.122.31.4``. - port (int): Endpoint port number i.e., ``9221``. - protocol (int): An ``int`` representation of the protocol. - netuid (int): The network uid to serve on. - placeholder1 (int): A placeholder for future use. - placeholder2 (int): A placeholder for future use. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. - Defaults to ``None``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + """ + Subscribes a Bittensor endpoint to the subtensor chain. + + Parameters: + subtensor: Subtensor instance object. + wallet: Bittensor wallet object. + ip: Endpoint host port i.e., ``192.122.31.4``. + port: Endpoint port number i.e., ``9221``. + protocol: An ``int`` representation of the protocol. + netuid: The network uid to serve on. + placeholder1: A placeholder for future use. + placeholder2: A placeholder for future use. + certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is ``True``. + bool: True if the subnet registration was successful, False otherwise. """ # Decrypt hotkey if not (unlock := unlock_key(wallet, "hotkey")).success: @@ -140,13 +89,26 @@ def serve_extrinsic( f"Serving axon with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] -> " f"[green]{subtensor.network}:{netuid}[/green]" ) - success, message = do_serve_axon( - subtensor=subtensor, + + if params.certificate is None: + call_function = "serve_axon" + else: + call_function = "serve_axon_tls" + + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function=call_function, + call_params=params.dict(), + ) + + success, message = subtensor.sign_and_send_extrinsic( + call=call, wallet=wallet, - call_params=params, - wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", period=period, + raise_error=raise_error, ) if success: @@ -164,30 +126,30 @@ def serve_axon_extrinsic( subtensor: "Subtensor", netuid: int, axon: "Axon", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, certificate: Optional["Certificate"] = None, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, ) -> bool: - """Serves the axon to the network. + """ + Serves the axon to the network. - Args: - subtensor (bittensor.core.subtensor.Subtensor): Subtensor instance object. + Parameters: + subtensor: Subtensor instance object. netuid (int): The ``netuid`` being served on. axon (bittensor.core.axon.Axon): Axon to serve. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is ``true``. + bool: True if the subnet registration was successful, False otherwise. """ if not (unlock := unlock_key(axon.wallet, "hotkey")).success: logging.error(unlock.message) @@ -216,10 +178,11 @@ def serve_axon_extrinsic( port=external_port, protocol=4, netuid=netuid, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, certificate=certificate, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) return serve_success diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 52aea8887e..b1735c6f38 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4116,41 +4116,44 @@ def serve_axon( self, netuid: int, axon: "Axon", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, certificate: Optional[Certificate] = None, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, ) -> bool: """ - Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. This function is used to - set up the Axon, a key component of a neuron that handles incoming queries and data processing tasks. + Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. - Args: - netuid (int): The unique identifier of the subnetwork. - axon (bittensor.core.axon.Axon): The Axon instance to be registered for serving. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``True``. - certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. - Defaults to ``None``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's + This function is used to set up the Axon, a key component of a neuron that handles incoming queries and data + processing tasks. + + Parameters: + netuid: The unique identifier of the subnetwork. + axon: The Axon instance to be registered for serving. + certificate: Certificate to use for TLS. If ``None``, no TLS will be used. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: bool: ``True`` if the Axon serve registration is successful, False otherwise. By registering an Axon, the neuron becomes an active part of the network's distributed computing infrastructure, - contributing to the collective intelligence of Bittensor. + contributing to the collective intelligence of Bittensor. """ return serve_axon_extrinsic( subtensor=self, netuid=netuid, axon=axon, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, certificate=certificate, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) def start_call( diff --git a/migration.md b/migration.md index a26722daab..29d7bd9783 100644 --- a/migration.md +++ b/migration.md @@ -188,4 +188,6 @@ wait_for_finalization: bool = False, - [x] `.register_subnet_extrinsic` and `subtensor.register_subnet` - [x] `.register_extrinsic` and `subtensor.register` - [x] `.set_subnet_identity_extrinsic` and `subtensor.set_subnet_identity` -- [x] `.root_register_extrinsic`, `subtensor.burned_register` and `subtensor.root_register` \ No newline at end of file +- [x] `.root_register_extrinsic`, `subtensor.burned_register` and `subtensor.root_register` +- [x] `.serve_extrinsic` +- [x] `.serve_axon_extrinsic` and `subtensor.serve_axon` \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/test_serving.py b/tests/unit_tests/extrinsics/test_serving.py index 7095ea0bc1..4fca1b7bc8 100644 --- a/tests/unit_tests/extrinsics/test_serving.py +++ b/tests/unit_tests/extrinsics/test_serving.py @@ -110,20 +110,22 @@ def test_serve_extrinsic_happy_path( test_id, mocker, ): - # Arrange - serving.do_serve_axon = mocker.MagicMock(return_value=(True, "")) - # Act + # Prep + mocker.patch.object( + mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "") + ) + # Call result = serving.serve_extrinsic( - mock_subtensor, - mock_wallet, - ip, - port, - protocol, - netuid, - placeholder1, - placeholder2, - wait_for_inclusion, - wait_for_finalization, + subtensor=mock_subtensor, + wallet=mock_wallet, + ip=ip, + port=port, + protocol=protocol, + netuid=netuid, + placeholder1=placeholder1, + placeholder2=placeholder2, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) # Assert @@ -164,20 +166,23 @@ def test_serve_extrinsic_edge_cases( test_id, mocker, ): - # Arrange - serving.do_serve_axon = mocker.MagicMock(return_value=(True, "")) - # Act + # Prep + mocker.patch.object( + mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "") + ) + + # Call result = serving.serve_extrinsic( - mock_subtensor, - mock_wallet, - ip, - port, - protocol, - netuid, - placeholder1, - placeholder2, - wait_for_inclusion, - wait_for_finalization, + subtensor=mock_subtensor, + wallet=mock_wallet, + ip=ip, + port=port, + protocol=protocol, + netuid=netuid, + placeholder1=placeholder1, + placeholder2=placeholder2, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) # Assert @@ -218,20 +223,22 @@ def test_serve_extrinsic_error_cases( test_id, mocker, ): - # Arrange - serving.do_serve_axon = mocker.MagicMock(return_value=(False, "Error serving axon")) - # Act + # Prep + mocker.patch.object( + mock_subtensor, "sign_and_send_extrinsic", return_value=(False, "") + ) + # Call result = serving.serve_extrinsic( - mock_subtensor, - mock_wallet, - ip, - port, - protocol, - netuid, - placeholder1, - placeholder2, - wait_for_inclusion, - wait_for_finalization, + subtensor=mock_subtensor, + wallet=mock_wallet, + ip=ip, + port=port, + protocol=protocol, + netuid=netuid, + placeholder1=placeholder1, + placeholder2=placeholder2, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) # Assert @@ -281,29 +288,32 @@ def test_serve_axon_extrinsic( mocker, ): mock_axon.external_ip = external_ip - # Arrange + # Preps with patch( "bittensor.utils.networking.get_external_ip", side_effect=Exception("Failed to fetch IP") if not external_ip_success else MagicMock(return_value="192.168.1.1"), ): - serving.do_serve_axon = mocker.MagicMock(return_value=(serve_success, "")) - # Act + mocker.patch.object( + mock_subtensor, "sign_and_send_extrinsic", return_value=(serve_success, "") + ) + + # Calls if not external_ip_success: with pytest.raises(ConnectionError): serving.serve_axon_extrinsic( - mock_subtensor, - netuid, - mock_axon, + subtensor=mock_subtensor, + netuid=netuid, + axon=mock_axon, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) else: result = serving.serve_axon_extrinsic( - mock_subtensor, - netuid, - mock_axon, + subtensor=mock_subtensor, + netuid=netuid, + axon=mock_axon, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 9c8103f1a2..d9a1a6d401 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -15,7 +15,6 @@ from bittensor.core.async_subtensor import AsyncSubtensor, logging from bittensor.core.axon import Axon from bittensor.core.chain_data import SubnetHyperparameters, SelectiveMetagraphIndex -from bittensor.core.extrinsics.serving import do_serve_axon from bittensor.core.settings import version_as_int from bittensor.core.subtensor import Subtensor from bittensor.core.types import AxonServeCallParams @@ -1222,7 +1221,10 @@ def test_serve_axon(subtensor, mocker): # Call result = subtensor.serve_axon( - fake_netuid, fake_axon, fake_wait_for_inclusion, fake_wait_for_finalization + netuid=fake_netuid, + axon=fake_axon, + wait_for_inclusion=fake_wait_for_inclusion, + wait_for_finalization=fake_wait_for_finalization, ) # Asserts @@ -1230,10 +1232,11 @@ def test_serve_axon(subtensor, mocker): subtensor=subtensor, netuid=fake_netuid, axon=fake_axon, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, certificate=fake_certificate, period=None, + raise_error=False, + wait_for_inclusion=fake_wait_for_inclusion, + wait_for_finalization=fake_wait_for_finalization, ) assert result == mocked_serve_axon_extrinsic.return_value @@ -1453,128 +1456,6 @@ def test_neuron_for_uid_success(subtensor, mocker): assert result == mocked_neuron_from_dict.return_value -@pytest.mark.parametrize( - ["fake_call_params", "expected_call_function"], - [ - (call_params(), "serve_axon"), - (call_params_with_certificate(), "serve_axon_tls"), - ], -) -def test_do_serve_axon_is_success( - subtensor, fake_wallet, mocker, fake_call_params, expected_call_function -): - """Successful do_serve_axon call.""" - # Prep - fake_wait_for_inclusion = True - fake_wait_for_finalization = True - - mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(True, "")) - - # Call - result = do_serve_axon( - subtensor=subtensor, - wallet=fake_wallet, - call_params=fake_call_params, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - # Asserts - subtensor.substrate.compose_call.assert_called_once_with( - call_module="SubtensorModule", - call_function=expected_call_function, - call_params=fake_call_params, - ) - - subtensor.sign_and_send_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, - wallet=fake_wallet, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - sign_with="hotkey", - period=None, - ) - - assert result[0] is True - assert result[1] == "" - - -def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_params): - """Unsuccessful do_serve_axon call.""" - # Prep - fake_wait_for_inclusion = True - fake_wait_for_finalization = True - - mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(False, None) - ) - - # Call - result = do_serve_axon( - subtensor=subtensor, - wallet=fake_wallet, - call_params=fake_call_params, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - # Asserts - subtensor.substrate.compose_call.assert_called_once_with( - call_module="SubtensorModule", - call_function="serve_axon", - call_params=fake_call_params, - ) - - subtensor.sign_and_send_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, - wallet=fake_wallet, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - sign_with="hotkey", - period=None, - ) - - assert result == (False, None) - - -def test_do_serve_axon_no_waits(subtensor, fake_wallet, mocker, fake_call_params): - """Unsuccessful do_serve_axon call.""" - # Prep - fake_wait_for_inclusion = False - fake_wait_for_finalization = False - - mocked_sign_and_send_extrinsic = mocker.Mock(return_value=(True, "")) - mocker.patch.object( - subtensor, "sign_and_send_extrinsic", new=mocked_sign_and_send_extrinsic - ) - - # Call - result = do_serve_axon( - subtensor=subtensor, - wallet=fake_wallet, - call_params=fake_call_params, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - # Asserts - subtensor.substrate.compose_call.assert_called_once_with( - call_module="SubtensorModule", - call_function="serve_axon", - call_params=fake_call_params, - ) - - mocked_sign_and_send_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, - wallet=fake_wallet, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - sign_with="hotkey", - period=None, - ) - assert result == (True, "") - - def test_immunity_period(subtensor, mocker): """Successful immunity_period call.""" # Preps From 2083d880c4b5b1ca06a599fd21fa129747d4ae02 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 22:08:27 -0700 Subject: [PATCH 24/49] change default values --- bittensor/core/async_subtensor.py | 2 +- bittensor/core/extrinsics/asyncex/serving.py | 2 +- bittensor/core/extrinsics/serving.py | 4 ++-- bittensor/core/subtensor.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index ae0ec9a4a2..f6c850a528 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5322,7 +5322,7 @@ async def serve_axon( certificate: Optional[Certificate] = None, period: Optional[int] = None, raise_error: bool = False, - wait_for_inclusion: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ) -> bool: """ diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index e8084b023f..9ff6359022 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -179,7 +179,7 @@ async def serve_axon_extrinsic( certificate: Optional[Certificate] = None, period: Optional[int] = None, raise_error: bool = False, - wait_for_inclusion: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ) -> bool: """ diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index 6a8a85f00c..bbec738894 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -129,7 +129,7 @@ def serve_axon_extrinsic( certificate: Optional["Certificate"] = None, period: Optional[int] = None, raise_error: bool = False, - wait_for_inclusion: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ) -> bool: """ @@ -193,10 +193,10 @@ def publish_metadata( netuid: int, data_type: str, data: Union[bytes, dict], + reset_bonds: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, period: Optional[int] = None, - reset_bonds: bool = False, ) -> bool: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index b1735c6f38..a455f8ec83 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4119,7 +4119,7 @@ def serve_axon( certificate: Optional[Certificate] = None, period: Optional[int] = None, raise_error: bool = False, - wait_for_inclusion: bool = False, + wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ) -> bool: """ From 3c046592f68e41119dce7cde286a78c296d93057 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 22:47:13 -0700 Subject: [PATCH 25/49] `.publish_metadata`, `subtensor.set_commitment` and `subtenor.set_reveal_commitment` + `subtensor.comit` renamed to `subtensor.set_commitment` + alias `subtensor.set_commitment` removed --- bittensor/core/async_subtensor.py | 115 +++++++++++-------- bittensor/core/extrinsics/asyncex/serving.py | 50 +++++--- bittensor/core/extrinsics/serving.py | 45 ++++---- bittensor/core/subtensor.py | 111 +++++++++++------- bittensor/core/subtensor_api/extrinsics.py | 1 + bittensor/core/subtensor_api/utils.py | 1 - migration.md | 5 +- tests/unit_tests/extrinsics/test_serving.py | 1 + tests/unit_tests/test_subtensor.py | 5 +- 9 files changed, 205 insertions(+), 129 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index f6c850a528..bb00e8a4b5 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -930,45 +930,6 @@ async def bonds( return b_map - async def commit( - self, wallet: "Wallet", netuid: int, data: str, period: Optional[int] = None - ) -> bool: - """Commits arbitrary data to the Bittensor network by publishing metadata. - - This method allows neurons to publish arbitrary data to the blockchain, which can be used for various purposes - such as sharing model updates, configuration data, or other network-relevant information. - - Arguments: - wallet: The wallet associated with the neuron committing the data. - netuid: The unique identifier of the subnet. - data: The data to be committed to the network. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. - - Returns: - bool: True if the commit was successful, False otherwise. - - Example: - # Commit some data to subnet 1 - success = await subtensor.commit(wallet=my_wallet, netuid=1, data="Hello Bittensor!") - - # Commit with custom period - success = await subtensor.commit(wallet=my_wallet, netuid=1, data="Model update v2.0", period=100) - - Note: See - """ - return await publish_metadata( - subtensor=self, - wallet=wallet, - netuid=netuid, - data_type=f"Raw{len(data)}", - data=data.encode(), - period=period, - ) - - set_commitment = commit - async def commit_reveal_enabled( self, netuid: int, @@ -3839,23 +3800,29 @@ async def set_reveal_commitment( blocks_until_reveal: int = 360, block_time: Union[int, float] = 12, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, int]: """ Commits arbitrary data to the Bittensor network by publishing metadata. - Arguments: + Parameters: wallet: The wallet associated with the neuron committing the data. netuid: The unique identifier of the subnetwork. data: The data to be committed to the network. - blocks_until_reveal: The number of blocks from now after which the data will be revealed. - Defaults to ``360`` (the number of blocks in one epoch). - block_time: The number of seconds between each block. Defaults to ``12``. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + blocks_until_reveal: The number of blocks from now after which the data will be revealed. Then number of + blocks in one epoch. + block_time: The number of seconds between each block. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: ``True`` if the commitment was successful, ``False`` otherwise. + bool: `True` if the commitment was successful, `False` otherwise. Note: A commitment can be set once per subnet epoch and is reset at the next epoch in the chain automatically. """ @@ -3874,6 +3841,9 @@ async def set_reveal_commitment( data_type="TimelockEncrypted", data=data_, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ), reveal_round async def subnet( @@ -5359,6 +5329,57 @@ async def serve_axon( wait_for_finalization=wait_for_finalization, ) + async def set_commitment( + self, + wallet: "Wallet", + netuid: int, + data: str, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + ) -> bool: + """ + Commits arbitrary data to the Bittensor network by publishing metadata. + + This method allows neurons to publish arbitrary data to the blockchain, which can be used for various purposes + such as sharing model updates, configuration data, or other network-relevant information. + + Parameters: + wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. + netuid (int): The unique identifier of the subnetwork. + data (str): The data to be committed to the network. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + + Returns: + bool: `True` if the commitment was successful, `False` otherwise. + + Example: + # Commit some data to subnet 1 + success = await subtensor.commit(wallet=my_wallet, netuid=1, data="Hello Bittensor!") + + # Commit with custom period + success = await subtensor.commit(wallet=my_wallet, netuid=1, data="Model update v2.0", period=100) + + Note: See + """ + return await publish_metadata( + subtensor=self, + wallet=wallet, + netuid=netuid, + data_type=f"Raw{len(data)}", + data=data.encode(), + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + async def start_call( self, wallet: "Wallet", diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index 9ff6359022..6a511718d1 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -245,34 +245,34 @@ async def publish_metadata( netuid: int, data_type: str, data: Union[bytes, dict], - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, reset_bonds: bool = False, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. - Args: - subtensor (bittensor.subtensor): The subtensor instance representing the Bittensor blockchain connection. - wallet (bittensor.wallet): The wallet object used for authentication in the transaction. - netuid (int): Network UID on which the metadata is to be published. - data_type (str): The data type of the information being submitted. It should be one of the following: + Parameters: + subtensor: The subtensor instance representing the Bittensor blockchain connection. + wallet: The wallet object used for authentication in the transaction. + netuid: Network UID on which the metadata is to be published. + data_type: The data type of the information being submitted. It should be one of the following: ``'Sha256'``, ``'Blake256'``, ``'Keccak256'``, or ``'Raw0-128'``. This specifies the format or hashing algorithm used for the data. - data (Union[bytes, dict]): The actual metadata content to be published. This should be formatted or hashed + data: The actual metadata content to be published. This should be formatted or hashed according to the ``type`` specified. (Note: max ``str`` length is 128 bytes for ``'Raw0-128'``.) - wait_for_inclusion (bool, optional): If ``True``, the function will wait for the extrinsic to be included in a - block before returning. Defaults to ``False``. - wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized - on the chain before returning. Defaults to ``True``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. - reset_bonds (bool): If `True`, the function will reset the bonds for the neuron. Defaults to `False`. + reset_bonds: If `True`, the function will reset the bonds for the neuron. Defaults to `False`. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: ``True`` if the metadata was successfully published (and finalized if specified). ``False`` otherwise. + bool: True if the subnet registration was successful, False otherwise. Raises: MetadataError: If there is an error in submitting the extrinsic, or if the response from the blockchain indicates @@ -304,6 +304,7 @@ async def publish_metadata( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: @@ -342,7 +343,20 @@ async def get_last_bonds_reset( block_hash: Optional[str] = None, reuse_block: bool = False, ) -> bytes: - """Fetches the last bonds reset triggered at commitment from the blockchain for a given hotkey and netuid.""" + """ + Fetches the last bonds reset triggered at commitment from the blockchain for a given hotkey and netuid. + + Parameters: + subtensor: Subtensor instance object. + netuid: The network uid to fetch from. + hotkey: The hotkey of the neuron for which to fetch the last bonds reset. + block: The block number to query. If ``None``, the latest block is used. + block_hash: The hash of the block to retrieve the parameter from. Do not specify if using block or reuse_block. + reuse_block: Whether to use the last-used block. Do not set if using block_hash or block. + + Returns: + bytes: The last bonds reset data for the specified hotkey and netuid. + """ block_hash = await subtensor.determine_block_hash(block, block_hash, reuse_block) block = await subtensor.substrate.query( module="Commitments", diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index bbec738894..dd62fa3669 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -194,33 +194,33 @@ def publish_metadata( data_type: str, data: Union[bytes, dict], reset_bonds: bool = False, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. - Args: - subtensor (bittensor.subtensor): The subtensor instance representing the Bittensor blockchain connection. - wallet (bittensor.wallet): The wallet object used for authentication in the transaction. - netuid (int): Network UID on which the metadata is to be published. - data_type (str): The data type of the information being submitted. It should be one of the following: + Parameters: + subtensor: The subtensor instance representing the Bittensor blockchain connection. + wallet: The wallet object used for authentication in the transaction. + netuid: Network UID on which the metadata is to be published. + data_type: The data type of the information being submitted. It should be one of the following: ``'Sha256'``, ``'Blake256'``, ``'Keccak256'``, or ``'Raw0-128'``. This specifies the format or hashing algorithm used for the data. - data (Union[bytes, dict]): The actual metadata content to be published. This should be formatted or hashed + data: The actual metadata content to be published. This should be formatted or hashed according to the ``type`` specified. (Note: max ``str`` length is 128 bytes for ``'Raw0-128'``.) - wait_for_inclusion (bool, optional): If ``True``, the function will wait for the extrinsic to be included in a - block before returning. Defaults to ``False``. - wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized - on the chain before returning. Defaults to ``True``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. - reset_bonds (bool): If `True`, the function will reset the bonds for the neuron. Defaults to `False`. + reset_bonds: If `True`, the function will reset the bonds for the neuron. Defaults to `False`. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: ``True`` if the metadata was successfully published (and finalized if specified). ``False`` otherwise. + bool: True if the subnet registration was successful, False otherwise. Raises: MetadataError: If there is an error in submitting the extrinsic, or if the response from the blockchain indicates @@ -251,6 +251,7 @@ def publish_metadata( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: @@ -277,11 +278,11 @@ def get_last_bonds_reset( """ Fetches the last bonds reset triggered at commitment from the blockchain for a given hotkey and netuid. - Args: - subtensor (bittensor.core.subtensor.Subtensor): Subtensor instance object. - netuid (int): The network uid to fetch from. - hotkey (str): The hotkey of the neuron for which to fetch the last bonds reset. - block (Optional[int]): The block number to query. If ``None``, the latest block is used. + Parameters: + subtensor: Subtensor instance object. + netuid: The network uid to fetch from. + hotkey: The hotkey of the neuron for which to fetch the last bonds reset. + block: The block number to query. If ``None``, the latest block is used. Returns: bytes: The last bonds reset data for the specified hotkey and netuid. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index a455f8ec83..3a2ca29cc2 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -530,35 +530,6 @@ def bonds( return b_map - def commit( - self, wallet, netuid: int, data: str, period: Optional[int] = None - ) -> bool: - """ - Commits arbitrary data to the Bittensor network by publishing metadata. - - Arguments: - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. - netuid (int): The unique identifier of the subnetwork. - data (str): The data to be committed to the network. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. - - Returns: - bool: `True` if the commitment was successful, `False` otherwise. - """ - return publish_metadata( - subtensor=self, - wallet=wallet, - netuid=netuid, - data_type=f"Raw{len(data)}", - data=data.encode(), - period=period, - ) - - # add explicit alias - set_commitment = commit - def commit_reveal_enabled( self, netuid: int, block: Optional[int] = None ) -> Optional[bool]: @@ -2772,20 +2743,27 @@ def set_reveal_commitment( blocks_until_reveal: int = 360, block_time: Union[int, float] = 12, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, int]: """ Commits arbitrary data to the Bittensor network by publishing metadata. - Arguments: - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. - netuid (int): The unique identifier of the subnetwork. - data (str): The data to be committed to the network. - blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to - `360`. Then number of blocks in one epoch. - block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: The wallet associated with the neuron committing the data. + netuid: The unique identifier of the subnetwork. + data: The data to be committed to the network. + blocks_until_reveal: The number of blocks from now after which the data will be revealed. Then number of + blocks in one epoch. + block_time: The number of seconds between each block. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + Returns: bool: `True` if the commitment was successful, `False` otherwise. @@ -2806,6 +2784,9 @@ def set_reveal_commitment( data_type="TimelockEncrypted", data=data_, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ), reveal_round def subnet(self, netuid: int, block: Optional[int] = None) -> Optional[DynamicInfo]: @@ -4156,6 +4137,58 @@ def serve_axon( wait_for_finalization=wait_for_finalization, ) + def set_commitment( + self, + wallet: "Wallet", + netuid: int, + data: str, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + ) -> bool: + """ + Commits arbitrary data to the Bittensor network by publishing metadata. + + This method allows neurons to publish arbitrary data to the blockchain, which can be used for various purposes + such as sharing model updates, configuration data, or other network-relevant information. + + + Parameters: + wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. + netuid (int): The unique identifier of the subnetwork. + data (str): The data to be committed to the network. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + + Returns: + bool: `True` if the commitment was successful, `False` otherwise. + + Example: + # Commit some data to subnet 1 + success = await subtensor.commit(wallet=my_wallet, netuid=1, data="Hello Bittensor!") + + # Commit with custom period + success = await subtensor.commit(wallet=my_wallet, netuid=1, data="Model update v2.0", period=100) + + Note: See + """ + return publish_metadata( + subtensor=self, + wallet=wallet, + netuid=netuid, + data_type=f"Raw{len(data)}", + data=data.encode(), + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + def start_call( self, wallet: "Wallet", diff --git a/bittensor/core/subtensor_api/extrinsics.py b/bittensor/core/subtensor_api/extrinsics.py index c2acf078e5..62c6c5c1b4 100644 --- a/bittensor/core/subtensor_api/extrinsics.py +++ b/bittensor/core/subtensor_api/extrinsics.py @@ -26,6 +26,7 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.set_subnet_identity = subtensor.set_subnet_identity self.set_weights = subtensor.set_weights self.serve_axon = subtensor.serve_axon + self.set_commitment = subtensor.set_commitment self.start_call = subtensor.start_call self.swap_stake = subtensor.swap_stake self.toggle_user_liquidity = subtensor.toggle_user_liquidity diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/subtensor_api/utils.py index b8383a3f62..34cc48502a 100644 --- a/bittensor/core/subtensor_api/utils.py +++ b/bittensor/core/subtensor_api/utils.py @@ -15,7 +15,6 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.bonds = subtensor._subtensor.bonds subtensor.burned_register = subtensor._subtensor.burned_register subtensor.chain_endpoint = subtensor._subtensor.chain_endpoint - subtensor.commit = subtensor._subtensor.commit subtensor.commit_reveal_enabled = subtensor._subtensor.commit_reveal_enabled subtensor.commit_weights = subtensor._subtensor.commit_weights subtensor.determine_block_hash = subtensor._subtensor.determine_block_hash diff --git a/migration.md b/migration.md index 29d7bd9783..b94ace9c47 100644 --- a/migration.md +++ b/migration.md @@ -190,4 +190,7 @@ wait_for_finalization: bool = False, - [x] `.set_subnet_identity_extrinsic` and `subtensor.set_subnet_identity` - [x] `.root_register_extrinsic`, `subtensor.burned_register` and `subtensor.root_register` - [x] `.serve_extrinsic` -- [x] `.serve_axon_extrinsic` and `subtensor.serve_axon` \ No newline at end of file +- [x] `.serve_axon_extrinsic` and `subtensor.serve_axon` +- [x] alias `subtensor.set_commitment` removed +- [x] `subtensor.comit` renamed to `subtensor.set_commitment` +- [x] `.publish_metadata`, `subtensor.set_commitment` and `subtenor.set_reveal_commitment` \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/test_serving.py b/tests/unit_tests/extrinsics/test_serving.py index 4fca1b7bc8..3964df1cd2 100644 --- a/tests/unit_tests/extrinsics/test_serving.py +++ b/tests/unit_tests/extrinsics/test_serving.py @@ -386,4 +386,5 @@ def test_publish_metadata( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=None, + raise_error=False, ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index d9a1a6d401..1ee5971654 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1262,7 +1262,7 @@ def test_commit(subtensor, fake_wallet, mocker): mocked_publish_metadata = mocker.patch.object(subtensor_module, "publish_metadata") # Call - result = subtensor.commit(fake_wallet, fake_netuid, fake_data) + result = subtensor.set_commitment(fake_wallet, fake_netuid, fake_data) # Asserts mocked_publish_metadata.assert_called_once_with( @@ -1272,6 +1272,9 @@ def test_commit(subtensor, fake_wallet, mocker): data_type=f"Raw{len(fake_data)}", data=fake_data.encode(), period=None, + raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) assert result is mocked_publish_metadata.return_value From a6f6aa0883b9d49a62a40ca587b59dc4dc21c946 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 23:33:11 -0700 Subject: [PATCH 26/49] `.add_stake_extrinsic`, `subtensor.add_stake` + `.add_stake_multiple_extrinsic`, `subtensor.add_stake_multiple` --- bittensor/core/async_subtensor.py | 46 ++++++----- bittensor/core/extrinsics/asyncex/staking.py | 83 +++++++++---------- bittensor/core/extrinsics/staking.py | 84 +++++++++++--------- bittensor/core/subtensor.py | 65 ++++++++------- migration.md | 4 +- tests/unit_tests/extrinsics/test_staking.py | 6 +- tests/unit_tests/test_subtensor.py | 3 + 7 files changed, 158 insertions(+), 133 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index bb00e8a4b5..f95ab6381a 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4279,36 +4279,38 @@ async def add_stake( hotkey_ss58: Optional[str] = None, netuid: Optional[int] = None, amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. Staking is a fundamental process in the Bittensor network that enables neurons to participate actively and earn incentives. - Arguments: + Parameters: wallet: The wallet to be used for staking. hotkey_ss58: The SS58 address of the hotkey associated with the neuron to which you intend to delegate your - stake. If not specified, the wallet's hotkey will be used. Defaults to ``None``. + stake. If not specified, the wallet's hotkey will be used. netuid: The unique identifier of the subnet to which the neuron belongs. amount: The amount of TAO to stake. - wait_for_inclusion: Waits for the transaction to be included in a block. Defaults to `True`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Defaults to `False`. safe_staking: If true, enables price safety checks to protect against fluctuating prices. The stake will only execute if the price change doesn't exceed the rate tolerance. Default is ``False``. allow_partial_stake: If true and safe_staking is enabled, allows partial staking when the full amount would exceed the price tolerance. If false, the entire stake fails if it would exceed the tolerance. Default is ``False``. rate_tolerance: The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price - increase. Only used when safe_staking is True. Default is ``0.005``. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. Defaults to ``None``. + increase. Only used when safe_staking is True. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: bool: ``True`` if the staking is successful, ``False`` otherwise. @@ -4324,12 +4326,13 @@ async def add_stake( hotkey_ss58=hotkey_ss58, netuid=netuid, amount=amount, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) async def add_liquidity( @@ -4390,9 +4393,10 @@ async def add_stake_multiple( hotkey_ss58s: list[str], netuids: list[int], amounts: Optional[list[Balance]] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Adds stakes to multiple neurons identified by their hotkey SS58 addresses. @@ -4403,11 +4407,12 @@ async def add_stake_multiple( hotkey_ss58s: List of ``SS58`` addresses of hotkeys to stake to. netuids: list of subnet UIDs. amounts: Corresponding amounts of TAO to stake for each hotkey. - wait_for_inclusion: Waits for the transaction to be included in a block. Defaults to `True`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Defaults to `False`. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: bool: ``True`` if the staking is successful for all specified neurons, ``False`` otherwise. @@ -4421,9 +4426,10 @@ async def add_stake_multiple( hotkey_ss58s=hotkey_ss58s, netuids=netuids, amounts=amounts, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def burned_register( diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index 76ffe73285..f32d9f1a0c 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -20,39 +20,39 @@ async def add_stake_extrinsic( hotkey_ss58: Optional[str] = None, netuid: Optional[int] = None, amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. - Staking is a fundamental process in the Bittensor network that enables neurons to participate actively and earn incentives. + Staking is a fundamental process in the Bittensor network that enables neurons to participate actively and earn + incentives. - Arguments: + Parameters: subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object. old_balance: the balance prior to the staking hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. If not - specified, the wallet's hotkey will be used. Defaults to ``None``. + specified, the wallet's hotkey will be used. netuid: The unique identifier of the subnet to which the neuron belongs. - amount: Amount to stake as Bittensor balance in TAO always, `None` if staking all. Defaults is ``None``. - wait_for_inclusion: 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. Defaults to ``True``. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, - or returns `False` if the extrinsic fails to be finalized within the timeout. Defaults to ``False``. + amount: Amount to stake as Bittensor balance in TAO always, `None` if staking all. safe_staking: If True, enables price safety checks. Default is ``False``. allow_partial_stake: If True, allows partial unstaking if price tolerance exceeded. Default is ``False``. rate_tolerance: Maximum allowed price increase percentage (0.005 = 0.5%). Default is ``0.005``. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. Defaults to ``None``. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for - finalization/inclusion, the response is `True`. + bool: True if the subnet registration was successful, False otherwise. Raises: SubstrateRequestException: Raised if the extrinsic fails to be included in the block within the timeout. @@ -158,7 +158,7 @@ async def add_stake_extrinsic( call_function=call_function, call_params=call_params, ) - staking_response, err_msg = await subtensor.sign_and_send_extrinsic( + success, message = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -167,8 +167,9 @@ async def add_stake_extrinsic( sign_with="coldkey", use_nonce=True, period=period, + raise_error=raise_error, ) - if staking_response is True: # If we successfully staked. + if success: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -199,14 +200,14 @@ async def add_stake_extrinsic( f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" ) return True + + if safe_staking and "Custom error: 8" in message: + logging.error( + ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." + ) else: - if safe_staking and "Custom error: 8" in err_msg: - logging.error( - ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") - return False + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") + return False except SubstrateRequestException as error: logging.error( @@ -222,30 +223,30 @@ async def add_stake_multiple_extrinsic( netuids: list[int], old_balance: Optional[Balance] = None, amounts: Optional[list[Balance]] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = True, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Adds a stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. + """ + Adds a stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. - Arguments: - subtensor: The initialized SubtensorInterface object. + Parameters: + subtensor: AsyncSubtensor instance with the connection to the chain. wallet: Bittensor wallet object for the coldkey. old_balance: The balance of the wallet prior to staking. hotkey_ss58s: List of hotkeys to stake to. netuids: List of netuids to stake to. amounts: List of amounts to stake. If `None`, stake all to the first hotkey. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, or - returns `False` if the extrinsic fails to be finalized within the timeout. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: `True` if extrinsic was finalized or included in the block. `True` if any wallet was staked. If we did - not wait for finalization/inclusion, the response is `True`. + bool: True if the subnet registration was successful, False otherwise. """ if not isinstance(hotkey_ss58s, list) or not all( isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s @@ -362,11 +363,11 @@ async def add_stake_multiple_extrinsic( sign_with="coldkey", use_nonce=True, period=period, + raise_error=raise_error, ) - if success is True: # If we successfully staked. - # We only wait here if we expect finalization. - + # If we successfully staked. + if success: if not wait_for_finalization and not wait_for_inclusion: old_balance -= staking_balance successful_stakes += 1 diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index fc8b69c48b..fc08c90871 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -15,15 +15,17 @@ def add_stake_extrinsic( subtensor: "Subtensor", wallet: "Wallet", + old_balance: Optional[Balance] = None, hotkey_ss58: Optional[str] = None, netuid: Optional[int] = None, amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. @@ -32,24 +34,23 @@ def add_stake_extrinsic( Arguments: subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object. + old_balance: the balance prior to the staking hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. If not - specified, the wallet's hotkey will be used. Defaults to ``None``. + specified, the wallet's hotkey will be used. netuid: The unique identifier of the subnet to which the neuron belongs. - amount: Amount to stake as Bittensor balance in TAO always, `None` if staking all. Defaults is ``None``. - wait_for_inclusion: 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. Defaults to ``True``. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, - or returns `False` if the extrinsic fails to be finalized within the timeout. Defaults to ``False``. + amount: Amount to stake as Bittensor balance in TAO always, `None` if staking all. safe_staking: If True, enables price safety checks. Default is ``False``. allow_partial_stake: If True, allows partial unstaking if price tolerance exceeded. Default is ``False``. rate_tolerance: Maximum allowed price increase percentage (0.005 = 0.5%). Default is ``0.005``. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. Defaults to ``None``. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for - finalization/inclusion, the response is `True`. + bool: True if the subnet registration was successful, False otherwise. Raises: SubstrateRequestException: Raised if the extrinsic fails to be included in the block within the timeout. @@ -67,7 +68,8 @@ def add_stake_extrinsic( logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" ) - old_balance = subtensor.get_balance(wallet.coldkeypub.ss58_address) + if not old_balance: + old_balance = subtensor.get_balance(wallet.coldkeypub.ss58_address) block = subtensor.get_current_block() # Get current stake and existential deposit @@ -160,6 +162,7 @@ def add_stake_extrinsic( sign_with="coldkey", nonce_key="coldkeypub", period=period, + raise_error=raise_error, ) if success is True: # If we successfully staked. # We only wait here if we expect finalization. @@ -189,14 +192,14 @@ def add_stake_extrinsic( f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" ) return True + + if safe_staking and "Custom error: 8" in message: + logging.error( + ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." + ) else: - if safe_staking and "Custom error: 8" in message: - logging.error( - ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") - return False + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") + return False except SubstrateRequestException as error: logging.error( @@ -210,32 +213,33 @@ def add_stake_multiple_extrinsic( wallet: "Wallet", hotkey_ss58s: list[str], netuids: list[int], + old_balance: Optional[Balance] = None, amounts: Optional[list[Balance]] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. + """ + Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. - Arguments: - subtensor: The initialized SubtensorInterface object. + Parameters: + subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object for the coldkey. + old_balance: The balance of the wallet prior to staking. hotkey_ss58s: List of hotkeys to stake to. netuids: List of netuids to stake to. amounts: List of amounts to stake. If `None`, stake all to the first hotkey. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, or - returns `False` if the extrinsic fails to be finalized within the timeout. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: `True` if extrinsic was finalized or included in the block. `True` if any wallet was staked. If we did - not wait for finalization/inclusion, the response is `True`. + bool: True if the subnet registration was successful, False otherwise. """ - if not isinstance(hotkey_ss58s, list) or not all( isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s ): @@ -283,9 +287,10 @@ def add_stake_multiple_extrinsic( total_staking_rao = sum( [amount.rao if amount is not None else 0 for amount in new_amounts] ) - old_balance = initial_balance = subtensor.get_balance( - wallet.coldkeypub.ss58_address, block=block - ) + if old_balance is None: + old_balance = initial_balance = subtensor.get_balance( + wallet.coldkeypub.ss58_address, block=block + ) if total_staking_rao == 0: # Staking all to the first wallet. if old_balance.rao > 1000: @@ -344,6 +349,7 @@ def add_stake_multiple_extrinsic( nonce_key="coldkeypub", sign_with="coldkey", period=period, + raise_error=raise_error, ) if success is True: # If we successfully staked. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 3a2ca29cc2..dfad565985 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3120,44 +3120,45 @@ def add_stake( hotkey_ss58: Optional[str] = None, netuid: Optional[int] = None, amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified - subnet. Staking is a fundamental process in the Bittensor network that enables neurons to participate - actively and earn incentives. + subnet. Staking is a fundamental process in the Bittensor network that enables neurons to participate actively + and earn incentives. - Args: + Parameters: wallet: The wallet to be used for staking. hotkey_ss58: The SS58 address of the hotkey associated with the neuron to which you intend to delegate your - stake. If not specified, the wallet's hotkey will be used. Defaults to ``None``. + stake. If not specified, the wallet's hotkey will be used. netuid: The unique identifier of the subnet to which the neuron belongs. amount: The amount of TAO to stake. - wait_for_inclusion: Waits for the transaction to be included in a block. Defaults to ``True``. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Defaults to ``False``. safe_staking: If true, enables price safety checks to protect against fluctuating prices. The stake will only execute if the price change doesn't exceed the rate tolerance. Default is ``False``. allow_partial_stake: If true and safe_staking is enabled, allows partial staking when the full amount would exceed the price tolerance. If false, the entire stake fails if it would exceed the tolerance. Default is ``False``. - rate_tolerance: The maximum allowed price change ratio when staking. For example, - 0.005 = 0.5% maximum price increase. Only used when safe_staking is True. Default is ``0.005``. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. Defaults to ``None``. + rate_tolerance: The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price + increase. Only used when safe_staking is True. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - bool: True if the staking is successful, False otherwise. + bool: ``True`` if the staking is successful, ``False`` otherwise. - This function enables neurons to increase their stake in the network, enhancing their influence and potential - rewards in line with Bittensor's consensus and reward mechanisms. - When safe_staking is enabled, it provides protection against price fluctuations during the time stake is - executed and the time it is actually processed by the chain. + This function enables neurons to increase their stake in the network, enhancing their influence and potential. + When safe_staking is enabled, it provides protection against price fluctuations during the time stake is + executed and the time it is actually processed by the chain. """ amount = check_and_convert_to_balance(amount) return add_stake_extrinsic( @@ -3166,12 +3167,13 @@ def add_stake( hotkey_ss58=hotkey_ss58, netuid=netuid, amount=amount, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) def add_liquidity( @@ -3232,30 +3234,32 @@ def add_stake_multiple( hotkey_ss58s: list[str], netuids: list[int], amounts: Optional[list[Balance]] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Adds stakes to multiple neurons identified by their hotkey SS58 addresses. This bulk operation allows for efficient staking across different neurons from a single wallet. - Parameters: + Arguments: wallet: The wallet used for staking. hotkey_ss58s: List of ``SS58`` addresses of hotkeys to stake to. - netuids: List of network UIDs to stake to. + netuids: list of subnet UIDs. amounts: Corresponding amounts of TAO to stake for each hotkey. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. Returns: - bool: ``True`` if the staking is successful for all specified neurons, False otherwise. + bool: ``True`` if the staking is successful for all specified neurons, ``False`` otherwise. This function is essential for managing stakes across multiple neurons, reflecting the dynamic and collaborative - nature of the Bittensor network. + nature of the Bittensor network. """ return add_stake_multiple_extrinsic( subtensor=self, @@ -3263,9 +3267,10 @@ def add_stake_multiple( hotkey_ss58s=hotkey_ss58s, netuids=netuids, amounts=amounts, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def burned_register( diff --git a/migration.md b/migration.md index b94ace9c47..8762090d66 100644 --- a/migration.md +++ b/migration.md @@ -193,4 +193,6 @@ wait_for_finalization: bool = False, - [x] `.serve_axon_extrinsic` and `subtensor.serve_axon` - [x] alias `subtensor.set_commitment` removed - [x] `subtensor.comit` renamed to `subtensor.set_commitment` -- [x] `.publish_metadata`, `subtensor.set_commitment` and `subtenor.set_reveal_commitment` \ No newline at end of file +- [x] `.publish_metadata`, `subtensor.set_commitment` and `subtenor.set_reveal_commitment` +- [x] `.add_stake_extrinsic`, `subtensor.add_stake` +- [x] `.add_stake_multiple_extrinsic`, `subtensor.add_stake_multiple` \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/test_staking.py b/tests/unit_tests/extrinsics/test_staking.py index 80ecb5c240..9ba6fe71a6 100644 --- a/tests/unit_tests/extrinsics/test_staking.py +++ b/tests/unit_tests/extrinsics/test_staking.py @@ -52,6 +52,7 @@ def test_add_stake_extrinsic(mocker): sign_with="coldkey", use_nonce=True, period=None, + raise_error=False, ) @@ -134,10 +135,11 @@ def test_add_stake_multiple_extrinsic(mocker): fake_subtensor.sign_and_send_extrinsic.assert_called_with( call=fake_subtensor.substrate.compose_call.return_value, wallet=fake_wallet_, - wait_for_inclusion=True, - wait_for_finalization=True, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, period=None, + raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=True, ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 1ee5971654..648f519b8d 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2746,6 +2746,7 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): allow_partial_stake=False, rate_tolerance=0.005, period=None, + raise_error=False, ) assert result == mock_add_stake_extrinsic.return_value @@ -2786,6 +2787,7 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): allow_partial_stake=False, rate_tolerance=fake_rate_tolerance, period=None, + raise_error=False, ) assert result == mock_add_stake_extrinsic.return_value @@ -2820,6 +2822,7 @@ def test_add_stake_multiple_success(mocker, fake_wallet, subtensor): wait_for_inclusion=True, wait_for_finalization=False, period=None, + raise_error=False, ) assert result == mock_add_stake_multiple_extrinsic.return_value From 1cc391be2243b09efb9a853fb199a1e82e5490a5 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 3 Sep 2025 23:44:16 -0700 Subject: [PATCH 27/49] `.start_call_extrinsic`, `subtensor.start_call` --- bittensor/core/async_subtensor.py | 17 +++++++------ .../core/extrinsics/asyncex/start_call.py | 17 +++++++------ bittensor/core/extrinsics/start_call.py | 11 +++++--- bittensor/core/subtensor.py | 25 ++++++++++--------- migration.md | 3 ++- .../extrinsics/asyncex/test_start_call.py | 1 + .../unit_tests/extrinsics/test_start_call.py | 1 + tests/unit_tests/test_async_subtensor.py | 1 + tests/unit_tests/test_subtensor.py | 1 + 9 files changed, 46 insertions(+), 31 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index f95ab6381a..4b915410a1 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5390,22 +5390,24 @@ async def start_call( self, wallet: "Wallet", netuid: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a new subnet's emission mechanism). - Arguments: + Parameters: wallet: The wallet used to sign the extrinsic (must be unlocked). netuid: The UID of the target subnet for which the call is being initiated. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. Defaults to `True`. - wait_for_finalization: Whether to wait for finalization of the extrinsic. Defaults to `False`. period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: @@ -5416,9 +5418,10 @@ async def start_call( subtensor=self, wallet=wallet, netuid=netuid, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def swap_stake( diff --git a/bittensor/core/extrinsics/asyncex/start_call.py b/bittensor/core/extrinsics/asyncex/start_call.py index 63f6fbc3c1..3a2c64937a 100644 --- a/bittensor/core/extrinsics/asyncex/start_call.py +++ b/bittensor/core/extrinsics/asyncex/start_call.py @@ -12,23 +12,25 @@ async def start_call_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", netuid: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a new subnet's emission mechanism). - Args: - subtensor (Subtensor): The Subtensor client instance used for blockchain interaction. - wallet (Wallet): The wallet used to sign the extrinsic (must be unlocked). - netuid (int): The UID of the target subnet for which the call is being initiated. - wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. + Parameters: + subtensor: The Subtensor client instance used for blockchain interaction. + wallet: The wallet used to sign the extrinsic (must be unlocked). + netuid: The UID of the target subnet for which the call is being initiated. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -52,6 +54,7 @@ async def start_call_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/extrinsics/start_call.py b/bittensor/core/extrinsics/start_call.py index 2788bb88f1..4a0b37e382 100644 --- a/bittensor/core/extrinsics/start_call.py +++ b/bittensor/core/extrinsics/start_call.py @@ -12,23 +12,25 @@ def start_call_extrinsic( subtensor: "Subtensor", wallet: "Wallet", netuid: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a new subnet's emission mechanism). - Args: + Parameters: subtensor (Subtensor): The Subtensor client instance used for blockchain interaction. wallet (Wallet): The wallet used to sign the extrinsic (must be unlocked). netuid (int): The UID of the target subnet for which the call is being initiated. - wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. - wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -51,6 +53,7 @@ def start_call_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index dfad565985..2dc16bd679 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4198,24 +4198,24 @@ def start_call( self, wallet: "Wallet", netuid: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a new subnet's emission mechanism). - Args: - wallet (Wallet): The wallet used to sign the extrinsic (must be unlocked). - netuid (int): The UID of the target subnet for which the call is being initiated. - wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. - Defaults to `True`. - wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. - Defaults to `False`. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: The wallet used to sign the extrinsic (must be unlocked). + netuid: The UID of the target subnet for which the call is being initiated. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: Tuple[bool, str]: @@ -4226,9 +4226,10 @@ def start_call( subtensor=self, wallet=wallet, netuid=netuid, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def swap_stake( diff --git a/migration.md b/migration.md index 8762090d66..2eb52422b2 100644 --- a/migration.md +++ b/migration.md @@ -195,4 +195,5 @@ wait_for_finalization: bool = False, - [x] `subtensor.comit` renamed to `subtensor.set_commitment` - [x] `.publish_metadata`, `subtensor.set_commitment` and `subtenor.set_reveal_commitment` - [x] `.add_stake_extrinsic`, `subtensor.add_stake` -- [x] `.add_stake_multiple_extrinsic`, `subtensor.add_stake_multiple` \ No newline at end of file +- [x] `.add_stake_multiple_extrinsic`, `subtensor.add_stake_multiple` +- [x] `.start_call_extrinsic`, `subtensor.start_call` \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/asyncex/test_start_call.py b/tests/unit_tests/extrinsics/asyncex/test_start_call.py index d99295195e..68507acbd2 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_start_call.py +++ b/tests/unit_tests/extrinsics/asyncex/test_start_call.py @@ -36,6 +36,7 @@ async def test_start_call_extrinsics(subtensor, mocker, fake_wallet): wait_for_inclusion=True, wait_for_finalization=False, period=None, + raise_error=False, ) assert success is True diff --git a/tests/unit_tests/extrinsics/test_start_call.py b/tests/unit_tests/extrinsics/test_start_call.py index ece0e6cf42..03373fa955 100644 --- a/tests/unit_tests/extrinsics/test_start_call.py +++ b/tests/unit_tests/extrinsics/test_start_call.py @@ -34,6 +34,7 @@ def test_start_call_extrinsics(subtensor, mocker, fake_wallet): wait_for_inclusion=True, wait_for_finalization=False, period=None, + raise_error=False, ) assert success is True diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index ecc791bfaa..662aa4e8a8 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3113,6 +3113,7 @@ async def test_start_call(subtensor, mocker): wait_for_inclusion=True, wait_for_finalization=False, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 648f519b8d..71210f6b5e 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3236,6 +3236,7 @@ def test_start_call(subtensor, mocker): wait_for_inclusion=True, wait_for_finalization=False, period=None, + raise_error=False, ) assert result == mocked_extrinsic.return_value From 38201475c9cc9facf3815363502ddc3b9d86b668 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 00:14:13 -0700 Subject: [PATCH 28/49] fix `test_dendrite` --- tests/e2e_tests/test_dendrite.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/test_dendrite.py b/tests/e2e_tests/test_dendrite.py index c4a8085444..fcd601577d 100644 --- a/tests/e2e_tests/test_dendrite.py +++ b/tests/e2e_tests/test_dendrite.py @@ -15,6 +15,9 @@ wait_to_start_call, ) +logging.on() +logging.set_debug() + @pytest.mark.asyncio async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): @@ -108,7 +111,7 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): ) assert subtensor.staking.add_stake( - bob_wallet, + wallet=bob_wallet, netuid=alice_subnet_netuid, amount=tao, ) @@ -188,6 +191,8 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall wallet=alice_wallet, netuid=alice_subnet_netuid, amount=Balance.from_tao(1), + wait_for_inclusion=False, + wait_for_finalization=False, ) # update max_allowed_validators so only one neuron can get validator_permit @@ -242,9 +247,11 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall ).tao_to_alpha_with_slippage(tao) assert await async_subtensor.staking.add_stake( - bob_wallet, + wallet=bob_wallet, netuid=alice_subnet_netuid, amount=tao, + wait_for_inclusion=False, + wait_for_finalization=False, ) # Refresh metagraph From 162e30dc95e859de0137db12f73d039253480ce3 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 00:54:12 -0700 Subject: [PATCH 29/49] `.increase_take_extrinsic`, `.decrease_take_extrinsic` and `subtenor.set_reveal_commitment` --- bittensor/core/async_subtensor.py | 59 +++++++++---------- bittensor/core/extrinsics/asyncex/take.py | 69 ++++++++++++----------- bittensor/core/extrinsics/take.py | 69 ++++++++++++----------- bittensor/core/subtensor.py | 60 +++++++++----------- migration.md | 7 ++- 5 files changed, 131 insertions(+), 133 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 4b915410a1..d46bfb13a7 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5035,30 +5035,30 @@ async def set_delegate_take( wallet: "Wallet", hotkey_ss58: str, take: float, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - raise_error: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Sets the delegate 'take' percentage for a neuron identified by its hotkey. The 'take' represents the percentage of rewards that the delegate claims from its nominators' stakes. - Arguments: + Parameters: wallet: bittensor wallet instance. hotkey_ss58: The ``SS58`` address of the neuron's hotkey. take: Percentage reward for the delegate. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on_error: Raises a relevant exception - rather than returning ``False`` if unsuccessful. - raise_error: raises a relevant exception rather than returning ``False`` if unsuccessful. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. Raises: DelegateTakeTooHigh: Delegate take is too high. @@ -5073,7 +5073,6 @@ async def set_delegate_take( The delegate take is a critical parameter in the network's incentive structure, influencing the distribution of rewards among neurons and their nominators. """ - # u16 representation of the take take_u16 = int(take * 0xFFFF) @@ -5086,33 +5085,27 @@ async def set_delegate_take( logging.info(f"Updating {hotkey_ss58} take: current={current_take} new={take}") - if current_take_u16 < take_u16: - success, error = await increase_take_extrinsic( - self, - wallet, - hotkey_ss58, - take_u16, - wait_for_finalization=wait_for_finalization, - wait_for_inclusion=wait_for_inclusion, - raise_error=raise_error, - period=period, - ) - else: - success, error = await decrease_take_extrinsic( - self, - wallet, - hotkey_ss58, - take_u16, - wait_for_finalization=wait_for_finalization, - wait_for_inclusion=wait_for_inclusion, - raise_error=raise_error, - period=period, - ) + extrinsic_call = ( + increase_take_extrinsic + if current_take_u16 < take_u16 + else decrease_take_extrinsic + ) + + success, message = await extrinsic_call( + subtensor=self, + wallet=wallet, + hotkey_ss58=hotkey_ss58, + take=take_u16, + period=period, + raise_error=raise_error, + wait_for_finalization=wait_for_finalization, + wait_for_inclusion=wait_for_inclusion, + ) if success: logging.info(":white_heavy_check_mark: [green]Take Updated[/green]") - return success, error + return success, message async def set_subnet_identity( self, diff --git a/bittensor/core/extrinsics/asyncex/take.py b/bittensor/core/extrinsics/asyncex/take.py index 543d4e72da..3840821c08 100644 --- a/bittensor/core/extrinsics/asyncex/take.py +++ b/bittensor/core/extrinsics/asyncex/take.py @@ -13,27 +13,29 @@ async def increase_take_extrinsic( wallet: Wallet, hotkey_ss58: str, take: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - raise_error: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """Sets the delegate 'take' percentage for a neuron identified by its hotkey. - Args: - subtensor (Subtensor): Blockchain connection. - wallet (Wallet): The wallet to sign the extrinsic. - hotkey_ss58 (str): SS58 address of the hotkey to set take for. - take (int): The percentage of rewards that the delegate claims from nominators. - wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. - wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. - raise_error (bool, optional): Raise error on failure. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: The Subtensor instance. + wallet: The wallet to sign the extrinsic. + hotkey_ss58: SS58 address of the hotkey to set take for. + take: The percentage of rewards that the delegate claims from nominators. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: Success flag and status message. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ unlock = unlock_key(wallet, raise_error=raise_error) @@ -65,27 +67,30 @@ async def decrease_take_extrinsic( wallet: Wallet, hotkey_ss58: str, take: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - raise_error: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: - """Sets the delegate 'take' percentage for a neuron identified by its hotkey. - - Args: - subtensor (Subtensor): Blockchain connection. - wallet (Wallet): The wallet to sign the extrinsic. - hotkey_ss58 (str): SS58 address of the hotkey to set take for. - take (int): The percentage of rewards that the delegate claims from nominators. - wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. - wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. - raise_error (bool, optional): Raise error on failure. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + """ + Sets the delegate 'take' percentage for a neuron identified by its hotkey. + + Parameters: + subtensor: The Subtensor instance. + wallet: The wallet to sign the extrinsic. + hotkey_ss58: SS58 address of the hotkey to set take for. + take: The percentage of rewards that the delegate claims from nominators. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: Success flag and status message. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ unlock = unlock_key(wallet, raise_error=raise_error) @@ -104,8 +109,8 @@ async def decrease_take_extrinsic( return await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index 968338d8a9..3effd7699b 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -13,27 +13,29 @@ def increase_take_extrinsic( wallet: Wallet, hotkey_ss58: str, take: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - raise_error: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: """Sets the delegate 'take' percentage for a neuron identified by its hotkey. - Args: - subtensor (Subtensor): Blockchain connection. - wallet (Wallet): The wallet to sign the extrinsic. - hotkey_ss58 (str): SS58 address of the hotkey to set take for. - take (int): The percentage of rewards that the delegate claims from nominators. - wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. - wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. - raise_error (bool, optional): Raise error on failure. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: The Subtensor instance. + wallet: The wallet to sign the extrinsic. + hotkey_ss58: SS58 address of the hotkey to set take for. + take: The percentage of rewards that the delegate claims from nominators. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: Success flag and status message. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ unlock = unlock_key(wallet, raise_error=raise_error) @@ -64,27 +66,30 @@ def decrease_take_extrinsic( wallet: Wallet, hotkey_ss58: str, take: int, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - raise_error: bool = False, - period: Optional[int] = None, ) -> tuple[bool, str]: - """Sets the delegate `take` percentage for a neuron identified by its hotkey. - - Args: - subtensor (Subtensor): Blockchain connection. - wallet (Wallet): The wallet to sign the extrinsic. - hotkey_ss58 (str): SS58 address of the hotkey to set take for. - take (int): The percentage of rewards that the delegate claims from nominators. - wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. - wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. - raise_error (bool, optional): Raise error on failure. Defaults to False. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. - If the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + """ + Sets the delegate `take` percentage for a neuron identified by its hotkey. + + Parameters: + subtensor: The Subtensor instance. + wallet: The wallet to sign the extrinsic. + hotkey_ss58: SS58 address of the hotkey to set take for. + take: The percentage of rewards that the delegate claims from nominators. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: Success flag and status message. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. """ unlock = unlock_key(wallet, raise_error=raise_error) @@ -103,8 +108,8 @@ def decrease_take_extrinsic( return subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, - wait_for_inclusion=wait_for_inclusion, + period=period, raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 2dc16bd679..2d50e9b819 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3867,19 +3867,20 @@ def set_delegate_take( The 'take' represents the percentage of rewards that the delegate claims from its nominators' stakes. Parameters: - wallet (bittensor_wallet.Wallet): bittensor wallet instance. - hotkey_ss58 (str): The ``SS58`` address of the neuron's hotkey. - take (float): Percentage reward for the delegate. - 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. - raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's + wallet: bittensor wallet instance. + hotkey_ss58: The ``SS58`` address of the neuron's hotkey. + take: Percentage reward for the delegate. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. Raises: DelegateTakeTooHigh: Delegate take is too high. @@ -3894,7 +3895,6 @@ def set_delegate_take( The delegate take is a critical parameter in the network's incentive structure, influencing the distribution of rewards among neurons and their nominators. """ - # u16 representation of the take take_u16 = int(take * 0xFFFF) @@ -3907,33 +3907,27 @@ def set_delegate_take( logging.info(f"Updating {hotkey_ss58} take: current={current_take} new={take}") - if current_take_u16 < take_u16: - success, error = increase_take_extrinsic( - self, - wallet, - hotkey_ss58, - take_u16, - wait_for_finalization=wait_for_finalization, - wait_for_inclusion=wait_for_inclusion, - raise_error=raise_error, - period=period, - ) - else: - success, error = decrease_take_extrinsic( - self, - wallet, - hotkey_ss58, - take_u16, - wait_for_finalization=wait_for_finalization, - wait_for_inclusion=wait_for_inclusion, - raise_error=raise_error, - period=period, - ) + extrinsic_call = ( + increase_take_extrinsic + if current_take_u16 < take_u16 + else decrease_take_extrinsic + ) + + success, message = extrinsic_call( + subtensor=self, + wallet=wallet, + hotkey_ss58=hotkey_ss58, + take=take_u16, + period=period, + raise_error=raise_error, + wait_for_finalization=wait_for_finalization, + wait_for_inclusion=wait_for_inclusion, + ) if success: logging.info(":white_heavy_check_mark: [green]Take Updated[/green]") - return success, error + return success, message def set_subnet_identity( self, diff --git a/migration.md b/migration.md index 2eb52422b2..64a2ba3960 100644 --- a/migration.md +++ b/migration.md @@ -194,6 +194,7 @@ wait_for_finalization: bool = False, - [x] alias `subtensor.set_commitment` removed - [x] `subtensor.comit` renamed to `subtensor.set_commitment` - [x] `.publish_metadata`, `subtensor.set_commitment` and `subtenor.set_reveal_commitment` -- [x] `.add_stake_extrinsic`, `subtensor.add_stake` -- [x] `.add_stake_multiple_extrinsic`, `subtensor.add_stake_multiple` -- [x] `.start_call_extrinsic`, `subtensor.start_call` \ No newline at end of file +- [x] `.add_stake_extrinsic` and `subtensor.add_stake` +- [x] `.add_stake_multiple_extrinsic` and `subtensor.add_stake_multiple` +- [x] `.start_call_extrinsic` and `subtensor.start_call` +- [x] `.increase_take_extrinsic`, `.decrease_take_extrinsic` and `subtenor.set_reveal_commitment` \ No newline at end of file From 6f17d02992633eff50c6cc8afad99d12cc993950 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 01:50:10 -0700 Subject: [PATCH 30/49] no flaky behavior for `tests/e2e_tests/test_dendrite.py` --- tests/e2e_tests/test_dendrite.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/e2e_tests/test_dendrite.py b/tests/e2e_tests/test_dendrite.py index fcd601577d..d78531ef75 100644 --- a/tests/e2e_tests/test_dendrite.py +++ b/tests/e2e_tests/test_dendrite.py @@ -52,12 +52,13 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): "Subnet is not active." ) - # Make sure Alice is Top Validator - assert subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - amount=Balance.from_tao(1), - ) + # Make sure Alice is Top Validator (for non-fast-runtime only) + if subtensor.chain.is_fast_blocks(): + assert subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + amount=Balance.from_tao(1), + ) # update max_allowed_validators so only one neuron can get validator_permit assert sudo_set_admin_utils( @@ -118,6 +119,7 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): # Refresh metagraph metagraph = subtensor.metagraphs.metagraph(alice_subnet_netuid) + metagraph.sync() bob_neuron = metagraph.neurons[1] # Assert alpha is close to stake equivalent @@ -186,14 +188,15 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall "Subnet is not active." ) - # Make sure Alice is Top Validator - assert await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - amount=Balance.from_tao(1), - wait_for_inclusion=False, - wait_for_finalization=False, - ) + # Make sure Alice is Top Validator (for non-fast-runtime only) + if await async_subtensor.chain.is_fast_blocks(): + assert await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + amount=Balance.from_tao(1), + wait_for_inclusion=False, + wait_for_finalization=False, + ) # update max_allowed_validators so only one neuron can get validator_permit assert await async_sudo_set_admin_utils( From 7626866332c6c71e7004f37c752cd1659a482f6b Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 03:19:23 -0700 Subject: [PATCH 31/49] `.transfer_extrinsic` and `subtensor.transfer` --- bittensor/core/async_subtensor.py | 24 ++++++----- bittensor/core/extrinsics/asyncex/transfer.py | 41 +++++++++--------- bittensor/core/extrinsics/transfer.py | 42 +++++++++---------- bittensor/core/subtensor.py | 34 ++++++++------- migration.md | 3 +- .../extrinsics/asyncex/test_transfer.py | 2 + tests/unit_tests/extrinsics/test_transfer.py | 2 + tests/unit_tests/test_async_subtensor.py | 1 + tests/unit_tests/test_subtensor.py | 1 + 9 files changed, 81 insertions(+), 69 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index d8c47b61ec..875615573f 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5531,25 +5531,28 @@ async def transfer( destination: str, amount: Optional[Balance], transfer_all: bool = False, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, keep_alive: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, ) -> bool: """ Transfer token of amount to destination. - Arguments: + Parameters: wallet: Source wallet for the transfer. destination: Destination address for the transfer. amount: Number of tokens to transfer. `None` is transferring all. transfer_all: Flag to transfer all tokens. Default is `False`. - wait_for_inclusion: Waits for the transaction to be included in a block. Defaults to `True`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Defaults to `False`. keep_alive: Flag to keep the connection alive. Default is `True`. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. + Returns: `True` if the transferring was successful, otherwise `False`. """ @@ -5561,10 +5564,11 @@ async def transfer( destination=destination, amount=amount, transfer_all=transfer_all, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, keep_alive=keep_alive, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) async def transfer_stake( diff --git a/bittensor/core/extrinsics/asyncex/transfer.py b/bittensor/core/extrinsics/asyncex/transfer.py index 365441d51c..3e3d6d7193 100644 --- a/bittensor/core/extrinsics/asyncex/transfer.py +++ b/bittensor/core/extrinsics/asyncex/transfer.py @@ -4,9 +4,9 @@ from bittensor.core.settings import NETWORK_EXPLORER_MAP from bittensor.utils import ( get_explorer_url_for_network, + get_transfer_fn_params, is_valid_bittensor_address_or_public_key, unlock_key, - get_transfer_fn_params, ) from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -21,33 +21,31 @@ async def transfer_extrinsic( wallet: "Wallet", destination: str, amount: Optional[Balance], - transfer_all: bool = False, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, keep_alive: bool = True, + transfer_all: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """Transfers funds from this wallet to the destination public key address. - Args: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): initialized AsyncSubtensor object used for transfer - wallet (bittensor_wallet.Wallet): Bittensor wallet object to make transfer from. - destination (str): Destination public key address (ss58_address or ed25519) of recipient. - amount (Optional[bittensor.utils.balance.Balance]): Amount to stake as Bittensor balance. `None` if - transferring all. - transfer_all (bool): Whether to transfer all funds from this wallet to the destination address. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - keep_alive (bool): If set, keeps the account alive by keeping the balance above the existential deposit. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. - If the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: The Subtensor instance. + wallet: The wallet to sign the extrinsic. + destination: Destination public key address (ss58_address or ed25519) of recipient. + amount: Amount to stake as Bittensor balance. `None` if transferring all. + transfer_all: Whether to transfer all funds from this wallet to the destination address. + keep_alive: If set, keeps the account alive by keeping the balance above the existential deposit. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is `True`, regardless of its inclusion. + bool: True if the subnet registration was successful, False otherwise. """ if amount is None and not transfer_all: logging.error("If not transferring all, `amount` must be specified.") @@ -114,6 +112,7 @@ async def transfer_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index 0d0a8377b5..7d2a28ad9f 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -2,10 +2,10 @@ from bittensor.core.settings import NETWORK_EXPLORER_MAP from bittensor.utils import ( - is_valid_bittensor_address_or_public_key, - unlock_key, get_explorer_url_for_network, get_transfer_fn_params, + is_valid_bittensor_address_or_public_key, + unlock_key, ) from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -20,32 +20,31 @@ def transfer_extrinsic( wallet: "Wallet", destination: str, amount: Optional[Balance], - transfer_all: bool = False, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, keep_alive: bool = True, + transfer_all: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """Transfers funds from this wallet to the destination public key address. - Args: - subtensor (bittensor.core.subtensor.Subtensor): the Subtensor object used for transfer - wallet (bittensor_wallet.Wallet): Bittensor wallet object to make transfer from. - destination (str): Destination public key address (ss58_address or ed25519) of recipient. - amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. `None` if transferring all. - transfer_all (bool): Whether to transfer all funds from this wallet to the destination address. - 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. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning - `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - keep_alive (bool): If set, keeps the account alive by keeping the balance above the existential deposit. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. - If the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + Parameters: + subtensor: the Subtensor object used for transfer + wallet: The wallet to sign the extrinsic. + destination: Destination public key address (ss58_address or ed25519) of recipient. + amount: Amount to stake as Bittensor balance. `None` if transferring all. + transfer_all: Whether to transfer all funds from this wallet to the destination address. + keep_alive: If set, keeps the account alive by keeping the balance above the existential deposit. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is `True`, regardless of its inclusion. + bool: True if the subnet registration was successful, False otherwise. """ if amount is None and not transfer_all: logging.error("If not transferring all, `amount` must be specified.") @@ -110,6 +109,7 @@ def transfer_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, + raise_error=raise_error, ) if success: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index f006ec92d1..83f1c62e11 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4338,27 +4338,28 @@ def transfer( wallet: "Wallet", destination: str, amount: Optional[Balance], - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, transfer_all: bool = False, keep_alive: bool = True, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, ) -> bool: """ Transfer token of amount to destination. - Arguments: - wallet (bittensor_wallet.Wallet): Source wallet for the transfer. - destination (str): Destination address for the transfer. - amount (float): Amount of tao to transfer. - transfer_all (bool): Flag to transfer all tokens. Default is ``False``. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``True``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``False``. - keep_alive (bool): Flag to keep the connection alive. Default is ``True``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: Source wallet for the transfer. + destination: Destination address for the transfer. + amount: Number of tokens to transfer. `None` is transferring all. + transfer_all: Flag to transfer all tokens. Default is `False`. + keep_alive: Flag to keep the connection alive. Default is `True`. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: `True` if the transferring was successful, otherwise `False`. @@ -4371,10 +4372,11 @@ def transfer( destination=destination, amount=amount, transfer_all=transfer_all, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, keep_alive=keep_alive, period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) def transfer_stake( diff --git a/migration.md b/migration.md index 64a2ba3960..f01ab6f02e 100644 --- a/migration.md +++ b/migration.md @@ -197,4 +197,5 @@ wait_for_finalization: bool = False, - [x] `.add_stake_extrinsic` and `subtensor.add_stake` - [x] `.add_stake_multiple_extrinsic` and `subtensor.add_stake_multiple` - [x] `.start_call_extrinsic` and `subtensor.start_call` -- [x] `.increase_take_extrinsic`, `.decrease_take_extrinsic` and `subtenor.set_reveal_commitment` \ No newline at end of file +- [x] `.increase_take_extrinsic`, `.decrease_take_extrinsic` and `subtenor.set_reveal_commitment` +- [x] `.transfer_extrinsic` and `subtensor.transfer` \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/asyncex/test_transfer.py b/tests/unit_tests/extrinsics/asyncex/test_transfer.py index a755c8c894..3992fa33bb 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_transfer.py +++ b/tests/unit_tests/extrinsics/asyncex/test_transfer.py @@ -68,6 +68,7 @@ async def test_transfer_extrinsic_success(subtensor, fake_wallet, mocker): wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) assert result is True @@ -140,6 +141,7 @@ async def test_transfer_extrinsic_call_successful_with_failed_response( wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) assert result is False diff --git a/tests/unit_tests/extrinsics/test_transfer.py b/tests/unit_tests/extrinsics/test_transfer.py index b4ceee33f8..b885977de8 100644 --- a/tests/unit_tests/extrinsics/test_transfer.py +++ b/tests/unit_tests/extrinsics/test_transfer.py @@ -67,6 +67,7 @@ def test_transfer_extrinsic_success(subtensor, fake_wallet, mocker): wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) assert result is True @@ -137,6 +138,7 @@ def test_transfer_extrinsic_call_successful_with_failed_response( wait_for_inclusion=True, wait_for_finalization=True, period=None, + raise_error=False, ) assert result is False diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 662aa4e8a8..490c21bf77 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2604,6 +2604,7 @@ async def test_transfer_success(subtensor, fake_wallet, mocker): wait_for_finalization=False, keep_alive=True, period=None, + raise_error=False, ) assert result == mocked_transfer_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 71210f6b5e..7a0170a17a 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1333,6 +1333,7 @@ def test_transfer(subtensor, fake_wallet, mocker): wait_for_finalization=fake_wait_for_finalization, keep_alive=True, period=None, + raise_error=False, ) assert result == mocked_transfer_extrinsic.return_value From 232a12cb17ce7601aaf49c309c711e22c5e57e2c Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 14:03:13 -0700 Subject: [PATCH 32/49] `.unstake_extrinsic` and `subtensor.unstake` + changes + tests --- bittensor/core/async_subtensor.py | 57 ++++++----- .../core/extrinsics/asyncex/unstaking.py | 94 ++++++++----------- bittensor/core/extrinsics/unstaking.py | 86 +++++++---------- bittensor/core/subtensor.py | 60 ++++++------ migration.md | 8 +- tests/e2e_tests/test_staking.py | 72 ++++++-------- .../extrinsics/asyncex/test_unstaking.py | 1 + tests/unit_tests/extrinsics/test_unstaking.py | 1 + tests/unit_tests/test_subtensor.py | 20 ++-- 9 files changed, 178 insertions(+), 221 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 875615573f..dd4bbbc706 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5622,40 +5622,39 @@ async def transfer_stake( async def unstake( self, wallet: "Wallet", - hotkey_ss58: Optional[str] = None, - netuid: Optional[int] = None, # TODO why is this optional? - amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - safe_staking: bool = False, + netuid: int, + hotkey_ss58: str, + amount: Balance, allow_partial_stake: bool = False, + safe_staking: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, - unstake_all: bool = False, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting individual neuron stakes within the Bittensor network. - Arguments: - wallet: The wallet associated with the neuron from which the stake is being - removed. - hotkey_ss58: The `SS58` address of the hotkey account to unstake from. + Parameters: + wallet: The wallet associated with the neuron from which the stake is being removed. netuid: The unique identifier of the subnet. - amount: The amount of alpha to unstake. If not specified, unstakes all. - wait_for_inclusion: Waits for the transaction to be included in a block. Defaults to `True`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Defaults to `False`. - safe_staking: If true, enables price safety checks to protect against fluctuating prices. The unstake will - only execute if the price change doesn't exceed the rate tolerance. Default is False. + hotkey_ss58: The ``SS58`` address of the hotkey account to unstake from. + amount: The amount of alpha to unstake. If not specified, unstakes all. Alpha amount. allow_partial_stake: If true and safe_staking is enabled, allows partial unstaking when - the full amount would exceed the price threshold. If false, the entire unstake fails if it would exceed - the threshold. Default is False. - rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum - price decrease. Only used when safe_staking is True. Default is 0.005. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. - unstake_all: If `True`, unstakes all tokens and `amount` is ignored. Default is `False` + the full amount would exceed the price tolerance. If false, the entire unstake fails if it would + exceed the tolerance. + rate_tolerance: The maximum allowed price change ratio when unstaking. For example, + 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. + safe_staking: If true, enables price safety checks to protect against fluctuating prices. The unstake + will only execute if the price change doesn't exceed the rate tolerance. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: bool: `True` if the unstaking process is successful, False otherwise. @@ -5670,13 +5669,13 @@ async def unstake( hotkey_ss58=hotkey_ss58, netuid=netuid, amount=amount, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + safe_staking=safe_staking, period=period, - unstake_all=unstake_all, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) async def unstake_all( @@ -5686,7 +5685,7 @@ async def unstake_all( netuid: int, rate_tolerance: Optional[float] = 0.005, wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, + wait_for_finalization: bool = True, period: Optional[int] = None, ) -> tuple[bool, str]: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index fcc0416dd5..f62359b3da 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -16,54 +16,44 @@ async def unstake_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", - hotkey_ss58: Optional[str] = None, - netuid: Optional[int] = None, - amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - safe_staking: bool = False, + netuid: int, + hotkey_ss58: str, + amount: Balance, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + safe_staking: bool = False, period: Optional[int] = None, - unstake_all: bool = False, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Removes stake into the wallet coldkey from the specified hotkey ``uid``. + """ + Removes stake into the wallet coldkey from the specified hotkey ``uid``. - Args: - subtensor: AsyncSubtensor instance. + Parameters: + subtensor: Subtensor instance. wallet: Bittensor wallet object. - hotkey_ss58: The ``ss58`` address of the hotkey to unstake from. By default, the wallet hotkey is used. - netuid: The subnet uid to unstake from. - amount: Amount to stake as Bittensor balance, or ``float`` interpreted as Tao. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning ``True``, - or returns ``False`` if the extrinsic fails to be finalized within the timeout. - safe_staking: If true, enables price safety checks - allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded - rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%) + netuid: Subnet unique id. + hotkey_ss58: The ``ss58`` address of the hotkey to unstake from. + amount: Amount to stake as Bittensor balance. + allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded. + safe_staking: If true, enables price safety checks. + rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%). period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - unstake_all: If true, unstakes all tokens. Default is ``False``. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: - A tuple containing: - - `True` and a success message if the unstake operation succeeded; - - `False` and an error message otherwise. + bool: True if the subnet registration was successful, False otherwise. """ - if amount and unstake_all: - raise ValueError("Cannot specify both `amount` and `unstake_all`.") - # Decrypt keys, if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) return False - if hotkey_ss58 is None: - hotkey_ss58 = wallet.hotkey.ss58_address # Default to wallet's own hotkey. - logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" ) @@ -78,24 +68,13 @@ async def unstake_extrinsic( ), ) - # Convert to bittensor.Balance - if amount is None: - # Unstake it all. - unstaking_balance = old_stake - logging.warning( - f"Didn't receive any unstaking amount. Unstaking all existing stake: [blue]{old_stake}[/blue] " - f"from hotkey: [blue]{hotkey_ss58}[/blue]" - ) - else: - unstaking_balance = amount - unstaking_balance.set_unit(netuid) + amount.set_unit(netuid) # Check enough to unstake. - stake_on_uid = old_stake - if unstaking_balance > stake_on_uid: + if amount > old_stake: logging.error( - f":cross_mark: [red]Not enough stake[/red]: [green]{stake_on_uid}[/green] to unstake: " - f"[blue]{unstaking_balance}[/blue] from hotkey: [yellow]{wallet.hotkey_str}[/yellow]" + f":cross_mark: [red]Not enough stake[/red]: [green]{old_stake}[/green] to unstake: " + f"[blue]{amount}[/blue] from hotkey: [yellow]{wallet.hotkey_str}[/yellow]" ) return False @@ -103,7 +82,7 @@ async def unstake_extrinsic( call_params = { "hotkey": hotkey_ss58, "netuid": netuid, - "amount_unstaked": unstaking_balance.rao, + "amount_unstaked": amount.rao, } if safe_staking: pool = await subtensor.subnet(netuid=netuid) @@ -116,7 +95,7 @@ async def unstake_extrinsic( logging_info = ( f":satellite: [magenta]Safe Unstaking from:[/magenta] " - f"netuid: [green]{netuid}[/green], amount: [green]{unstaking_balance}[/green], " + f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green], " f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " f"price limit: [green]{price_with_tolerance}[/green], " f"original price: [green]{base_price}[/green], " @@ -135,7 +114,7 @@ async def unstake_extrinsic( else: logging_info = ( f":satellite: [magenta]Unstaking from:[/magenta] " - f"netuid: [green]{netuid}[/green], amount: [green]{unstaking_balance}[/green] " + f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green] " f"on [blue]{subtensor.network}[/blue]" ) call_function = "remove_stake" @@ -158,9 +137,10 @@ async def unstake_extrinsic( sign_with="coldkey", use_nonce=True, period=period, + raise_error=raise_error, ) - if success is True: # If we successfully unstaked. + if success: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -190,14 +170,14 @@ async def unstake_extrinsic( f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" ) return True + + if safe_staking and "Custom error: 8" in message: + logging.error( + ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." + ) else: - if safe_staking and "Custom error: 8" in message: - logging.error( - ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") - return False + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") + return False except SubstrateRequestException as error: logging.error( diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index 8f0ce3329d..10c021ca05 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -16,54 +16,44 @@ def unstake_extrinsic( subtensor: "Subtensor", wallet: "Wallet", - hotkey_ss58: Optional[str] = None, - netuid: Optional[int] = None, - amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - safe_staking: bool = False, + netuid: int, + hotkey_ss58: str, + amount: Balance, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + safe_staking: bool = False, period: Optional[int] = None, - unstake_all: bool = False, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Removes stake into the wallet coldkey from the specified hotkey ``uid``. + """ + Removes stake into the wallet coldkey from the specified hotkey ``uid``. - Args: + Parameters: subtensor: Subtensor instance. wallet: Bittensor wallet object. - hotkey_ss58: The ``ss58`` address of the hotkey to unstake from. By default, the wallet hotkey is used. + hotkey_ss58: The ``ss58`` address of the hotkey to unstake from. netuid: Subnet unique id. amount: Amount to stake as Bittensor balance. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning ``True``, - or returns ``False`` if the extrinsic fails to be finalized within the timeout. + allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded. + rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%). safe_staking: If true, enables price safety checks. - allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded - rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%) period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - unstake_all: If true, unstakes all tokens. Default is ``False``. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: - A tuple containing: - - `True` and a success message if the unstake operation succeeded; - - `False` and an error message otherwise. + bool: True if the subnet registration was successful, False otherwise. """ - if amount and unstake_all: - raise ValueError("Cannot specify both `amount` and `unstake_all`.") - # Decrypt keys, if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) return False - if hotkey_ss58 is None: - hotkey_ss58 = wallet.hotkey.ss58_address # Default to wallet's own hotkey. - logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" ) @@ -76,24 +66,14 @@ def unstake_extrinsic( block=block, ) - # Convert to bittensor.Balance - if amount is None: - # Unstake it all. - logging.warning( - f"Didn't receive any unstaking amount. Unstaking all existing stake: [blue]{old_stake}[/blue] " - f"from hotkey: [blue]{hotkey_ss58}[/blue]" - ) - unstaking_balance = old_stake - else: - unstaking_balance = amount - unstaking_balance.set_unit(netuid) + # unstaking_balance = amount + amount.set_unit(netuid) # Check enough to unstake. - stake_on_uid = old_stake - if unstaking_balance > stake_on_uid: + if amount > old_stake: logging.error( - f":cross_mark: [red]Not enough stake[/red]: [green]{stake_on_uid}[/green] to unstake: " - f"[blue]{unstaking_balance}[/blue] from hotkey: [yellow]{wallet.hotkey_str}[/yellow]" + f":cross_mark: [red]Not enough stake[/red]: [green]{old_stake}[/green] to unstake: " + f"[blue]{amount}[/blue] from hotkey: [yellow]{wallet.hotkey_str}[/yellow]" ) return False @@ -101,7 +81,7 @@ def unstake_extrinsic( call_params = { "hotkey": hotkey_ss58, "netuid": netuid, - "amount_unstaked": unstaking_balance.rao, + "amount_unstaked": amount.rao, } if safe_staking: @@ -115,7 +95,7 @@ def unstake_extrinsic( logging_info = ( f":satellite: [magenta]Safe Unstaking from:[/magenta] " - f"netuid: [green]{netuid}[/green], amount: [green]{unstaking_balance}[/green], " + f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green], " f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " f"price limit: [green]{price_with_tolerance}[/green], " f"original price: [green]{base_price}[/green], " @@ -134,7 +114,7 @@ def unstake_extrinsic( else: logging_info = ( f":satellite: [magenta]Unstaking from:[/magenta] " - f"netuid: [green]{netuid}[/green], amount: [green]{unstaking_balance}[/green] " + f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green] " f"on [blue]{subtensor.network}[/blue]" ) call_function = "remove_stake" @@ -158,6 +138,7 @@ def unstake_extrinsic( sign_with="coldkey", use_nonce=True, period=period, + raise_error=raise_error, ) if success: # If we successfully unstaked. @@ -188,14 +169,15 @@ def unstake_extrinsic( f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" ) return True + + if safe_staking and "Custom error: 8" in message: + logging.error( + ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or" + " enable partial staking." + ) else: - if safe_staking and "Custom error: 8" in message: - logging.error( - ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") - return False + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") + return False except SubstrateRequestException as error: logging.error( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 83f1c62e11..819ebd4d2a 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4430,61 +4430,61 @@ def transfer_stake( def unstake( self, wallet: "Wallet", - hotkey_ss58: Optional[str] = None, - netuid: Optional[int] = None, # TODO why is this optional? - amount: Optional[Balance] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - safe_staking: bool = False, + netuid: int, + hotkey_ss58: str, + amount: Balance, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + safe_staking: bool = False, period: Optional[int] = None, - unstake_all: bool = False, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting - individual neuron stakes within the Bittensor network. + individual neuron stakes within the Bittensor network. - Args: + Parameters: wallet: The wallet associated with the neuron from which the stake is being removed. - hotkey_ss58: The ``SS58`` address of the hotkey account to unstake from. netuid: The unique identifier of the subnet. + hotkey_ss58: The ``SS58`` address of the hotkey account to unstake from. amount: The amount of alpha to unstake. If not specified, unstakes all. Alpha amount. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - safe_staking: If true, enables price safety checks to protect against fluctuating prices. The unstake - will only execute if the price change doesn't exceed the rate tolerance. Default is False. - allow_partial_stake (bool): If true and safe_staking is enabled, allows partial unstaking when + allow_partial_stake: If true and safe_staking is enabled, allows partial unstaking when the full amount would exceed the price tolerance. If false, the entire unstake fails if it would - exceed the tolerance. Default is False. - rate_tolerance (float): The maximum allowed price change ratio when unstaking. For example, - 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. Default is 0.005. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. - unstake_all: If `True`, unstakes all tokens, and `amount` is ignored. Default is `False`. + exceed the tolerance. + rate_tolerance: The maximum allowed price change ratio when unstaking. For example, + 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. + safe_staking: If true, enables price safety checks to protect against fluctuating prices. The unstake + will only execute if the price change doesn't exceed the rate tolerance. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: bool: ``True`` if the unstaking process is successful, False otherwise. This function supports flexible stake management, allowing neurons to adjust their network participation and - potential reward accruals. When safe_staking is enabled, it provides protection against price fluctuations - during the time unstake is executed and the time it is actually processed by the chain. + potential reward accruals. When safe_staking is enabled, it provides protection against price fluctuations + during the time unstake is executed and the time it is actually processed by the chain. """ amount = check_and_convert_to_balance(amount) return unstake_extrinsic( subtensor=self, wallet=wallet, - hotkey_ss58=hotkey_ss58, netuid=netuid, + hotkey_ss58=hotkey_ss58, amount=amount, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + safe_staking=safe_staking, period=period, - unstake_all=unstake_all, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, ) def unstake_all( @@ -4494,7 +4494,7 @@ def unstake_all( netuid: int, rate_tolerance: Optional[float] = 0.005, wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, + wait_for_finalization: bool = True, period: Optional[int] = None, ) -> tuple[bool, str]: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. diff --git a/migration.md b/migration.md index f01ab6f02e..b55b85a403 100644 --- a/migration.md +++ b/migration.md @@ -198,4 +198,10 @@ wait_for_finalization: bool = False, - [x] `.add_stake_multiple_extrinsic` and `subtensor.add_stake_multiple` - [x] `.start_call_extrinsic` and `subtensor.start_call` - [x] `.increase_take_extrinsic`, `.decrease_take_extrinsic` and `subtenor.set_reveal_commitment` -- [x] `.transfer_extrinsic` and `subtensor.transfer` \ No newline at end of file +- [x] `.transfer_extrinsic` and `subtensor.transfer` +- [x] `.unstake_extrinsic` and `subtensor.unstake` + - Changes in `unstake_extrinsic`: + - parameter `netuid: Optional[int]` is now required -> `netuid: int` + - parameter `hotkey_ss58: Optional[str]` is now required -> `hotkey_ss58: str` + - parameter `amount: Optional[Balance]` is now required -> `amount: Balance` + - parameter `unstake_all: bool` removed (use `unstake_all_extrinsic` for unstake all stake) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 27b3468c89..6ea1ffa3dc 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -165,13 +165,19 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): ), } + stake = subtensor.staking.get_stake( + coldkey_ss58=alice_wallet.coldkey.ss58_address, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + ) + logging.console.info(f"Alice stake before unstake: {stake}") + # unstale all to check in later success = subtensor.staking.unstake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + amount=stake, period=16, ) @@ -351,10 +357,8 @@ async def test_single_operation_async(async_subtensor, alice_wallet, bob_wallet) # unstale all to check in later success, message = await async_subtensor.staking.unstake_all( wallet=alice_wallet, - hotkey=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, + hotkey=bob_wallet.hotkey.ss58_address, period=16, ) assert success is True, message @@ -486,9 +490,9 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): expected_fee_paid += fee_tao success = subtensor.staking.unstake_multiple( - alice_wallet, - hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], + wallet=alice_wallet, netuids=netuids, + hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], amounts=[Balance.from_tao(100) for _ in netuids], ) @@ -496,8 +500,8 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): for netuid, old_stake in zip(netuids, stakes): stake = subtensor.staking.get_stake( - alice_wallet.coldkey.ss58_address, - bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=netuid, ) @@ -631,9 +635,9 @@ async def test_batch_operations_async(async_subtensor, alice_wallet, bob_wallet) expected_fee_paid += fee_tao success = await async_subtensor.staking.unstake_multiple( - alice_wallet, - hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], + wallet=alice_wallet, netuids=netuids, + hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], amounts=[Balance.from_tao(100) for _ in netuids], ) @@ -641,8 +645,8 @@ async def test_batch_operations_async(async_subtensor, alice_wallet, bob_wallet) for netuid, old_stake in zip(netuids, stakes): stake = await async_subtensor.staking.get_stake( - alice_wallet.coldkey.ss58_address, - bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=netuid, ) @@ -793,11 +797,9 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) # 1. Strict params - should fail success = subtensor.staking.unstake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=full_stake, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=False, @@ -820,11 +822,9 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) # 2. Partial allowed - should succeed partially success = subtensor.staking.unstake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=current_stake, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=True, @@ -844,11 +844,9 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) # 3. Higher threshold - should succeed fully success = subtensor.staking.unstake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=partial_unstake, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.3, # 30% allow_partial_stake=False, @@ -995,11 +993,9 @@ async def test_safe_staking_scenarios_async( # 1. Strict params - should fail success = await async_subtensor.staking.unstake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=full_stake, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=False, @@ -1022,11 +1018,9 @@ async def test_safe_staking_scenarios_async( # 2. Partial allowed - should succeed partially success = await async_subtensor.staking.unstake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=current_stake, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=True, @@ -1046,11 +1040,9 @@ async def test_safe_staking_scenarios_async( # 3. Higher threshold - should succeed fully success = await async_subtensor.staking.unstake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=partial_unstake, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.3, # 30% allow_partial_stake=False, @@ -1979,8 +1971,6 @@ def test_unstaking_with_limit( hotkey=bob_stakes[0].hotkey_ss58, netuid=bob_stakes[0].netuid, rate_tolerance=rate_tolerance, - wait_for_inclusion=True, - wait_for_finalization=True, ) else: # Successful cases @@ -1990,8 +1980,6 @@ def test_unstaking_with_limit( hotkey=si.hotkey_ss58, netuid=si.netuid, rate_tolerance=rate_tolerance, - wait_for_inclusion=True, - wait_for_finalization=True, )[0] # Make sure both unstake were successful. @@ -2070,8 +2058,8 @@ async def test_unstaking_with_limit_async( assert await async_subtensor.staking.add_stake( wallet=bob_wallet, - hotkey_ss58=dave_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid_2, + hotkey_ss58=dave_wallet.hotkey.ss58_address, amount=Balance.from_tao(10000), wait_for_inclusion=True, wait_for_finalization=True, @@ -2079,8 +2067,8 @@ async def test_unstaking_with_limit_async( ), f"Cant add stake to dave in SN {alice_subnet_netuid_2}" assert await async_subtensor.staking.add_stake( wallet=bob_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid_3, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(15000), wait_for_inclusion=True, wait_for_finalization=True, @@ -2100,11 +2088,9 @@ async def test_unstaking_with_limit_async( ): await async_subtensor.staking.unstake_all( wallet=bob_wallet, - hotkey=bob_stakes[0].hotkey_ss58, netuid=bob_stakes[0].netuid, + hotkey=bob_stakes[0].hotkey_ss58, rate_tolerance=rate_tolerance, - wait_for_inclusion=True, - wait_for_finalization=True, ) else: # Successful cases @@ -2112,11 +2098,9 @@ async def test_unstaking_with_limit_async( assert ( await async_subtensor.staking.unstake_all( wallet=bob_wallet, - hotkey=si.hotkey_ss58, netuid=si.netuid, + hotkey=si.hotkey_ss58, rate_tolerance=rate_tolerance, - wait_for_inclusion=True, - wait_for_finalization=True, ) )[0] diff --git a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py index 24857e0261..9be3be85fa 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py +++ b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py @@ -59,6 +59,7 @@ async def test_unstake_extrinsic(fake_wallet, mocker): nonce_key="coldkeypub", use_nonce=True, period=None, + raise_error=False, ) diff --git a/tests/unit_tests/extrinsics/test_unstaking.py b/tests/unit_tests/extrinsics/test_unstaking.py index 69e020854f..dbbbc98157 100644 --- a/tests/unit_tests/extrinsics/test_unstaking.py +++ b/tests/unit_tests/extrinsics/test_unstaking.py @@ -55,6 +55,7 @@ def test_unstake_extrinsic(fake_wallet, mocker): nonce_key="coldkeypub", use_nonce=True, period=None, + raise_error=False, ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 7a0170a17a..af200dabdd 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2832,6 +2832,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): """Test unstake operation is successful.""" # Preps fake_hotkey_ss58 = "hotkey_1" + fake_netuid = 1 fake_amount = 10.0 mock_unstake_extrinsic = mocker.patch.object(subtensor_module, "unstake_extrinsic") @@ -2839,6 +2840,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): # Call result = subtensor.unstake( wallet=fake_wallet, + netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, amount=fake_amount, wait_for_inclusion=True, @@ -2852,16 +2854,16 @@ def test_unstake_success(mocker, subtensor, fake_wallet): mock_unstake_extrinsic.assert_called_once_with( subtensor=subtensor, wallet=fake_wallet, + netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, - netuid=None, amount=Balance.from_rao(fake_amount), - wait_for_inclusion=True, - wait_for_finalization=False, safe_staking=False, allow_partial_stake=False, rate_tolerance=0.005, period=None, - unstake_all=False, + wait_for_inclusion=True, + wait_for_finalization=False, + raise_error=False, ) assert result == mock_unstake_extrinsic.return_value @@ -2870,6 +2872,7 @@ def test_unstake_with_safe_staking(mocker, subtensor, fake_wallet): """Test unstake with safe staking parameters enabled.""" fake_hotkey_ss58 = "hotkey_1" fake_amount = 10.0 + fake_netuid = 14 fake_rate_tolerance = 0.01 # 1% threshold mock_unstake_extrinsic = mocker.patch.object(subtensor_module, "unstake_extrinsic") @@ -2877,6 +2880,7 @@ def test_unstake_with_safe_staking(mocker, subtensor, fake_wallet): # Call result = subtensor.unstake( wallet=fake_wallet, + netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, amount=fake_amount, wait_for_inclusion=True, @@ -2890,16 +2894,16 @@ def test_unstake_with_safe_staking(mocker, subtensor, fake_wallet): mock_unstake_extrinsic.assert_called_once_with( subtensor=subtensor, wallet=fake_wallet, + netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, - netuid=None, amount=Balance.from_rao(fake_amount), - wait_for_inclusion=True, - wait_for_finalization=False, safe_staking=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, period=None, - unstake_all=False, + raise_error=False, + wait_for_inclusion=True, + wait_for_finalization=False, ) assert result == mock_unstake_extrinsic.return_value From 3bc569e015780dce0f697f5757096daba3d3fc31 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 14:18:32 -0700 Subject: [PATCH 33/49] `.unstake_all_extrinsic` and `subtensor.unstake_all` + tests --- bittensor/core/async_subtensor.py | 19 +++++++++++-------- .../core/extrinsics/asyncex/unstaking.py | 15 +++++++++------ bittensor/core/extrinsics/unstaking.py | 15 +++++++++------ bittensor/core/subtensor.py | 19 +++++++++++-------- migration.md | 1 + .../extrinsics/asyncex/test_unstaking.py | 3 ++- tests/unit_tests/extrinsics/test_unstaking.py | 3 ++- tests/unit_tests/test_async_subtensor.py | 3 ++- tests/unit_tests/test_subtensor.py | 3 ++- 9 files changed, 49 insertions(+), 32 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index dd4bbbc706..5d52fbf184 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5684,23 +5684,25 @@ async def unstake_all( hotkey: str, netuid: int, rate_tolerance: Optional[float] = 0.005, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> tuple[bool, str]: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. - Arguments: + Parameters: wallet: The wallet of the stake owner. hotkey: The SS58 address of the hotkey to unstake from. netuid: The unique identifier of the subnet. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. If not passed (None), then unstaking goes without price limit. Default is 0.005. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `True`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is `False`. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. Default is `None`. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: tuple[bool, str]: @@ -5757,9 +5759,10 @@ async def unstake_all( hotkey=hotkey, netuid=netuid, rate_tolerance=rate_tolerance, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) async def unstake_multiple( diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index f62359b3da..872d18e942 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -192,24 +192,26 @@ async def unstake_all_extrinsic( hotkey: str, netuid: int, rate_tolerance: Optional[float] = 0.005, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. - Arguments: + Parameters: subtensor: Subtensor instance. wallet: The wallet of the stake owner. hotkey: The SS58 address of the hotkey to unstake from. netuid: The unique identifier of the subnet. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. If not passed (None), then unstaking goes without price limit. Default is `0.005`. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `True`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is `False`. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can - think of it as an expiration date for the transaction. Default is `None`. + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: tuple[bool, str]: @@ -248,6 +250,7 @@ async def unstake_all_extrinsic( sign_with="coldkey", use_nonce=True, period=period, + raise_error=raise_error, ) diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index 10c021ca05..377791914c 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -192,24 +192,26 @@ def unstake_all_extrinsic( hotkey: str, netuid: int, rate_tolerance: Optional[float] = 0.005, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> tuple[bool, str]: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. - Arguments: + Parameters: subtensor: Subtensor instance. wallet: The wallet of the stake owner. hotkey: The SS58 address of the hotkey to unstake from. netuid: The unique identifier of the subnet. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. If not passed (None), then unstaking goes without price limit. Default is `0.005`. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `True`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is `False`. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can - think of it as an expiration date for the transaction. Default is `None`. + think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: tuple[bool, str]: @@ -247,6 +249,7 @@ def unstake_all_extrinsic( sign_with="coldkey", use_nonce=True, period=period, + raise_error=raise_error, ) return success, message diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 819ebd4d2a..5169a0f475 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4493,23 +4493,25 @@ def unstake_all( hotkey: str, netuid: int, rate_tolerance: Optional[float] = 0.005, + period: Optional[int] = None, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - period: Optional[int] = None, ) -> tuple[bool, str]: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. - Arguments: + Parameters: wallet: The wallet of the stake owner. hotkey: The SS58 address of the hotkey to unstake from. netuid: The unique identifier of the subnet. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. If not passed (None), then unstaking goes without price limit. Default is 0.005. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `True`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is `False`. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. Default is `None`. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: tuple[bool, str]: @@ -4565,9 +4567,10 @@ def unstake_all( hotkey=hotkey, netuid=netuid, rate_tolerance=rate_tolerance, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) def unstake_multiple( diff --git a/migration.md b/migration.md index b55b85a403..14890cad68 100644 --- a/migration.md +++ b/migration.md @@ -205,3 +205,4 @@ wait_for_finalization: bool = False, - parameter `hotkey_ss58: Optional[str]` is now required -> `hotkey_ss58: str` - parameter `amount: Optional[Balance]` is now required -> `amount: Balance` - parameter `unstake_all: bool` removed (use `unstake_all_extrinsic` for unstake all stake) +- [x] `.unstake_all_extrinsic` and `subtensor.unstake_all` diff --git a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py index 9be3be85fa..5e7dd01df4 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py +++ b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py @@ -101,11 +101,12 @@ async def test_unstake_all_extrinsic(fake_wallet, mocker): call=fake_substrate.compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, sign_with="coldkey", nonce_key="coldkeypub", use_nonce=True, period=None, + raise_error=False, ) diff --git a/tests/unit_tests/extrinsics/test_unstaking.py b/tests/unit_tests/extrinsics/test_unstaking.py index dbbbc98157..783f377b94 100644 --- a/tests/unit_tests/extrinsics/test_unstaking.py +++ b/tests/unit_tests/extrinsics/test_unstaking.py @@ -96,11 +96,12 @@ def test_unstake_all_extrinsic(fake_wallet, mocker): call=fake_subtensor.substrate.compose_call.return_value, wallet=fake_wallet, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, sign_with="coldkey", nonce_key="coldkeypub", use_nonce=True, period=None, + raise_error=False, ) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 490c21bf77..57c94ed227 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3479,8 +3479,9 @@ async def test_unstake_all(subtensor, fake_wallet, mocker): netuid=1, rate_tolerance=0.005, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == fake_unstake_all_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index af200dabdd..ce55dbabad 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3708,8 +3708,9 @@ def test_unstake_all(subtensor, fake_wallet, mocker): netuid=1, rate_tolerance=0.005, wait_for_inclusion=True, - wait_for_finalization=False, + wait_for_finalization=True, period=None, + raise_error=False, ) assert result == fake_unstake_all_extrinsic.return_value From 80d21a2847c8cd2577cd832ddf18388ac2d30bb5 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 15:00:15 -0700 Subject: [PATCH 34/49] `.unstake_multiple_extrinsic` and `subtensor.unstake_multiple` + tests --- bittensor/core/async_subtensor.py | 31 +++++++------ .../core/extrinsics/asyncex/unstaking.py | 33 +++++++------- bittensor/core/extrinsics/unstaking.py | 33 +++++++------- bittensor/core/subtensor.py | 43 ++++++++++--------- migration.md | 1 + .../extrinsics/asyncex/test_unstaking.py | 1 + tests/unit_tests/extrinsics/test_unstaking.py | 1 + tests/unit_tests/test_subtensor.py | 1 + 8 files changed, 75 insertions(+), 69 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 5d52fbf184..4adf2db840 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5768,29 +5768,31 @@ async def unstake_all( async def unstake_multiple( self, wallet: "Wallet", - hotkey_ss58s: list[str], netuids: list[int], + hotkey_ss58s: list[str], amounts: Optional[list[Balance]] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - period: Optional[int] = None, unstake_all: bool = False, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts efficiently. This function is useful for managing the distribution of stakes across multiple neurons. - Arguments: + Parameters: wallet: The wallet linked to the coldkey from which the stakes are being withdrawn. - hotkey_ss58s: A list of hotkey `SS58` addresses to unstake from. netuids: Subnets unique IDs. + hotkey_ss58s: A list of hotkey `SS58` addresses to unstake from. amounts: The amounts of TAO to unstake from each hotkey. If not provided, unstakes all. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. unstake_all: If true, unstakes all tokens. Default is `False`. If `True` amounts are ignored. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: bool: `True` if the batch unstaking is successful, False otherwise. @@ -5801,13 +5803,14 @@ async def unstake_multiple( return await unstake_multiple_extrinsic( subtensor=self, wallet=wallet, - hotkey_ss58s=hotkey_ss58s, netuids=netuids, + hotkey_ss58s=hotkey_ss58s, amounts=amounts, + unstake_all=unstake_all, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, - unstake_all=unstake_all, ) diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 872d18e942..609349ec07 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -257,36 +257,34 @@ async def unstake_all_extrinsic( async def unstake_multiple_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", - hotkey_ss58s: list[str], netuids: list[int], + hotkey_ss58s: list[str], amounts: Optional[list[Balance]] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - period: Optional[int] = None, unstake_all: bool = False, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. + """ + Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. - Args: - subtensor: Subtensor instance. + Parameters: + subtensor: AsyncSubtensor instance. wallet: The wallet with the coldkey to unstake to. + netuids: List of subnets unique IDs to unstake from. hotkey_ss58s: List of hotkeys to unstake from. - netuids: List of netuids to unstake from. amounts: List of amounts to unstake. If ``None``, unstake all. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning - ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. + unstake_all: If true, unstakes all tokens. Default is ``False``. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - unstake_all: If true, unstakes all tokens. Default is ``False``. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: - A tuple containing: - - `True` and a success message if the unstake operation succeeded; - - `False` and an error message otherwise. + bool: True if the subnet registration was successful, False otherwise. """ if amounts and unstake_all: raise ValueError("Cannot specify both `amounts` and `unstake_all`.") @@ -394,6 +392,7 @@ async def unstake_multiple_extrinsic( sign_with="coldkey", use_nonce=True, period=period, + raise_error=raise_error, ) if staking_response is True: # If we successfully unstaked. diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index 377791914c..fff7cdd693 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -258,36 +258,34 @@ def unstake_all_extrinsic( def unstake_multiple_extrinsic( subtensor: "Subtensor", wallet: "Wallet", - hotkey_ss58s: list[str], netuids: list[int], + hotkey_ss58s: list[str], amounts: Optional[list[Balance]] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - period: Optional[int] = None, unstake_all: bool = False, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: - """Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. + """ + Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. - Args: + Parameters: subtensor: Subtensor instance. wallet: The wallet with the coldkey to unstake to. - hotkey_ss58s: List of hotkeys to unstake from. netuids: List of subnets unique IDs to unstake from. + hotkey_ss58s: List of hotkeys to unstake from. amounts: List of amounts to unstake. If ``None``, unstake all. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning ``True``, - or returns ``False`` if the extrinsic fails to be finalized within the timeout. + unstake_all: If true, unstakes all tokens. Default is ``False``. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - unstake_all: If true, unstakes all tokens. Default is ``False``. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: - A tuple containing: - - `True` and a success message if the unstake operation succeeded; - - `False` and an error message otherwise. + bool: True if the subnet registration was successful, False otherwise. """ if amounts and unstake_all: raise ValueError("Cannot specify both `amounts` and `unstake_all`.") @@ -388,9 +386,10 @@ def unstake_multiple_extrinsic( sign_with="coldkey", use_nonce=True, period=period, + raise_error=raise_error, ) - if staking_response is True: # If we successfully unstaked. + if staking_response: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 5169a0f475..dfae544119 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4576,46 +4576,47 @@ def unstake_all( def unstake_multiple( self, wallet: "Wallet", - hotkey_ss58s: list[str], netuids: list[int], + hotkey_ss58s: list[str], amounts: Optional[list[Balance]] = None, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - period: Optional[int] = None, unstake_all: bool = False, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, ) -> bool: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts efficiently. This function is useful for managing the distribution of stakes across multiple neurons. - Args: - wallet: The wallet linked to the coldkey from which the stakes are being - withdrawn. - hotkey_ss58s (List[str]): A list of hotkey ``SS58`` addresses to unstake from. - netuids (List[int]): The list of subnet uids. - amounts (List[Balance]): The amounts of TAO to unstake from each hotkey. If not provided, - unstakes all available stakes. - 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. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. - unstake_all: If `True`, unstakes all tokens, and `amounts` is ignored. Default is `False`. + Parameters: + wallet: The wallet linked to the coldkey from which the stakes are being withdrawn. + netuids: Subnets unique IDs. + hotkey_ss58s: A list of hotkey `SS58` addresses to unstake from. + amounts: The amounts of TAO to unstake from each hotkey. If not provided, unstakes all. + unstake_all: If true, unstakes all tokens. Default is `False`. If `True` amounts are ignored. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: bool: ``True`` if the batch unstaking is successful, False otherwise. This function allows for strategic reallocation or withdrawal of stakes, aligning with the dynamic stake - management aspect of the Bittensor network. + management aspect of the Bittensor network. """ return unstake_multiple_extrinsic( subtensor=self, wallet=wallet, - hotkey_ss58s=hotkey_ss58s, netuids=netuids, + hotkey_ss58s=hotkey_ss58s, amounts=amounts, + unstake_all=unstake_all, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, - unstake_all=unstake_all, ) diff --git a/migration.md b/migration.md index 14890cad68..eea33cf0ba 100644 --- a/migration.md +++ b/migration.md @@ -206,3 +206,4 @@ wait_for_finalization: bool = False, - parameter `amount: Optional[Balance]` is now required -> `amount: Balance` - parameter `unstake_all: bool` removed (use `unstake_all_extrinsic` for unstake all stake) - [x] `.unstake_all_extrinsic` and `subtensor.unstake_all` +- [x] `.unstake_multiple_extrinsic` and `subtensor.unstake_multiple` diff --git a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py index 5e7dd01df4..2f70f9bc68 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py +++ b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py @@ -179,4 +179,5 @@ async def test_unstake_multiple_extrinsic(fake_wallet, mocker): nonce_key="coldkeypub", use_nonce=True, period=None, + raise_error=False, ) diff --git a/tests/unit_tests/extrinsics/test_unstaking.py b/tests/unit_tests/extrinsics/test_unstaking.py index 783f377b94..f4719cdbb2 100644 --- a/tests/unit_tests/extrinsics/test_unstaking.py +++ b/tests/unit_tests/extrinsics/test_unstaking.py @@ -173,4 +173,5 @@ def test_unstake_multiple_extrinsic(fake_wallet, mocker): nonce_key="coldkeypub", use_nonce=True, period=None, + raise_error=False, ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index ce55dbabad..56b18759a5 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3030,6 +3030,7 @@ def test_unstake_multiple_success(mocker, subtensor, fake_wallet): wait_for_finalization=False, period=None, unstake_all=False, + raise_error=False, ) assert result == mock_unstake_multiple_extrinsic.return_value From 1528bbc678f1292ca07d512a6ae0b9f5b4514dbd Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 15:12:36 -0700 Subject: [PATCH 35/49] `.unstake_multiple_extrinsic` and `subtensor.unstake_multiple` + tests --- bittensor/core/async_subtensor.py | 31 ++++++++------- bittensor/core/extrinsics/asyncex/weights.py | 14 +++---- bittensor/core/extrinsics/weights.py | 14 +++---- bittensor/core/subtensor.py | 42 ++++++++++---------- migration.md | 1 + 5 files changed, 52 insertions(+), 50 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 4adf2db840..74de1dc550 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4488,29 +4488,30 @@ async def commit_weights( uids: Union[NDArray[np.int64], list], weights: Union[NDArray[np.int64], list], version_key: int = version_as_int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, max_retries: int = 5, period: Optional[int] = 16, + raise_error: bool = True, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, ) -> tuple[bool, str]: """ Commits a hash of the subnet validator's weight vector to the Bittensor blockchain using the provided wallet. This action serves as a commitment or snapshot of the validator's current weight distribution. - Arguments: - wallet: The wallet associated with the subnet validator committing the weights. + Parameters: + wallet: The wallet associated with the neuron committing the weights. netuid: The unique identifier of the subnet. salt: list of randomly generated integers as salt to generated weighted hash. - uids: NumPy array of subnet miner neuron UIDs for which weights are being committed. - weights: of weight values corresponding toon_key - version_key: Integer representation of version key for compatibility with the network. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `False`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is - `False`. - max_retries: The number of maximum attempts to commit weights. Default is `5`. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + uids: NumPy array of neuron UIDs for which weights are being committed. + weights: NumPy array of weight values corresponding to each UID. + version_key: Version key for compatibility with the network. + max_retries: The number of maximum attempts to commit weights. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: tuple[bool, str]: @@ -4553,7 +4554,7 @@ async def commit_weights( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, - raise_error=True, + raise_error=raise_error, ) if success: break diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 8717bf638d..d231db50af 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -24,26 +24,26 @@ async def commit_weights_extrinsic( wallet: "Wallet", netuid: int, commit_hash: str, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, period: Optional[int] = None, raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. This function is a wrapper around the `do_commit_weights` method. - Args: + Parameters: subtensor: The subtensor instance used for blockchain interaction. wallet: The wallet associated with the neuron committing the weights. netuid: The unique identifier of the subnet. commit_hash: The hash of the neuron's weights to be committed. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - raise_error: raises the relevant exception rather than returning `False` if unsuccessful. + raise_error: Whether to raise an error if the transaction fails. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: tuple[bool, str]: @@ -51,7 +51,7 @@ async def commit_weights_extrinsic( `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for committing weights to the Bittensor blockchain, ensuring proper - error handling and user interaction when required. + error handling and user interaction when required. """ call = await subtensor.substrate.compose_call( call_module="SubtensorModule", diff --git a/bittensor/core/extrinsics/weights.py b/bittensor/core/extrinsics/weights.py index fec8e36b70..9de3949cb2 100644 --- a/bittensor/core/extrinsics/weights.py +++ b/bittensor/core/extrinsics/weights.py @@ -24,26 +24,26 @@ def commit_weights_extrinsic( wallet: "Wallet", netuid: int, commit_hash: str, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, period: Optional[int] = None, raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. This function is a wrapper around the `do_commit_weights` method. - Args: + Parameters: subtensor: The subtensor instance used for blockchain interaction. wallet: The wallet associated with the neuron committing the weights. netuid: The unique identifier of the subnet. commit_hash: The hash of the neuron's weights to be committed. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - raise_error (bool): Whether to raise an error if the transaction fails. + raise_error: Whether to raise an error if the transaction fails. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: tuple[bool, str]: @@ -51,7 +51,7 @@ def commit_weights_extrinsic( `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for committing weights to the Bittensor blockchain, ensuring proper - error handling and user interaction when required. + error handling and user interaction when required. """ call = subtensor.substrate.compose_call( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index dfae544119..85a8e1f06d 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3328,38 +3328,38 @@ def commit_weights( uids: Union[NDArray[np.int64], list], weights: Union[NDArray[np.int64], list], version_key: int = version_as_int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, max_retries: int = 5, period: Optional[int] = 16, + raise_error: bool = True, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. This action serves as a commitment or snapshot of the neuron's current weight distribution. - Arguments: - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the weights. - netuid (int): The unique identifier of the subnet. - salt (list[int]): list of randomly generated integers as salt to generated weighted hash. - uids (np.ndarray): NumPy array of neuron UIDs for which weights are being committed. - weights (np.ndarray): NumPy array of weight values corresponding to each UID. - version_key (int): Version key for compatibility with the network. Default is ``int representation of - a Bittensor version.``. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``False``. - max_retries (int): The number of maximum attempts to commit weights. Default is ``5``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: The wallet associated with the neuron committing the weights. + netuid: The unique identifier of the subnet. + salt: list of randomly generated integers as salt to generated weighted hash. + uids: NumPy array of neuron UIDs for which weights are being committed. + weights: NumPy array of weight values corresponding to each UID. + version_key: Version key for compatibility with the network. + max_retries: The number of maximum attempts to commit weights. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: tuple[bool, str]: `True` if the weight commitment is successful, False otherwise. `msg` is a string value describing the success or potential error. - This function allows neurons to create a tamper-proof record of their weight distribution at a specific point - in time, enhancing transparency and accountability within the Bittensor network. + This function allows neurons to create a tamper-proof record of their weight distribution at a specific point in + time, enhancing transparency and accountability within the Bittensor network. """ retries = 0 success = False @@ -3388,10 +3388,10 @@ def commit_weights( wallet=wallet, netuid=netuid, commit_hash=commit_hash, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, - raise_error=True, ) if success: break diff --git a/migration.md b/migration.md index eea33cf0ba..dca67d3d77 100644 --- a/migration.md +++ b/migration.md @@ -207,3 +207,4 @@ wait_for_finalization: bool = False, - parameter `unstake_all: bool` removed (use `unstake_all_extrinsic` for unstake all stake) - [x] `.unstake_all_extrinsic` and `subtensor.unstake_all` - [x] `.unstake_multiple_extrinsic` and `subtensor.unstake_multiple` +- [x] `.commit_weights_extrinsic` and `subtensor.commit_weights` From f1eb2d1a4a6d68f2ef6fb2d9a21f272782b3593b Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 15:27:47 -0700 Subject: [PATCH 36/49] `.reveal_weights_extrinsic` and `subtensor.reveal_weights` --- bittensor/core/async_subtensor.py | 28 ++++++------ bittensor/core/extrinsics/asyncex/weights.py | 12 ++--- bittensor/core/extrinsics/weights.py | 12 ++--- bittensor/core/subtensor.py | 46 ++++++++++---------- migration.md | 1 + 5 files changed, 51 insertions(+), 48 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 74de1dc550..f0223537ca 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4841,32 +4841,32 @@ async def reveal_weights( uids: Union[NDArray[np.int64], list], weights: Union[NDArray[np.int64], list], salt: Union[NDArray[np.int64], list], + max_retries: int = 5, version_key: int = version_as_int, + period: Optional[int] = 16, + raise_error: bool = True, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - max_retries: int = 5, - period: Optional[int] = None, ) -> tuple[bool, str]: """ Reveals the weight vector for a specific subnet on the Bittensor blockchain using the provided wallet. This action serves as a revelation of the subnet validator's previously committed weight distribution as part of the commit-reveal mechanism. - Arguments: + Parameters: wallet: The wallet associated with the subnet validator revealing the weights. netuid: unique identifier of the subnet. uids: NumPy array of subnet miner neuron UIDs for which weights are being revealed. weights: NumPy array of weight values corresponding to each UID. salt: NumPy array of salt values - version_key: Version key for compatibility with the network. Default is `int representation of - the Bittensor version`. - wait_for_inclusion: Waits for the transaction to be included in a block. Default is `False`. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is - `False`. - max_retries: The number of maximum attempts to reveal weights. Default is `5`. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + max_retries: The number of maximum attempts to reveal weights. + version_key: Version key for compatibility with the network. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: Tuple[bool, str]: @@ -4891,10 +4891,10 @@ async def reveal_weights( weights=list(weights), salt=list(salt), version_key=version_key, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, - raise_error=True, ) if success: break diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index d231db50af..a6586a784f 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -88,16 +88,16 @@ async def reveal_weights_extrinsic( weights: list[int], salt: list[int], version_key: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, period: Optional[int] = None, raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. This function is a wrapper around the `_do_reveal_weights` method. - Args: + Parameters: subtensor: The subtensor instance used for blockchain interaction. wallet: The wallet associated with the neuron revealing the weights. netuid: The unique identifier of the subnet. @@ -105,12 +105,12 @@ async def reveal_weights_extrinsic( weights: List of weight values corresponding to each UID. salt: List of salt values corresponding to the hash function. version_key: Version key for compatibility with the network. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - raise_error: raises the relevant exception rather than returning `False` if unsuccessful. + raise_error: Whether to raise an error if the transaction fails. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: tuple[bool, str]: diff --git a/bittensor/core/extrinsics/weights.py b/bittensor/core/extrinsics/weights.py index 9de3949cb2..46f660a514 100644 --- a/bittensor/core/extrinsics/weights.py +++ b/bittensor/core/extrinsics/weights.py @@ -89,16 +89,16 @@ def reveal_weights_extrinsic( weights: list[int], salt: list[int], version_key: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, period: Optional[int] = None, raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. This function is a wrapper around the `_do_reveal_weights` method. - Args: + Parameters: subtensor: The subtensor instance used for blockchain interaction. wallet: The wallet associated with the neuron revealing the weights. netuid: The unique identifier of the subnet. @@ -106,12 +106,12 @@ def reveal_weights_extrinsic( weights: List of weight values corresponding to each UID. salt: List of salt values corresponding to the hash function. version_key: Version key for compatibility with the network. - wait_for_inclusion: Waits for the transaction to be included in a block. - wait_for_finalization: Waits for the transaction to be finalized on the blockchain. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - raise_error: raises the relevant exception rather than returning `False` if unsuccessful. + raise_error: Whether to raise an error if the transaction fails. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: tuple[bool, str]: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 85a8e1f06d..47bcc61715 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3678,38 +3678,40 @@ def reveal_weights( uids: Union[NDArray[np.int64], list], weights: Union[NDArray[np.int64], list], salt: Union[NDArray[np.int64], list], + max_retries: int = 5, version_key: int = version_as_int, + period: Optional[int] = 16, + raise_error: bool = True, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - max_retries: int = 5, - period: Optional[int] = 16, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. This action serves as a revelation of the neuron's previously committed weight distribution. - Args: - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron revealing the weights. - netuid (int): The unique identifier of the subnet. - uids (np.ndarray): NumPy array of neuron UIDs for which weights are being revealed. - weights (np.ndarray): NumPy array of weight values corresponding to each UID. - salt (np.ndarray): NumPy array of salt values corresponding to the hash function. - version_key (int): Version key for compatibility with the network. Default is ``int representation of - the Bittensor version``. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``False``. - max_retries (int): The number of maximum attempts to reveal weights. Default is ``5``. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + wallet: The wallet associated with the subnet validator revealing the weights. + netuid: unique identifier of the subnet. + uids: NumPy array of subnet miner neuron UIDs for which weights are being revealed. + weights: NumPy array of weight values corresponding to each UID. + salt: NumPy array of salt values + max_retries: The number of maximum attempts to reveal weights. + version_key: Version key for compatibility with the network. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. + wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - tuple[bool, str]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + Tuple[bool, str]: + - True and a success message if the extrinsic is successfully submitted or processed. + - False and an error message if the submission fails or the wallet cannot be unlocked. + + This function allows subnet validators to reveal their previously committed weight vector. - This function allows neurons to reveal their previously committed weight distribution, ensuring transparency - and accountability within the Bittensor network. + See also: , """ retries = 0 success = False @@ -3728,7 +3730,7 @@ def reveal_weights( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, - raise_error=True, + raise_error=raise_error, ) if success: break diff --git a/migration.md b/migration.md index dca67d3d77..bdd9ec375a 100644 --- a/migration.md +++ b/migration.md @@ -208,3 +208,4 @@ wait_for_finalization: bool = False, - [x] `.unstake_all_extrinsic` and `subtensor.unstake_all` - [x] `.unstake_multiple_extrinsic` and `subtensor.unstake_multiple` - [x] `.commit_weights_extrinsic` and `subtensor.commit_weights` +- [x] `.reveal_weights_extrinsic` and `subtensor.reveal_weights` From 12c22b03b202d8b486f63c877e34399ab97c01d8 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 15:30:38 -0700 Subject: [PATCH 37/49] update migration.md --- migration.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/migration.md b/migration.md index bdd9ec375a..1ebdcf942e 100644 --- a/migration.md +++ b/migration.md @@ -181,9 +181,9 @@ wait_for_finalization: bool = False, - [x] `.transfer_stake_extrinsic` and `subtensor.transfer_stake` - [x] `.swap_stake_extrinsic` and `subtensor.swap_stake` - [x] `.move_stake_extrinsic` and `subtensor.move_stake` -- [x] `.move_stake_extrinsic` has renamed parameters: - - `origin_hotkey` to `origin_hotkey_ss58` - - `destination_hotkey` to `destination_hotkey_ss58` + - Changes in `move_stake_extrinsic` and `subtensor.move_stake`: + - parameter `origin_hotkey` renamed to `origin_hotkey_ss58` + - parameter `destination_hotkey` renamed to `destination_hotkey_ss58` - [x] `.burned_register_extrinsic` and `subtensor.burned_register` - [x] `.register_subnet_extrinsic` and `subtensor.register_subnet` - [x] `.register_extrinsic` and `subtensor.register` @@ -200,7 +200,7 @@ wait_for_finalization: bool = False, - [x] `.increase_take_extrinsic`, `.decrease_take_extrinsic` and `subtenor.set_reveal_commitment` - [x] `.transfer_extrinsic` and `subtensor.transfer` - [x] `.unstake_extrinsic` and `subtensor.unstake` - - Changes in `unstake_extrinsic`: + - Changes in `unstake_extrinsic` and `subtensor.unstake`: - parameter `netuid: Optional[int]` is now required -> `netuid: int` - parameter `hotkey_ss58: Optional[str]` is now required -> `hotkey_ss58: str` - parameter `amount: Optional[Balance]` is now required -> `amount: Balance` From 4098506017f706833443bdfdfcb92de9c7adf098 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 15:45:10 -0700 Subject: [PATCH 38/49] `.set_weights_extrinsic` and `subtensor.set_weights` --- bittensor/core/async_subtensor.py | 42 ++++--- bittensor/core/extrinsics/asyncex/weights.py | 18 +-- bittensor/core/extrinsics/commit_reveal.py | 103 +++++++++--------- bittensor/core/extrinsics/weights.py | 17 ++- bittensor/core/subtensor.py | 37 ++++--- migration.md | 1 + .../extrinsics/test_commit_reveal.py | 21 ++-- tests/unit_tests/test_async_subtensor.py | 1 + tests/unit_tests/test_subtensor.py | 3 +- 9 files changed, 124 insertions(+), 119 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index f0223537ca..259eff3e11 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5168,7 +5168,7 @@ async def set_weights( max_retries: int = 5, version_key: int = version_as_int, period: Optional[int] = 8, - raise_error: bool = False, + raise_error: bool = True, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ): @@ -5238,23 +5238,28 @@ async def _blocks_weight_limit() -> bool: and await _blocks_weight_limit() ): logging.info( - f"Committing weights for subnet #{netuid}. Attempt {retries + 1} of {max_retries}." + f"Committing weights for subnet [blue]{netuid}[/blue]. " + f"Attempt [blue]{retries + 1}[blue] of [green]{max_retries}[/green]." ) - success, message = await commit_reveal_extrinsic( - subtensor=self, - wallet=wallet, - netuid=netuid, - uids=uids, - weights=weights, - block_time=block_time, - commit_reveal_version=commit_reveal_version, - version_key=version_key, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - retries += 1 + try: + success, message = await commit_reveal_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + uids=uids, + weights=weights, + block_time=block_time, + commit_reveal_version=commit_reveal_version, + version_key=version_key, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + except Exception as e: + logging.error(f"Error setting weights: {e}") + retries += 1 + return success, message else: # go with classic `set weights extrinsic` @@ -5276,9 +5281,10 @@ async def _blocks_weight_limit() -> bool: uids=uids, weights=weights, version_key=version_key, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) except Exception as e: logging.error(f"Error setting weights: {e}") diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index a6586a784f..53aa650f2d 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -157,28 +157,27 @@ async def set_weights_extrinsic( uids: Union[NDArray[np.int64], "torch.LongTensor", list], weights: Union[NDArray[np.float32], "torch.FloatTensor", list], version_key: int = version_as_int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, period: Optional[int] = 8, raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, ) -> tuple[bool, str]: - """Sets the given weights and values on chain for a given wallet hotkey account. + """ + Sets the given weights and values on chain for a given wallet hotkey account. - Args: + Parameters: subtensor: Bittensor subtensor object. wallet: Bittensor wallet object. netuid: The ``netuid`` of the subnet to set weights for. uids: The ``uint64`` uids of destination neurons. weights: The weights to set. These must be ``float``s and correspond to the passed ``uid``s. version_key: The version key of the validator. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning ``True``, - or returns ``False`` if the extrinsic fails to be finalized within the timeout. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - raise_error: raises the relevant exception rather than returning `False` if unsuccessful. + raise_error: Whether to raise an error if the transaction fails. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: tuple[bool, str]: @@ -223,4 +222,5 @@ async def set_weights_extrinsic( logging.info("Successfully set weights and Finalized.") else: logging.error(f"{get_function_name}: {message}") + return success, message diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 997b13b410..d0997605b4 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -54,63 +54,58 @@ def commit_reveal_extrinsic( - True and a success message if the extrinsic is successfully submitted or processed. - False and an error message if the submission fails or the wallet cannot be unlocked. """ - try: - uids, weights = convert_and_normalize_weights_and_uids(uids, weights) + uids, weights = convert_and_normalize_weights_and_uids(uids, weights) - current_block = subtensor.get_current_block() - subnet_hyperparameters = subtensor.get_subnet_hyperparameters( - netuid, block=current_block - ) - tempo = subnet_hyperparameters.tempo - subnet_reveal_period_epochs = subnet_hyperparameters.commit_reveal_period + current_block = subtensor.get_current_block() + subnet_hyperparameters = subtensor.get_subnet_hyperparameters( + netuid, block=current_block + ) + tempo = subnet_hyperparameters.tempo + subnet_reveal_period_epochs = subnet_hyperparameters.commit_reveal_period - # Encrypt `commit_hash` with t-lock and `get reveal_round` - commit_for_reveal, reveal_round = get_encrypted_commit( - uids=uids, - weights=weights, - version_key=version_key, - tempo=tempo, - current_block=current_block, - netuid=netuid, - subnet_reveal_period_epochs=subnet_reveal_period_epochs, - block_time=block_time, - hotkey=wallet.hotkey.public_key, - ) + # Encrypt `commit_hash` with t-lock and `get reveal_round` + commit_for_reveal, reveal_round = get_encrypted_commit( + uids=uids, + weights=weights, + version_key=version_key, + tempo=tempo, + current_block=current_block, + netuid=netuid, + subnet_reveal_period_epochs=subnet_reveal_period_epochs, + block_time=block_time, + hotkey=wallet.hotkey.public_key, + ) - logging.info( - f"Committing weights hash [blue]{commit_for_reveal.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " - f"reveal round [blue]{reveal_round}[/blue]..." - ) + logging.info( + f"Committing weights hash [blue]{commit_for_reveal.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " + f"reveal round [blue]{reveal_round}[/blue]..." + ) - call = subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function="commit_timelocked_weights", - call_params={ - "netuid": netuid, - "commit": commit_for_reveal, - "reveal_round": reveal_round, - "commit_reveal_version": commit_reveal_version, - }, - ) - success, message = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - sign_with="hotkey", - period=period, - raise_error=raise_error, - ) + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="commit_timelocked_weights", + call_params={ + "netuid": netuid, + "commit": commit_for_reveal, + "reveal_round": reveal_round, + "commit_reveal_version": commit_reveal_version, + }, + ) + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", + period=period, + raise_error=raise_error, + ) - if not success: - logging.error(message) - return False, message + if not success: + logging.error(message) + return False, message - logging.success( - f"[green]Finalized![/green] Weights committed with reveal round [blue]{reveal_round}[/blue]." - ) - return True, f"reveal_round:{reveal_round}" - - except Exception as e: - logging.error(f":cross_mark: [red]Failed. Error:[/red] {e}") - return False, str(e) + logging.success( + f"[green]Finalized![/green] Weights committed with reveal round [blue]{reveal_round}[/blue]." + ) + return True, f"reveal_round:{reveal_round}" diff --git a/bittensor/core/extrinsics/weights.py b/bittensor/core/extrinsics/weights.py index 46f660a514..0a3aa8e264 100644 --- a/bittensor/core/extrinsics/weights.py +++ b/bittensor/core/extrinsics/weights.py @@ -160,28 +160,27 @@ def set_weights_extrinsic( uids: Union[NDArray[np.int64], "torch.LongTensor", list], weights: Union[NDArray[np.float32], "torch.FloatTensor", list], version_key: int = version_as_int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = False, period: Optional[int] = 8, raise_error: bool = False, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, ) -> tuple[bool, str]: - """Sets the given weights and values on a chain for a wallet hotkey account. + """ + Sets the given weights and values on a chain for a wallet hotkey account. - Args: + Parameters: subtensor: Bittensor subtensor object. wallet: Bittensor wallet object. netuid: The ``netuid`` of the subnet to set weights for. uids: The ``uint64`` uids of destination neurons. weights: The weights to set. These must be ``float``s and correspond to the passed ``uid``s. version_key: The version key of the validator. - wait_for_inclusion: 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. - wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning ``True``, - or returns ``False`` if the extrinsic fails to be finalized within the timeout. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - raise_error: raises the relevant exception rather than returning `False` if unsuccessful. + raise_error: Whether to raise an error if the transaction fails. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: tuple[bool, str]: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 47bcc61715..7924b4b584 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3990,7 +3990,7 @@ def set_weights( max_retries: int = 5, version_key: int = version_as_int, period: Optional[int] = 8, - raise_error: bool = False, + raise_error: bool = True, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ) -> tuple[bool, str]: @@ -4052,21 +4052,25 @@ def _blocks_weight_limit() -> bool: f"Committing weights for subnet [blue]{netuid}[/blue]. " f"Attempt [blue]{retries + 1}[blue] of [green]{max_retries}[/green]." ) - success, message = commit_reveal_extrinsic( - subtensor=self, - wallet=wallet, - netuid=netuid, - uids=uids, - weights=weights, - block_time=block_time, - commit_reveal_version=commit_reveal_version, - version_key=version_key, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + try: + success, message = commit_reveal_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + uids=uids, + weights=weights, + block_time=block_time, + commit_reveal_version=commit_reveal_version, + version_key=version_key, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + except Exception as e: + logging.error(f"Error setting weights: {e}") retries += 1 + return success, message else: # go with classic `set_weights_extrinsic` @@ -4084,9 +4088,10 @@ def _blocks_weight_limit() -> bool: uids=uids, weights=weights, version_key=version_key, + period=period, + raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, ) except Exception as e: logging.error(f"Error setting weights: {e}") diff --git a/migration.md b/migration.md index 1ebdcf942e..09004c4cf8 100644 --- a/migration.md +++ b/migration.md @@ -209,3 +209,4 @@ wait_for_finalization: bool = False, - [x] `.unstake_multiple_extrinsic` and `subtensor.unstake_multiple` - [x] `.commit_weights_extrinsic` and `subtensor.commit_weights` - [x] `.reveal_weights_extrinsic` and `subtensor.reveal_weights` +- [x] `.set_weights_extrinsic` and `subtensor.set_weights` \ No newline at end of file diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index e9490bcbc4..92dc926f51 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -249,15 +249,12 @@ def test_commit_reveal_v3_extrinsic_exception(mocker, subtensor, fake_wallet): side_effect=Exception("Test Error"), ) - # Call - success, message = commit_reveal.commit_reveal_extrinsic( - subtensor=subtensor, - wallet=fake_wallet, - netuid=fake_netuid, - uids=fake_uids, - weights=fake_weights, - ) - - # Asserts - assert success is False - assert "Test Error" in message + # Call + Asserts + with pytest.raises(Exception): + commit_reveal.commit_reveal_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + uids=fake_uids, + weights=fake_weights, + ) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 57c94ed227..28decac35a 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2805,6 +2805,7 @@ async def test_set_weights_success(subtensor, fake_wallet, mocker): wait_for_inclusion=True, weights=fake_weights, period=8, + raise_error=True, ) mocked_weights_rate_limit.assert_called_once_with(fake_netuid) assert result is True diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 56b18759a5..b0d7af970d 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1202,6 +1202,7 @@ def test_set_weights(subtensor, mocker, fake_wallet): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, period=8, + raise_error=True, ) assert result == expected_result @@ -3081,7 +3082,7 @@ def test_set_weights_with_commit_reveal_enabled(subtensor, fake_wallet, mocker): wait_for_finalization=fake_wait_for_finalization, block_time=12.0, period=8, - raise_error=False, + raise_error=True, ) assert result == mocked_commit_reveal_v3_extrinsic.return_value From f3d663361cedaa36b7b9508dd4adfea7f4360ffc Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 16:33:35 -0700 Subject: [PATCH 39/49] parameter `safe_staking: bool` renamed to `safe_unstaking: bool` for `.unstake_extrinsic` and `subtensor.unstake` --- bittensor/core/async_subtensor.py | 6 +++--- bittensor/core/extrinsics/asyncex/unstaking.py | 6 +++--- bittensor/core/extrinsics/unstaking.py | 8 ++++---- bittensor/core/subtensor.py | 6 +++--- migration.md | 1 + tests/e2e_tests/test_staking.py | 12 ++++++------ tests/unit_tests/test_subtensor.py | 12 ++++++------ 7 files changed, 26 insertions(+), 25 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 259eff3e11..be6b322a7b 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5633,7 +5633,7 @@ async def unstake( hotkey_ss58: str, amount: Balance, allow_partial_stake: bool = False, - safe_staking: bool = False, + safe_unstaking: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, raise_error: bool = False, @@ -5654,7 +5654,7 @@ async def unstake( exceed the tolerance. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. - safe_staking: If true, enables price safety checks to protect against fluctuating prices. The unstake + safe_unstaking: If true, enables price safety checks to protect against fluctuating prices. The unstake will only execute if the price change doesn't exceed the rate tolerance. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. @@ -5678,7 +5678,7 @@ async def unstake( amount=amount, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, - safe_staking=safe_staking, + safe_unstaking=safe_unstaking, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 609349ec07..41723cb259 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -21,7 +21,7 @@ async def unstake_extrinsic( amount: Balance, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - safe_staking: bool = False, + safe_unstaking: bool = False, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -37,7 +37,7 @@ async def unstake_extrinsic( hotkey_ss58: The ``ss58`` address of the hotkey to unstake from. amount: Amount to stake as Bittensor balance. allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded. - safe_staking: If true, enables price safety checks. + safe_unstaking: If true, enables price safety checks. rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%). period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can @@ -84,7 +84,7 @@ async def unstake_extrinsic( "netuid": netuid, "amount_unstaked": amount.rao, } - if safe_staking: + if safe_unstaking: pool = await subtensor.subnet(netuid=netuid) base_price = pool.price.tao diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index fff7cdd693..5aaf162ecc 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -21,7 +21,7 @@ def unstake_extrinsic( amount: Balance, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - safe_staking: bool = False, + safe_unstaking: bool = False, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -38,7 +38,7 @@ def unstake_extrinsic( amount: Amount to stake as Bittensor balance. allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded. rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%). - safe_staking: If true, enables price safety checks. + safe_unstaking: If true, enables price safety checks. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -84,7 +84,7 @@ def unstake_extrinsic( "amount_unstaked": amount.rao, } - if safe_staking: + if safe_unstaking: pool = subtensor.subnet(netuid=netuid) base_price = pool.price.tao @@ -170,7 +170,7 @@ def unstake_extrinsic( ) return True - if safe_staking and "Custom error: 8" in message: + if safe_unstaking and "Custom error: 8" in message: logging.error( ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or" " enable partial staking." diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 7924b4b584..9168faa99a 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4442,7 +4442,7 @@ def unstake( amount: Balance, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, - safe_staking: bool = False, + safe_unstaking: bool = False, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4462,7 +4462,7 @@ def unstake( exceed the tolerance. rate_tolerance: The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. - safe_staking: If true, enables price safety checks to protect against fluctuating prices. The unstake + safe_unstaking: If true, enables price safety checks to protect against fluctuating prices. The unstake will only execute if the price change doesn't exceed the rate tolerance. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. @@ -4487,7 +4487,7 @@ def unstake( amount=amount, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, - safe_staking=safe_staking, + safe_unstaking=safe_unstaking, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, diff --git a/migration.md b/migration.md index 09004c4cf8..26646a2603 100644 --- a/migration.md +++ b/migration.md @@ -204,6 +204,7 @@ wait_for_finalization: bool = False, - parameter `netuid: Optional[int]` is now required -> `netuid: int` - parameter `hotkey_ss58: Optional[str]` is now required -> `hotkey_ss58: str` - parameter `amount: Optional[Balance]` is now required -> `amount: Balance` + - parameter `safe_staking: bool` renamed to `safe_unstaking: bool` - parameter `unstake_all: bool` removed (use `unstake_all_extrinsic` for unstake all stake) - [x] `.unstake_all_extrinsic` and `subtensor.unstake_all` - [x] `.unstake_multiple_extrinsic` and `subtensor.unstake_multiple` diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 6ea1ffa3dc..4a9e65eba5 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -800,7 +800,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=full_stake, - safe_staking=True, + safe_unstaking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) @@ -825,7 +825,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=current_stake, - safe_staking=True, + safe_unstaking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=True, ) @@ -847,7 +847,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=partial_unstake, - safe_staking=True, + safe_unstaking=True, rate_tolerance=0.3, # 30% allow_partial_stake=False, ) @@ -996,7 +996,7 @@ async def test_safe_staking_scenarios_async( netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=full_stake, - safe_staking=True, + safe_unstaking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) @@ -1021,7 +1021,7 @@ async def test_safe_staking_scenarios_async( netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=current_stake, - safe_staking=True, + safe_unstaking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=True, ) @@ -1043,7 +1043,7 @@ async def test_safe_staking_scenarios_async( netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=partial_unstake, - safe_staking=True, + safe_unstaking=True, rate_tolerance=0.3, # 30% allow_partial_stake=False, ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index b0d7af970d..1044497093 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2846,7 +2846,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): amount=fake_amount, wait_for_inclusion=True, wait_for_finalization=False, - safe_staking=False, + safe_unstaking=False, allow_partial_stake=False, rate_tolerance=0.005, ) @@ -2858,7 +2858,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, amount=Balance.from_rao(fake_amount), - safe_staking=False, + safe_unstaking=False, allow_partial_stake=False, rate_tolerance=0.005, period=None, @@ -2869,8 +2869,8 @@ def test_unstake_success(mocker, subtensor, fake_wallet): assert result == mock_unstake_extrinsic.return_value -def test_unstake_with_safe_staking(mocker, subtensor, fake_wallet): - """Test unstake with safe staking parameters enabled.""" +def test_unstake_with_safe_unstaking(mocker, subtensor, fake_wallet): + """Test unstake with `safe_unstaking` parameters enabled.""" fake_hotkey_ss58 = "hotkey_1" fake_amount = 10.0 fake_netuid = 14 @@ -2886,7 +2886,7 @@ def test_unstake_with_safe_staking(mocker, subtensor, fake_wallet): amount=fake_amount, wait_for_inclusion=True, wait_for_finalization=False, - safe_staking=True, + safe_unstaking=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, ) @@ -2898,7 +2898,7 @@ def test_unstake_with_safe_staking(mocker, subtensor, fake_wallet): netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, amount=Balance.from_rao(fake_amount), - safe_staking=True, + safe_unstaking=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, period=None, From 2a925bc8da24ecc53b4ef598bd6f0e544a7cc083 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 16:43:57 -0700 Subject: [PATCH 40/49] in `.unstake_extrinsic` and `subtensor.unstake` parameter `safe_staking: bool` renamed to `safe_unstaking: bool` + in `.swap_stake_extrinsic` and `subtensor.swap_stake` parameter `safe_staking: bool` renamed to `safe_swapping: bool` --- bittensor/core/async_subtensor.py | 6 +++--- bittensor/core/extrinsics/asyncex/move_stake.py | 8 ++++---- bittensor/core/extrinsics/asyncex/unstaking.py | 2 +- bittensor/core/extrinsics/move_stake.py | 8 ++++---- bittensor/core/subtensor.py | 6 +++--- migration.md | 4 +++- tests/e2e_tests/test_staking.py | 8 ++++---- tests/unit_tests/test_subtensor.py | 8 ++++---- 8 files changed, 26 insertions(+), 24 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index be6b322a7b..17cae29646 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5432,7 +5432,7 @@ async def swap_stake( origin_netuid: int, destination_netuid: int, amount: Balance, - safe_staking: bool = False, + safe_swapping: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, @@ -5450,7 +5450,7 @@ async def swap_stake( origin_netuid: The netuid from which stake is removed. destination_netuid: The netuid to which stake is added. amount: The amount to swap. - safe_staking: If true, enables price safety checks to protect against fluctuating prices. The swap + safe_swapping: If true, enables price safety checks to protect against fluctuating prices. The swap will only execute if the price ratio between subnets doesn't exceed the rate tolerance. allow_partial_stake: If true and safe_staking is enabled, allows partial stake swaps when the full amount would exceed the price tolerance. If false, the entire swap fails if it would exceed the tolerance. @@ -5482,7 +5482,7 @@ async def swap_stake( origin_netuid=origin_netuid, destination_netuid=destination_netuid, amount=amount, - safe_staking=safe_staking, + safe_swapping=safe_swapping, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, period=period, diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index 53018613c9..0934a3ce9d 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -159,7 +159,7 @@ async def swap_stake_extrinsic( origin_netuid: int, destination_netuid: int, amount: Balance, - safe_staking: bool = False, + safe_swapping: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, @@ -177,7 +177,7 @@ async def swap_stake_extrinsic( origin_netuid: The source subnet UID. destination_netuid: The destination subnet UID. amount: Amount to swap. - safe_staking: If true, enables price safety checks to protect against price impact. + safe_swapping: If true, enables price safety checks to protect against price impact. allow_partial_stake: If true, allows partial stake swaps when the full amount would exceed the price tolerance. rate_tolerance: Maximum allowed increase in a price ratio (0.005 = 0.5%). period: The number of blocks during which the transaction will remain valid after it's submitted. If the @@ -218,7 +218,7 @@ async def swap_stake_extrinsic( "alpha_amount": amount.rao, } - if safe_staking: + if safe_swapping: origin_pool, destination_pool = await asyncio.gather( subtensor.subnet(netuid=origin_netuid), subtensor.subnet(netuid=destination_netuid), @@ -288,7 +288,7 @@ async def swap_stake_extrinsic( return True else: - if safe_staking and "Custom error: 8" in err_msg: + if safe_swapping and "Custom error: 8" in err_msg: logging.error( ":cross_mark: [red]Failed[/red]: Price ratio exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 41723cb259..7a0e828e34 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -171,7 +171,7 @@ async def unstake_extrinsic( ) return True - if safe_staking and "Custom error: 8" in message: + if safe_unstaking and "Custom error: 8" in message: logging.error( ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index cbef3c8117..bf7878083a 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -156,7 +156,7 @@ def swap_stake_extrinsic( origin_netuid: int, destination_netuid: int, amount: Balance, - safe_staking: bool = False, + safe_swapping: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, @@ -174,7 +174,7 @@ def swap_stake_extrinsic( origin_netuid: The source subnet UID. destination_netuid: The destination subnet UID. amount: Amount to swap. - safe_staking: If true, enables price safety checks to protect against price impact. + safe_swapping: If true, enables price safety checks to protect against price impact. allow_partial_stake: If true, allows partial stake swaps when the full amount would exceed the price tolerance. rate_tolerance: Maximum allowed increase in a price ratio (0.005 = 0.5%). period: The number of blocks during which the transaction will remain valid after it's submitted. If the @@ -216,7 +216,7 @@ def swap_stake_extrinsic( "alpha_amount": amount.rao, } - if safe_staking: + if safe_swapping: origin_pool = subtensor.subnet(netuid=origin_netuid) destination_pool = subtensor.subnet(netuid=destination_netuid) swap_rate_ratio = origin_pool.price.rao / destination_pool.price.rao @@ -284,7 +284,7 @@ def swap_stake_extrinsic( return True else: - if safe_staking and "Custom error: 8" in err_msg: + if safe_swapping and "Custom error: 8" in err_msg: logging.error( ":cross_mark: [red]Failed[/red]: Price ratio exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 9168faa99a..faec3edb60 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -4240,7 +4240,7 @@ def swap_stake( origin_netuid: int, destination_netuid: int, amount: Balance, - safe_staking: bool = False, + safe_swapping: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, period: Optional[int] = None, @@ -4258,7 +4258,7 @@ def swap_stake( origin_netuid: The netuid from which stake is removed. destination_netuid: The netuid to which stake is added. amount: The amount to swap. - safe_staking: If true, enables price safety checks to protect against fluctuating prices. The swap + safe_swapping: If true, enables price safety checks to protect against fluctuating prices. The swap will only execute if the price ratio between subnets doesn't exceed the rate tolerance. allow_partial_stake: If true and safe_staking is enabled, allows partial stake swaps when the full amount would exceed the price tolerance. If false, the entire swap fails if it would exceed the tolerance. @@ -4290,7 +4290,7 @@ def swap_stake( origin_netuid=origin_netuid, destination_netuid=destination_netuid, amount=amount, - safe_staking=safe_staking, + safe_swapping=safe_swapping, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, period=period, diff --git a/migration.md b/migration.md index 26646a2603..0b3c5a2fe5 100644 --- a/migration.md +++ b/migration.md @@ -33,7 +33,7 @@ amount: Optional[Balance] = None, rate_tolerance: float = 0.005, allow_partial_stake: bool = False, - safe_staking: bool = False, + safe_swapping: bool = False, period: Optional[int] = None, raise_error: bool = True, wait_for_inclusion: bool = True, @@ -180,6 +180,8 @@ wait_for_finalization: bool = False, - [x] `.toggle_user_liquidity_extrinsic` and `subtensor.toggle_user_liquidity` - [x] `.transfer_stake_extrinsic` and `subtensor.transfer_stake` - [x] `.swap_stake_extrinsic` and `subtensor.swap_stake` + - Changes in `swap_stake_extrinsic` and `subtensor.swap_stake`: + - parameter `safe_staking: bool` renamed to `safe_swapping: bool` - [x] `.move_stake_extrinsic` and `subtensor.move_stake` - Changes in `move_stake_extrinsic` and `subtensor.move_stake`: - parameter `origin_hotkey` renamed to `origin_hotkey_ss58` diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 4a9e65eba5..17cc94b51c 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -1120,7 +1120,7 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): amount=stake_swap_amount, wait_for_inclusion=True, wait_for_finalization=True, - safe_staking=True, + safe_swapping=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) @@ -1146,7 +1146,7 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): amount=stake_swap_amount, wait_for_inclusion=True, wait_for_finalization=True, - safe_staking=True, + safe_swapping=True, rate_tolerance=0.3, # 30% allow_partial_stake=True, ) @@ -1236,7 +1236,7 @@ async def test_safe_swap_stake_scenarios_async( amount=stake_swap_amount, wait_for_inclusion=True, wait_for_finalization=True, - safe_staking=True, + safe_swapping=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) @@ -1262,7 +1262,7 @@ async def test_safe_swap_stake_scenarios_async( amount=stake_swap_amount, wait_for_inclusion=True, wait_for_finalization=True, - safe_staking=True, + safe_swapping=True, rate_tolerance=0.3, # 30% allow_partial_stake=True, ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 1044497093..9f847d8bf2 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2930,7 +2930,7 @@ def test_swap_stake_success(mocker, subtensor, fake_wallet): amount=fake_amount, wait_for_inclusion=True, wait_for_finalization=False, - safe_staking=False, + safe_swapping=False, allow_partial_stake=False, rate_tolerance=0.005, ) @@ -2945,7 +2945,7 @@ def test_swap_stake_success(mocker, subtensor, fake_wallet): amount=Balance.from_rao(fake_amount), wait_for_inclusion=True, wait_for_finalization=False, - safe_staking=False, + safe_swapping=False, allow_partial_stake=False, rate_tolerance=0.005, period=None, @@ -2976,7 +2976,7 @@ def test_swap_stake_with_safe_staking(mocker, subtensor, fake_wallet): amount=fake_amount, wait_for_inclusion=True, wait_for_finalization=False, - safe_staking=True, + safe_swapping=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, ) @@ -2991,7 +2991,7 @@ def test_swap_stake_with_safe_staking(mocker, subtensor, fake_wallet): amount=Balance.from_rao(fake_amount), wait_for_inclusion=True, wait_for_finalization=False, - safe_staking=True, + safe_swapping=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, period=None, From f37c1cab6ffdc94cb75657c97493fe320e9bee1e Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 16:49:01 -0700 Subject: [PATCH 41/49] update migration.md --- migration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration.md b/migration.md index 0b3c5a2fe5..65904a1229 100644 --- a/migration.md +++ b/migration.md @@ -53,9 +53,9 @@ 5. Since SDK is not a responsible tool, try to remove all calculations inside extrinsics that do not affect the result, but are only used in logging. Actually, this should be applied not to extrinsics only but for all codebase. -6. Remove `unstake_all` parameter from `unstake_extrinsic` since we have `unstake_all_extrinsic`which is calles another subtensor function. +6. ✅ Remove `unstake_all` parameter from `unstake_extrinsic` since we have `unstake_all_extrinsic`which is calles another subtensor function. -7. `unstake` and `unstake_multiple` extrinsics should have `safe_unstaking` parameters instead of `safe_staking`. +7. ✅ `unstake` and `unstake_multiple` extrinsics should have `safe_unstaking` parameters instead of `safe_staking`. 8. ✅ Remove `_do*` extrinsic calls and combine them with extrinsic logic. From 779717c977a67464d9d04cda4e889982e1997124 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 17:30:36 -0700 Subject: [PATCH 42/49] `test_dendrite*` no flaky --- tests/e2e_tests/test_dendrite.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/e2e_tests/test_dendrite.py b/tests/e2e_tests/test_dendrite.py index d78531ef75..f41590a33a 100644 --- a/tests/e2e_tests/test_dendrite.py +++ b/tests/e2e_tests/test_dendrite.py @@ -119,7 +119,6 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): # Refresh metagraph metagraph = subtensor.metagraphs.metagraph(alice_subnet_netuid) - metagraph.sync() bob_neuron = metagraph.neurons[1] # Assert alpha is close to stake equivalent @@ -193,7 +192,7 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall assert await async_subtensor.staking.add_stake( wallet=alice_wallet, netuid=alice_subnet_netuid, - amount=Balance.from_tao(1), + amount=Balance.from_tao(5), wait_for_inclusion=False, wait_for_finalization=False, ) From e22bf9b2e959cbea2d7d698917ff52654fce6d68 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 18:23:35 -0700 Subject: [PATCH 43/49] test_dendrite --- tests/e2e_tests/test_dendrite.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/tests/e2e_tests/test_dendrite.py b/tests/e2e_tests/test_dendrite.py index f41590a33a..d8d780ef6d 100644 --- a/tests/e2e_tests/test_dendrite.py +++ b/tests/e2e_tests/test_dendrite.py @@ -18,6 +18,8 @@ logging.on() logging.set_debug() +NON_FAST_RUNTIME_TEMPO = 10 + @pytest.mark.asyncio async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): @@ -52,13 +54,23 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): "Subnet is not active." ) - # Make sure Alice is Top Validator (for non-fast-runtime only) - if subtensor.chain.is_fast_blocks(): + if not subtensor.chain.is_fast_blocks(): + # Make sure Alice is Top Validator (for non-fast-runtime only) assert subtensor.staking.add_stake( wallet=alice_wallet, netuid=alice_subnet_netuid, amount=Balance.from_tao(1), ) + # set tempo to 10 block for non-fast-runtime + assert sudo_set_admin_utils( + substrate=subtensor.substrate, + wallet=alice_wallet, + call_function="sudo_set_tempo", + call_params={ + "netuid": alice_subnet_netuid, + "tempo": NON_FAST_RUNTIME_TEMPO, + }, + ) # update max_allowed_validators so only one neuron can get validator_permit assert sudo_set_admin_utils( @@ -187,8 +199,8 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall "Subnet is not active." ) - # Make sure Alice is Top Validator (for non-fast-runtime only) - if await async_subtensor.chain.is_fast_blocks(): + if not await async_subtensor.chain.is_fast_blocks(): + # Make sure Alice is Top Validator (for non-fast-runtime only) assert await async_subtensor.staking.add_stake( wallet=alice_wallet, netuid=alice_subnet_netuid, @@ -196,6 +208,16 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall wait_for_inclusion=False, wait_for_finalization=False, ) + # set tempo to 10 block for non-fast-runtime + assert await async_sudo_set_admin_utils( + substrate=async_subtensor.substrate, + wallet=alice_wallet, + call_function="sudo_set_tempo", + call_params={ + "netuid": alice_subnet_netuid, + "tempo": NON_FAST_RUNTIME_TEMPO, + }, + ) # update max_allowed_validators so only one neuron can get validator_permit assert await async_sudo_set_admin_utils( From 1c7678df495a63e71a5c65ff0ba6004bf7cca3f6 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 20:02:09 -0700 Subject: [PATCH 44/49] Changes in `.add_stake_extrinsic` and `subtensor.add_stake`: see migration.md --- bittensor/core/async_subtensor.py | 9 +- bittensor/core/extrinsics/asyncex/staking.py | 213 ++++++++----------- bittensor/core/extrinsics/staking.py | 213 ++++++++----------- bittensor/core/subtensor.py | 9 +- migration.md | 5 + tests/e2e_tests/test_delegate.py | 16 +- tests/e2e_tests/test_dendrite.py | 4 + tests/e2e_tests/test_liquid_alpha.py | 2 + tests/e2e_tests/test_liquidity.py | 16 +- tests/e2e_tests/test_metagraph.py | 6 +- tests/e2e_tests/test_root_set_weights.py | 8 +- tests/e2e_tests/test_set_weights.py | 8 +- tests/e2e_tests/test_staking.py | 74 ++----- tests/unit_tests/test_subtensor.py | 16 +- 14 files changed, 247 insertions(+), 352 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 17cae29646..f0056d124a 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4277,9 +4277,9 @@ async def sign_and_send_extrinsic( async def add_stake( self, wallet: "Wallet", - hotkey_ss58: Optional[str] = None, - netuid: Optional[int] = None, - amount: Optional[Balance] = None, + netuid: int, + hotkey_ss58: str, + amount: Balance, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, @@ -4295,9 +4295,8 @@ async def add_stake( Parameters: wallet: The wallet to be used for staking. - hotkey_ss58: The SS58 address of the hotkey associated with the neuron to which you intend to delegate your - stake. If not specified, the wallet's hotkey will be used. netuid: The unique identifier of the subnet to which the neuron belongs. + hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. amount: The amount of TAO to stake. safe_staking: If true, enables price safety checks to protect against fluctuating prices. The stake will only execute if the price change doesn't exceed the rate tolerance. Default is ``False``. diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index f32d9f1a0c..c5295887e4 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -16,10 +16,9 @@ async def add_stake_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", - old_balance: Optional[Balance] = None, - hotkey_ss58: Optional[str] = None, - netuid: Optional[int] = None, - amount: Optional[Balance] = None, + netuid: int, + hotkey_ss58: str, + amount: Balance, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, @@ -36,11 +35,9 @@ async def add_stake_extrinsic( Parameters: subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object. - old_balance: the balance prior to the staking - hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. If not - specified, the wallet's hotkey will be used. netuid: The unique identifier of the subnet to which the neuron belongs. - amount: Amount to stake as Bittensor balance in TAO always, `None` if staking all. + hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. + amount: Amount to stake as Bittensor balance in TAO always. safe_staking: If True, enables price safety checks. Default is ``False``. allow_partial_stake: If True, allows partial unstaking if price tolerance exceeded. Default is ``False``. rate_tolerance: Maximum allowed price increase percentage (0.005 = 0.5%). Default is ``0.005``. @@ -63,15 +60,10 @@ async def add_stake_extrinsic( logging.error(unlock.message) return False - # Default to wallet's own hotkey if the value is not passed. - if hotkey_ss58 is None: - hotkey_ss58 = wallet.hotkey.ss58_address - logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" ) - if not old_balance: - old_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address) + old_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address) block_hash = await subtensor.substrate.get_chain_head() # Get current stake and existential deposit @@ -85,135 +77,116 @@ async def add_stake_extrinsic( subtensor.get_existential_deposit(block_hash=block_hash), ) - # Convert to bittensor.Balance - if amount is None: - # Stake it all. - staking_balance = Balance.from_tao(old_balance.tao) - logging.warning( - f"Didn't receive any staking amount. Staking all available balance: [blue]{staking_balance}[/blue] " - f"from wallet: [blue]{wallet.name}[/blue]" - ) - else: - staking_balance = amount - # Leave existential balance to keep key alive. - if staking_balance > old_balance - existential_deposit: + if amount > old_balance - existential_deposit: # If we are staking all, we need to leave at least the existential deposit. - staking_balance = old_balance - existential_deposit + amount = old_balance - existential_deposit else: - staking_balance = staking_balance + amount = amount # Check enough to stake. - if staking_balance > old_balance: + if amount > old_balance: logging.error(":cross_mark: [red]Not enough stake:[/red]") logging.error(f"\t\tbalance:{old_balance}") - logging.error(f"\t\tamount: {staking_balance}") + logging.error(f"\t\tamount: {amount}") logging.error(f"\t\twallet: {wallet.name}") return False - try: - call_params = { - "hotkey": hotkey_ss58, - "netuid": netuid, - "amount_staked": staking_balance.rao, - } + call_params = { + "hotkey": hotkey_ss58, + "netuid": netuid, + "amount_staked": amount.rao, + } - if safe_staking: - pool = await subtensor.subnet(netuid=netuid) - base_price = pool.price.tao + if safe_staking: + pool = await subtensor.subnet(netuid=netuid) + base_price = pool.price.tao - if pool.netuid == 0: - price_with_tolerance = base_price - else: - price_with_tolerance = base_price * (1 + rate_tolerance) - - logging.info( - f":satellite: [magenta]Safe Staking to:[/magenta] " - f"[blue]netuid: [green]{netuid}[/green], amount: [green]{staking_balance}[/green], " - f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " - f"price limit: [green]{price_with_tolerance}[/green], " - f"original price: [green]{base_price}[/green], " - f"with partial stake: [green]{allow_partial_stake}[/green] " - f"on [blue]{subtensor.network}[/blue][/magenta]...[/magenta]" - ) + price_with_tolerance = ( + base_price if pool.netuid == 0 else base_price * (1 + rate_tolerance) + ) - limit_price = Balance.from_tao(price_with_tolerance).rao - call_params.update( - { - "limit_price": limit_price, - "allow_partial": allow_partial_stake, - } - ) - call_function = "add_stake_limit" - else: - logging.info( - f":satellite: [magenta]Staking to:[/magenta] " - f"[blue]netuid: [green]{netuid}[/green], amount: [green]{staking_balance}[/green] " - f"on [blue]{subtensor.network}[/blue][magenta]...[/magenta]" - ) - call_function = "add_stake" + logging.info( + f":satellite: [magenta]Safe Staking to:[/magenta] " + f"[blue]netuid: [green]{netuid}[/green], amount: [green]{amount}[/green], " + f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " + f"price limit: [green]{price_with_tolerance}[/green], " + f"original price: [green]{base_price}[/green], " + f"with partial stake: [green]{allow_partial_stake}[/green] " + f"on [blue]{subtensor.network}[/blue][/magenta]...[/magenta]" + ) - call = await subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function=call_function, - call_params=call_params, + limit_price = Balance.from_tao(price_with_tolerance).rao + call_params.update( + { + "limit_price": limit_price, + "allow_partial": allow_partial_stake, + } ) - success, message = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - nonce_key="coldkeypub", - sign_with="coldkey", - use_nonce=True, - period=period, - raise_error=raise_error, + call_function = "add_stake_limit" + else: + logging.info( + f":satellite: [magenta]Staking to:[/magenta] " + f"[blue]netuid: [green]{netuid}[/green], amount: [green]{amount}[/green] " + f"on [blue]{subtensor.network}[/blue][magenta]...[/magenta]" ) - if success: # If we successfully staked. - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True + call_function = "add_stake" - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function=call_function, + call_params=call_params, + ) + success, message = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + nonce_key="coldkeypub", + sign_with="coldkey", + use_nonce=True, + period=period, + raise_error=raise_error, + ) + if success: # If we successfully staked. + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return True - logging.info( - f":satellite: [magenta]Checking Balance on:[/magenta] " - f"[blue]{subtensor.network}[/blue] [magenta]...[/magenta]" - ) - new_block_hash = await subtensor.substrate.get_chain_head() - new_balance, new_stake = await asyncio.gather( - subtensor.get_balance( - wallet.coldkeypub.ss58_address, block_hash=new_block_hash - ), - subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=netuid, - block_hash=new_block_hash, - ), - ) + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - logging.info( - f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" - ) - logging.info( - f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" - ) - return True + logging.info( + f":satellite: [magenta]Checking Balance on:[/magenta] " + f"[blue]{subtensor.network}[/blue] [magenta]...[/magenta]" + ) + new_block_hash = await subtensor.substrate.get_chain_head() + new_balance, new_stake = await asyncio.gather( + subtensor.get_balance( + wallet.coldkeypub.ss58_address, block_hash=new_block_hash + ), + subtensor.get_stake( + coldkey_ss58=wallet.coldkeypub.ss58_address, + hotkey_ss58=hotkey_ss58, + netuid=netuid, + block_hash=new_block_hash, + ), + ) - if safe_staking and "Custom error: 8" in message: - logging.error( - ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") - return False + logging.info( + f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" + ) + logging.info( + f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" + ) + return True - except SubstrateRequestException as error: + if safe_staking and "Custom error: 8" in message: logging.error( - f":cross_mark: [red]Add Stake Error: {format_error_message(error)}[/red]" + ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) - return False + else: + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") + return False async def add_stake_multiple_extrinsic( diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index fc08c90871..629d547144 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -15,10 +15,9 @@ def add_stake_extrinsic( subtensor: "Subtensor", wallet: "Wallet", - old_balance: Optional[Balance] = None, - hotkey_ss58: Optional[str] = None, - netuid: Optional[int] = None, - amount: Optional[Balance] = None, + netuid: int, + hotkey_ss58: str, + amount: Balance, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, @@ -29,16 +28,15 @@ def add_stake_extrinsic( ) -> bool: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. - Staking is a fundamental process in the Bittensor network that enables neurons to participate actively and earn incentives. + Staking is a fundamental process in the Bittensor network that enables neurons to participate actively and earn + incentives. - Arguments: + Parameters: subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object. - old_balance: the balance prior to the staking - hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. If not - specified, the wallet's hotkey will be used. netuid: The unique identifier of the subnet to which the neuron belongs. - amount: Amount to stake as Bittensor balance in TAO always, `None` if staking all. + hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. + amount: Amount to stake as Bittensor balance in TAO always. safe_staking: If True, enables price safety checks. Default is ``False``. allow_partial_stake: If True, allows partial unstaking if price tolerance exceeded. Default is ``False``. rate_tolerance: Maximum allowed price increase percentage (0.005 = 0.5%). Default is ``0.005``. @@ -61,15 +59,10 @@ def add_stake_extrinsic( logging.error(unlock.message) return False - # Default to wallet's own hotkey if the value is not passed. - if hotkey_ss58 is None: - hotkey_ss58 = wallet.hotkey.ss58_address - logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" ) - if not old_balance: - old_balance = subtensor.get_balance(wallet.coldkeypub.ss58_address) + old_balance = subtensor.get_balance(wallet.coldkeypub.ss58_address) block = subtensor.get_current_block() # Get current stake and existential deposit @@ -81,131 +74,113 @@ def add_stake_extrinsic( ) existential_deposit = subtensor.get_existential_deposit(block=block) - # Convert to bittensor.Balance - if amount is None: - # Stake it all. - staking_balance = Balance.from_tao(old_balance.tao) - logging.warning( - f"Didn't receive any staking amount. Staking all available balance: [blue]{staking_balance}[/blue] " - f"from wallet: [blue]{wallet.name}[/blue]" - ) - else: - staking_balance = amount - # Leave existential balance to keep key alive. - if staking_balance > old_balance - existential_deposit: + if amount > old_balance - existential_deposit: # If we are staking all, we need to leave at least the existential deposit. - staking_balance = old_balance - existential_deposit + amount = old_balance - existential_deposit # Check enough to stake. - if staking_balance > old_balance: + if amount > old_balance: logging.error(":cross_mark: [red]Not enough stake:[/red]") logging.error(f"\t\tbalance:{old_balance}") - logging.error(f"\t\tamount: {staking_balance}") + logging.error(f"\t\tamount: {amount}") logging.error(f"\t\twallet: {wallet.name}") return False - try: - call_params = { - "hotkey": hotkey_ss58, - "netuid": netuid, - "amount_staked": staking_balance.rao, - } + call_params = { + "hotkey": hotkey_ss58, + "netuid": netuid, + "amount_staked": amount.rao, + } - if safe_staking: - pool = subtensor.subnet(netuid=netuid) - base_price = pool.price.tao + if safe_staking: + pool = subtensor.subnet(netuid=netuid) + base_price = pool.price.tao - if pool.netuid == 0: - price_with_tolerance = base_price - else: - price_with_tolerance = base_price * (1 + rate_tolerance) - - logging.info( - f":satellite: [magenta]Safe Staking to:[/magenta] " - f"[blue]netuid: [green]{netuid}[/green], amount: [green]{staking_balance}[/green], " - f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " - f"price limit: [green]{price_with_tolerance}[/green], " - f"original price: [green]{base_price}[/green], " - f"with partial stake: [green]{allow_partial_stake}[/green] " - f"on [blue]{subtensor.network}[/blue][/magenta]...[/magenta]" - ) - - limit_price = Balance.from_tao(price_with_tolerance).rao - call_params.update( - { - "limit_price": limit_price, - "allow_partial": allow_partial_stake, - } - ) - call_function = "add_stake_limit" - else: - logging.info( - f":satellite: [magenta]Staking to:[/magenta] " - f"[blue]netuid: [green]{netuid}[/green], amount: [green]{staking_balance}[/green] " - f"on [blue]{subtensor.network}[/blue][magenta]...[/magenta]" - ) - call_function = "add_stake" + price_with_tolerance = ( + base_price if pool.netuid == 0 else base_price * (1 + rate_tolerance) + ) - call = subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function=call_function, - call_params=call_params, + logging.info( + f":satellite: [magenta]Safe Staking to:[/magenta] " + f"[blue]netuid: [green]{netuid}[/green], amount: [green]{amount}[/green], " + f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " + f"price limit: [green]{price_with_tolerance}[/green], " + f"original price: [green]{base_price}[/green], " + f"with partial stake: [green]{allow_partial_stake}[/green] " + f"on [blue]{subtensor.network}[/blue][/magenta]...[/magenta]" ) - success, message = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - use_nonce=True, - sign_with="coldkey", - nonce_key="coldkeypub", - period=period, - raise_error=raise_error, + limit_price = Balance.from_tao(price_with_tolerance).rao + call_params.update( + { + "limit_price": limit_price, + "allow_partial": allow_partial_stake, + } + ) + call_function = "add_stake_limit" + else: + logging.info( + f":satellite: [magenta]Staking to:[/magenta] " + f"[blue]netuid: [green]{netuid}[/green], amount: [green]{amount}[/green] " + f"on [blue]{subtensor.network}[/blue][magenta]...[/magenta]" ) - if success is True: # If we successfully staked. - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True + call_function = "add_stake" - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function=call_function, + call_params=call_params, + ) - logging.info( - f":satellite: [magenta]Checking Balance on:[/magenta] " - f"[blue]{subtensor.network}[/blue] [magenta]...[/magenta]" - ) - new_block = subtensor.get_current_block() - new_balance = subtensor.get_balance( - wallet.coldkeypub.ss58_address, block=new_block - ) - new_stake = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=netuid, - block=new_block, - ) - logging.info( - f"Balance: [blue]{old_balance}[/blue] :arrow_right: {new_balance}[/green]" - ) - logging.info( - f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" - ) + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + use_nonce=True, + sign_with="coldkey", + nonce_key="coldkeypub", + period=period, + raise_error=raise_error, + ) + # If we successfully staked. + if success: + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: return True - if safe_staking and "Custom error: 8" in message: - logging.error( - ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") - return False + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - except SubstrateRequestException as error: + logging.info( + f":satellite: [magenta]Checking Balance on:[/magenta] " + f"[blue]{subtensor.network}[/blue] [magenta]...[/magenta]" + ) + new_block = subtensor.get_current_block() + new_balance = subtensor.get_balance( + wallet.coldkeypub.ss58_address, block=new_block + ) + new_stake = subtensor.get_stake( + coldkey_ss58=wallet.coldkeypub.ss58_address, + hotkey_ss58=hotkey_ss58, + netuid=netuid, + block=new_block, + ) + logging.info( + f"Balance: [blue]{old_balance}[/blue] :arrow_right: {new_balance}[/green]" + ) + logging.info( + f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" + ) + return True + + if safe_staking and "Custom error: 8" in message: logging.error( - f":cross_mark: [red]Add Stake Error: {format_error_message(error)}[/red]" + ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) - return False + else: + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") + return False def add_stake_multiple_extrinsic( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index faec3edb60..ff8c1c27d5 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3117,9 +3117,9 @@ def sign_and_send_extrinsic( def add_stake( self, wallet: "Wallet", - hotkey_ss58: Optional[str] = None, - netuid: Optional[int] = None, - amount: Optional[Balance] = None, + netuid: int, + hotkey_ss58: str, + amount: Balance, safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, @@ -3135,9 +3135,8 @@ def add_stake( Parameters: wallet: The wallet to be used for staking. - hotkey_ss58: The SS58 address of the hotkey associated with the neuron to which you intend to delegate your - stake. If not specified, the wallet's hotkey will be used. netuid: The unique identifier of the subnet to which the neuron belongs. + hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. amount: The amount of TAO to stake. safe_staking: If true, enables price safety checks to protect against fluctuating prices. The stake will only execute if the price change doesn't exceed the rate tolerance. Default is ``False``. diff --git a/migration.md b/migration.md index 65904a1229..3d9a3d9f1f 100644 --- a/migration.md +++ b/migration.md @@ -197,6 +197,11 @@ wait_for_finalization: bool = False, - [x] `subtensor.comit` renamed to `subtensor.set_commitment` - [x] `.publish_metadata`, `subtensor.set_commitment` and `subtenor.set_reveal_commitment` - [x] `.add_stake_extrinsic` and `subtensor.add_stake` + - Changes in `.add_stake_extrinsic` and `subtensor.add_stake`: + - parameter `old_balance` removed from async version + - parameter `netuid` required (no Optional anymore) + - parameter `hotkey_ss58` required (no Optional anymore) + - parameter `amount` required (no Optional anymore) - [x] `.add_stake_multiple_extrinsic` and `subtensor.add_stake_multiple` - [x] `.start_call_extrinsic` and `subtensor.start_call` - [x] `.increase_take_extrinsic`, `.decrease_take_extrinsic` and `subtenor.set_reveal_commitment` diff --git a/tests/e2e_tests/test_delegate.py b/tests/e2e_tests/test_delegate.py index 1a36389b57..04417f6615 100644 --- a/tests/e2e_tests/test_delegate.py +++ b/tests/e2e_tests/test_delegate.py @@ -452,11 +452,9 @@ def test_delegates(subtensor, alice_wallet, bob_wallet): subtensor.staking.add_stake( wallet=bob_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(10_000), - wait_for_inclusion=True, - wait_for_finalization=True, ) # let chain update validator_permits @@ -619,11 +617,9 @@ async def test_delegates_async(async_subtensor, alice_wallet, bob_wallet): await async_subtensor.staking.add_stake( wallet=bob_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(10_000), - wait_for_inclusion=True, - wait_for_finalization=True, ) # let chain update validator_permits @@ -686,11 +682,9 @@ def test_nominator_min_required_stake(subtensor, alice_wallet, bob_wallet, dave_ success = subtensor.staking.add_stake( wallet=dave_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=Balance.from_tao(1000), - wait_for_inclusion=True, - wait_for_finalization=True, ) assert success is True @@ -771,11 +765,9 @@ async def test_nominator_min_required_stake_async( success = await async_subtensor.staking.add_stake( wallet=dave_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=Balance.from_tao(1000), - wait_for_inclusion=True, - wait_for_finalization=True, ) assert success is True diff --git a/tests/e2e_tests/test_dendrite.py b/tests/e2e_tests/test_dendrite.py index d8d780ef6d..73d646aadf 100644 --- a/tests/e2e_tests/test_dendrite.py +++ b/tests/e2e_tests/test_dendrite.py @@ -59,6 +59,7 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): assert subtensor.staking.add_stake( wallet=alice_wallet, netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1), ) # set tempo to 10 block for non-fast-runtime @@ -126,6 +127,7 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): assert subtensor.staking.add_stake( wallet=bob_wallet, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=tao, ) @@ -204,6 +206,7 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall assert await async_subtensor.staking.add_stake( wallet=alice_wallet, netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(5), wait_for_inclusion=False, wait_for_finalization=False, @@ -273,6 +276,7 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall assert await async_subtensor.staking.add_stake( wallet=bob_wallet, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=tao, wait_for_inclusion=False, wait_for_finalization=False, diff --git a/tests/e2e_tests/test_liquid_alpha.py b/tests/e2e_tests/test_liquid_alpha.py index 3306deb072..e31a852933 100644 --- a/tests/e2e_tests/test_liquid_alpha.py +++ b/tests/e2e_tests/test_liquid_alpha.py @@ -60,6 +60,7 @@ def test_liquid_alpha(subtensor, alice_wallet): assert subtensor.staking.add_stake( wallet=alice_wallet, netuid=netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(10_000), ), "Unable to stake to Alice neuron" @@ -241,6 +242,7 @@ async def test_liquid_alpha_async(async_subtensor, alice_wallet): assert await async_subtensor.staking.add_stake( wallet=alice_wallet, netuid=netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(10_000), ), "Unable to stake to Alice neuron" diff --git a/tests/e2e_tests/test_liquidity.py b/tests/e2e_tests/test_liquidity.py index c99b23338d..2617a6259c 100644 --- a/tests/e2e_tests/test_liquidity.py +++ b/tests/e2e_tests/test_liquidity.py @@ -76,13 +76,11 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): assert message == "", "❌ Cannot enable user liquidity." # Add steak to call add_liquidity - assert subtensor.extrinsics.add_stake( + assert subtensor.staking.add_stake( wallet=alice_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, amount=Balance.from_tao(1), - wait_for_inclusion=True, - wait_for_finalization=True, ), "❌ Cannot cannot add stake to Alice from Alice." # wait for the next block to give the chain time to update the stake @@ -193,13 +191,11 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): ) # Add stake from Bob to Alice - assert subtensor.extrinsics.add_stake( + assert subtensor.staking.add_stake( wallet=bob_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, amount=Balance.from_tao(1000), - wait_for_inclusion=True, - wait_for_finalization=True, ), "❌ Cannot add stake from Bob to Alice." # wait for the next block to give the chain time to update the stake @@ -371,13 +367,11 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): assert message == "", "❌ Cannot enable user liquidity." # Add steak to call add_liquidity - assert await async_subtensor.extrinsics.add_stake( + assert await async_subtensor.staking.add_stake( wallet=alice_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, amount=Balance.from_tao(1), - wait_for_inclusion=True, - wait_for_finalization=True, ), "❌ Cannot cannot add stake to Alice from Alice." # wait for the next block to give the chain time to update the stake @@ -490,13 +484,11 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): ) # Add stake from Bob to Alice - assert await async_subtensor.extrinsics.add_stake( + assert await async_subtensor.staking.add_stake( wallet=bob_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, amount=Balance.from_tao(1000), - wait_for_inclusion=True, - wait_for_finalization=True, ), "❌ Cannot add stake from Bob to Alice." # wait for the next block to give the chain time to update the stake diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 570eefc460..d59b944162 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -143,9 +143,8 @@ def test_metagraph(subtensor, alice_wallet, bob_wallet, dave_wallet): assert subtensor.staking.add_stake( wallet=bob_wallet, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=tao, - wait_for_inclusion=True, - wait_for_finalization=True, ), "Failed to add stake for Bob" logging.console.info("Assert stake is added after updating metagraph") @@ -313,9 +312,8 @@ async def test_metagraph_async(async_subtensor, alice_wallet, bob_wallet, dave_w assert await async_subtensor.staking.add_stake( wallet=bob_wallet, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=tao, - wait_for_inclusion=True, - wait_for_finalization=True, ), "Failed to add stake for Bob" logging.console.info("Assert stake is added after updating metagraph") diff --git a/tests/e2e_tests/test_root_set_weights.py b/tests/e2e_tests/test_root_set_weights.py index 7ce08d9f47..7d18dccfcf 100644 --- a/tests/e2e_tests/test_root_set_weights.py +++ b/tests/e2e_tests/test_root_set_weights.py @@ -88,11 +88,9 @@ async def test_root_reg_hyperparams(subtensor, templates, alice_wallet, bob_wall assert subtensor.staking.add_stake( wallet=bob_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1), - wait_for_inclusion=True, - wait_for_finalization=True, period=16, ), "Unable to stake from Bob to Alice" @@ -213,11 +211,9 @@ async def test_root_reg_hyperparams_async( assert await async_subtensor.staking.add_stake( wallet=bob_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1), - wait_for_inclusion=True, - wait_for_finalization=True, period=16, ), "Unable to stake from Bob to Alice" diff --git a/tests/e2e_tests/test_set_weights.py b/tests/e2e_tests/test_set_weights.py index 4024a34e5d..ea754b68c8 100644 --- a/tests/e2e_tests/test_set_weights.py +++ b/tests/e2e_tests/test_set_weights.py @@ -83,11 +83,9 @@ def test_set_weights_uses_next_nonce(subtensor, alice_wallet): for netuid in netuids: assert subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(10_000), - wait_for_inclusion=True, - wait_for_finalization=True, ) # Set weight hyperparameters per subnet @@ -249,11 +247,9 @@ async def test_set_weights_uses_next_nonce_async(async_subtensor, alice_wallet): for netuid in netuids: assert await async_subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(10_000), - wait_for_inclusion=True, - wait_for_finalization=True, ) # Set weight hyperparameters per subnet diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 17cc94b51c..b940999825 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -59,11 +59,9 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): success = subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=Balance.from_tao(1), - wait_for_inclusion=True, - wait_for_finalization=True, period=16, ) @@ -239,11 +237,9 @@ async def test_single_operation_async(async_subtensor, alice_wallet, bob_wallet) success = await async_subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=Balance.from_tao(1), - wait_for_inclusion=True, - wait_for_finalization=True, period=16, ) @@ -725,11 +721,9 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) # 1. Strict params - should fail success = subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=stake_amount, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=False, @@ -749,11 +743,9 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) # 2. Partial allowed - should succeed partially success = subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=stake_amount, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=True, @@ -776,11 +768,9 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) amount = Balance.from_tao(100) success = subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=amount, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.22, # 22% allow_partial_stake=False, @@ -921,11 +911,9 @@ async def test_safe_staking_scenarios_async( # 1. Strict params - should fail success = await async_subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=stake_amount, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=False, @@ -945,11 +933,9 @@ async def test_safe_staking_scenarios_async( # 2. Partial allowed - should succeed partially success = await async_subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=stake_amount, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=True, @@ -972,11 +958,9 @@ async def test_safe_staking_scenarios_async( amount = Balance.from_tao(100) success = await async_subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=amount, - wait_for_inclusion=True, - wait_for_finalization=True, safe_staking=True, rate_tolerance=0.22, # 22% allow_partial_stake=False, @@ -1093,11 +1077,9 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): initial_stake_amount = Balance.from_tao(10_000) success = subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=origin_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=initial_stake_amount, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert success is True @@ -1209,11 +1191,9 @@ async def test_safe_swap_stake_scenarios_async( initial_stake_amount = Balance.from_tao(10_000) success = await async_subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=origin_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=initial_stake_amount, - wait_for_inclusion=True, - wait_for_finalization=True, ) assert success is True @@ -1301,11 +1281,9 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): assert subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1_000), - wait_for_inclusion=True, - wait_for_finalization=True, ) stakes = subtensor.staking.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) @@ -1399,11 +1377,9 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): assert subtensor.staking.add_stake( wallet=dave_wallet, - hotkey_ss58=dave_wallet.hotkey.ss58_address, netuid=bob_subnet_netuid, + hotkey_ss58=dave_wallet.hotkey.ss58_address, amount=Balance.from_tao(1000), - wait_for_inclusion=True, - wait_for_finalization=True, allow_partial_stake=True, ) @@ -1464,11 +1440,9 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ assert await async_subtensor.staking.add_stake( wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1_000), - wait_for_inclusion=True, - wait_for_finalization=True, ) stakes = await async_subtensor.staking.get_stake_for_coldkey( @@ -1571,8 +1545,6 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ hotkey_ss58=dave_wallet.hotkey.ss58_address, netuid=bob_subnet_netuid, amount=Balance.from_tao(1000), - wait_for_inclusion=True, - wait_for_finalization=True, allow_partial_stake=True, ) @@ -1635,12 +1607,10 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): ) assert subtensor.staking.add_stake( - alice_wallet, - alice_wallet.hotkey.ss58_address, + wallet=alice_wallet, netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1_000), - wait_for_inclusion=True, - wait_for_finalization=True, ) alice_stakes = subtensor.staking.get_stake_for_coldkey( @@ -1766,12 +1736,10 @@ async def test_transfer_stake_async( ) assert await async_subtensor.staking.add_stake( - alice_wallet, - alice_wallet.hotkey.ss58_address, + wallet=alice_wallet, netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1_000), - wait_for_inclusion=True, - wait_for_finalization=True, ) alice_stakes = await async_subtensor.staking.get_stake_for_coldkey( @@ -1941,8 +1909,6 @@ def test_unstaking_with_limit( hotkey_ss58=dave_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid_2, amount=Balance.from_tao(10000), - wait_for_inclusion=True, - wait_for_finalization=True, period=16, ), f"Cant add stake to dave in SN {alice_subnet_netuid_2}" assert subtensor.staking.add_stake( @@ -1950,8 +1916,6 @@ def test_unstaking_with_limit( hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid_3, amount=Balance.from_tao(15000), - wait_for_inclusion=True, - wait_for_finalization=True, period=16, ), f"Cant add stake to dave in SN {alice_subnet_netuid_3}" @@ -2061,8 +2025,6 @@ async def test_unstaking_with_limit_async( netuid=alice_subnet_netuid_2, hotkey_ss58=dave_wallet.hotkey.ss58_address, amount=Balance.from_tao(10000), - wait_for_inclusion=True, - wait_for_finalization=True, period=16, ), f"Cant add stake to dave in SN {alice_subnet_netuid_2}" assert await async_subtensor.staking.add_stake( @@ -2070,8 +2032,6 @@ async def test_unstaking_with_limit_async( netuid=alice_subnet_netuid_3, hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(15000), - wait_for_inclusion=True, - wait_for_finalization=True, period=16, ), f"Cant add stake to dave in SN {alice_subnet_netuid_3}" diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 9f847d8bf2..a8882e9897 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2717,7 +2717,8 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): """Test add_stake returns True on successful staking.""" # Prep fake_hotkey_ss58 = "fake_hotkey" - fake_amount = 10.0 + fake_amount = Balance.from_tao(10.0) + fake_netuid = 14 mock_add_stake_extrinsic = mocker.patch.object( subtensor_module, "add_stake_extrinsic" @@ -2726,6 +2727,7 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): # Call result = subtensor.add_stake( wallet=fake_wallet, + netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, amount=fake_amount, wait_for_inclusion=True, @@ -2740,8 +2742,8 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): subtensor=subtensor, wallet=fake_wallet, hotkey_ss58=fake_hotkey_ss58, - netuid=None, - amount=Balance.from_rao(fake_amount), + netuid=14, + amount=fake_amount.rao, wait_for_inclusion=True, wait_for_finalization=False, safe_staking=False, @@ -2756,8 +2758,9 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): """Test add_stake with safe staking parameters enabled.""" # Prep + fake_netuid = 14 fake_hotkey_ss58 = "fake_hotkey" - fake_amount = 10.0 + fake_amount = Balance.from_tao(10.0) fake_rate_tolerance = 0.01 # 1% threshold mock_add_stake_extrinsic = mocker.patch.object( @@ -2767,6 +2770,7 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): # Call result = subtensor.add_stake( wallet=fake_wallet, + netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, amount=fake_amount, wait_for_inclusion=True, @@ -2781,8 +2785,8 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): subtensor=subtensor, wallet=fake_wallet, hotkey_ss58=fake_hotkey_ss58, - netuid=None, - amount=Balance.from_rao(fake_amount), + netuid=14, + amount=fake_amount.rao, wait_for_inclusion=True, wait_for_finalization=False, safe_staking=True, From e00cbdb9f86184c26ec88553a1671e35172bd5d9 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 21:26:13 -0700 Subject: [PATCH 45/49] Changes in `.add_stake_multiple_extrinsic` and `subtensor.add_stake_multiple`: see migration.md --- bittensor/core/async_subtensor.py | 10 +- bittensor/core/extrinsics/asyncex/staking.py | 98 ++++++++------------ bittensor/core/extrinsics/staking.py | 96 ++++++++----------- bittensor/core/subtensor.py | 10 +- migration.md | 3 + tests/e2e_tests/test_staking.py | 6 +- 6 files changed, 93 insertions(+), 130 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index f0056d124a..14f91c1854 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4392,7 +4392,7 @@ async def add_stake_multiple( wallet: "Wallet", hotkey_ss58s: list[str], netuids: list[int], - amounts: Optional[list[Balance]] = None, + amounts: list[Balance], period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4402,11 +4402,11 @@ async def add_stake_multiple( Adds stakes to multiple neurons identified by their hotkey SS58 addresses. This bulk operation allows for efficient staking across different neurons from a single wallet. - Arguments: + Parameters: wallet: The wallet used for staking. hotkey_ss58s: List of ``SS58`` addresses of hotkeys to stake to. - netuids: list of subnet UIDs. - amounts: Corresponding amounts of TAO to stake for each hotkey. + netuids: List of subnet UIDs. + amounts: List of corresponding TAO amounts to bet for each netuid and hotkey. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4423,8 +4423,8 @@ async def add_stake_multiple( return await add_stake_multiple_extrinsic( subtensor=self, wallet=wallet, - hotkey_ss58s=hotkey_ss58s, netuids=netuids, + hotkey_ss58s=hotkey_ss58s, amounts=amounts, period=period, raise_error=raise_error, diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index c5295887e4..c06c58cd78 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -194,23 +194,22 @@ async def add_stake_multiple_extrinsic( wallet: "Wallet", hotkey_ss58s: list[str], netuids: list[int], - old_balance: Optional[Balance] = None, - amounts: Optional[list[Balance]] = None, + amounts: list[Balance], period: Optional[int] = None, - raise_error: bool = True, + raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ) -> bool: """ - Adds a stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. + Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey on subnet with + corresponding netuid. Parameters: - subtensor: AsyncSubtensor instance with the connection to the chain. + subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object for the coldkey. - old_balance: The balance of the wallet prior to staking. - hotkey_ss58s: List of hotkeys to stake to. netuids: List of netuids to stake to. - amounts: List of amounts to stake. If `None`, stake all to the first hotkey. + hotkey_ss58s: List of hotkeys to stake to. + amounts: List of corresponding TAO amounts to bet for each netuid and hotkey. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -221,35 +220,36 @@ async def add_stake_multiple_extrinsic( Returns: bool: True if the subnet registration was successful, False otherwise. """ - if not isinstance(hotkey_ss58s, list) or not all( - isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s - ): - raise TypeError("hotkey_ss58s must be a list of str") + # Decrypt keys, + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return False + + assert all( + [ + isinstance(netuids, list), + isinstance(hotkey_ss58s, list), + isinstance(amounts, list), + ] + ), "The `netuids`, `hotkey_ss58s` and `amounts` must be lists." if len(hotkey_ss58s) == 0: return True - 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") + assert len(netuids) == len(hotkey_ss58s) == len(amounts), ( + "The number of items in `netuids`, `hotkey_ss58s` and `amounts` must be the same." + ) - if len(netuids) != len(hotkey_ss58s): - raise ValueError("netuids must be a list of the same length as hotkey_ss58s") + if not all(isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s): + raise TypeError("hotkey_ss58s must be a list of str") - new_amounts: Sequence[Optional[Balance]] - if amounts is None: - new_amounts = [None] * len(hotkey_ss58s) - else: - new_amounts = [ - amount.set_unit(netuid=netuid) for amount, netuid in zip(amounts, netuids) - ] - if sum(amount.tao for amount in new_amounts) == 0: - # Staking 0 tao - return True + new_amounts: Sequence[Optional[Balance]] = [ + amount.set_unit(netuid) for amount, netuid in zip(amounts, netuids) + ] - # Decrypt keys, - if not (unlock := unlock_key(wallet)).success: - logging.error(unlock.message) - return False + if sum(amount.tao for amount in new_amounts) == 0: + # Staking 0 tao + return True logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" @@ -268,11 +268,9 @@ async def add_stake_multiple_extrinsic( total_staking_rao = sum( [amount.rao if amount is not None else 0 for amount in new_amounts] ) - if old_balance is None: - old_balance = await subtensor.get_balance( - wallet.coldkeypub.ss58_address, block_hash=block_hash - ) - initial_balance = old_balance + old_balance = initial_balance = await subtensor.get_balance( + wallet.coldkeypub.ss58_address, block_hash=block_hash + ) if total_staking_rao == 0: # Staking all to the first wallet. @@ -294,28 +292,17 @@ async def add_stake_multiple_extrinsic( for idx, (hotkey_ss58, amount, old_stake, netuid) in enumerate( zip(hotkey_ss58s, new_amounts, old_stakes, netuids) ): - staking_all = False - # Convert to bittensor.Balance - if amount is None: - # Stake it all. - staking_balance = Balance.from_tao(old_balance.tao) - staking_all = True - else: - # Amounts are cast to balance earlier in the function - assert isinstance(amount, Balance) - staking_balance = amount - # Check enough to stake - if staking_balance > old_balance: + if amount > old_balance: logging.error( f":cross_mark: [red]Not enough balance[/red]: [green]{old_balance}[/green] to stake: " - f"[blue]{staking_balance}[/blue] from wallet: [white]{wallet.name}[/white]" + f"[blue]{amount}[/blue] from wallet: [white]{wallet.name}[/white]" ) continue try: logging.info( - f"Staking [blue]{staking_balance}[/blue] to hotkey: [magenta]{hotkey_ss58}[/magenta] on netuid: " + f"Staking [blue]{amount}[/blue] to hotkey: [magenta]{hotkey_ss58}[/magenta] on netuid: " f"[blue]{netuid}[/blue]" ) call = await subtensor.substrate.compose_call( @@ -323,7 +310,7 @@ async def add_stake_multiple_extrinsic( call_function="add_stake", call_params={ "hotkey": hotkey_ss58, - "amount_staked": staking_balance.rao, + "amount_staked": amount.rao, "netuid": netuid, }, ) @@ -342,12 +329,8 @@ async def add_stake_multiple_extrinsic( # If we successfully staked. if success: if not wait_for_finalization and not wait_for_inclusion: - old_balance -= staking_balance + old_balance -= amount successful_stakes += 1 - if staking_all: - # If staked all, no need to continue - break - continue logging.success(":white_heavy_check_mark: [green]Finalized[/green]") @@ -373,10 +356,6 @@ async def add_stake_multiple_extrinsic( ) old_balance = new_balance successful_stakes += 1 - if staking_all: - # If staked all, no need to continue - break - else: logging.error(f":cross_mark: [red]Failed: {message}.[/red]") continue @@ -385,7 +364,6 @@ async def add_stake_multiple_extrinsic( logging.error( f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]" ) - continue if successful_stakes != 0: logging.info( diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 629d547144..eefe5ea65d 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -188,23 +188,22 @@ def add_stake_multiple_extrinsic( wallet: "Wallet", hotkey_ss58s: list[str], netuids: list[int], - old_balance: Optional[Balance] = None, - amounts: Optional[list[Balance]] = None, + amounts: list[Balance], period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, ) -> bool: """ - Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. + Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey on subnet with + corresponding netuid. Parameters: subtensor: Subtensor instance with the connection to the chain. wallet: Bittensor wallet object for the coldkey. - old_balance: The balance of the wallet prior to staking. - hotkey_ss58s: List of hotkeys to stake to. netuids: List of netuids to stake to. - amounts: List of amounts to stake. If `None`, stake all to the first hotkey. + hotkey_ss58s: List of hotkeys to stake to. + amounts: List of corresponding TAO amounts to bet for each netuid and hotkey. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -215,36 +214,36 @@ def add_stake_multiple_extrinsic( Returns: bool: True if the subnet registration was successful, False otherwise. """ - if not isinstance(hotkey_ss58s, list) or not all( - isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s - ): - raise TypeError("hotkey_ss58s must be a list of str") + # Decrypt keys, + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return False + + assert all( + [ + isinstance(netuids, list), + isinstance(hotkey_ss58s, list), + isinstance(amounts, list), + ] + ), "The `netuids`, `hotkey_ss58s` and `amounts` must be lists." if len(hotkey_ss58s) == 0: return True - 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") + assert len(netuids) == len(hotkey_ss58s) == len(amounts), ( + "The number of items in `netuids`, `hotkey_ss58s` and `amounts` must be the same." + ) - if netuids is not None and len(netuids) != len(hotkey_ss58s): - raise ValueError("netuids must be a list of the same length as hotkey_ss58s") + if not all(isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s): + raise TypeError("hotkey_ss58s must be a list of str") - new_amounts: Sequence[Optional[Balance]] + new_amounts: Sequence[Optional[Balance]] = [ + amount.set_unit(netuid) for amount, netuid in zip(amounts, netuids) + ] - if amounts is None: - new_amounts = [None] * len(hotkey_ss58s) - else: - new_amounts = [ - amount.set_unit(netuid) for amount, netuid in zip(amounts, netuids) - ] - if sum(amount.tao for amount in new_amounts) == 0: - # Staking 0 tao - return True - - # Decrypt keys, - if not (unlock := unlock_key(wallet)).success: - logging.error(unlock.message) - return False + if sum(amount.tao for amount in new_amounts) == 0: + # Staking 0 tao + return True logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" @@ -262,10 +261,9 @@ def add_stake_multiple_extrinsic( total_staking_rao = sum( [amount.rao if amount is not None else 0 for amount in new_amounts] ) - if old_balance is None: - old_balance = initial_balance = subtensor.get_balance( - wallet.coldkeypub.ss58_address, block=block - ) + old_balance = initial_balance = subtensor.get_balance( + address=wallet.coldkeypub.ss58_address, block=block + ) if total_staking_rao == 0: # Staking all to the first wallet. if old_balance.rao > 1000: @@ -286,32 +284,25 @@ def add_stake_multiple_extrinsic( for idx, (hotkey_ss58, amount, old_stake, netuid) in enumerate( zip(hotkey_ss58s, new_amounts, old_stakes, netuids) ): - staking_all = False - if amount is None: - # Stake it all. - staking_balance = Balance.from_tao(old_balance.tao) - staking_all = True - else: - staking_balance = amount - # Check enough to stake - if staking_balance > old_balance: + if amount > old_balance: logging.error( f":cross_mark: [red]Not enough balance[/red]: [green]{old_balance}[/green] to stake: " - f"[blue]{staking_balance}[/blue] from wallet: [white]{wallet.name}[/white]" + f"[blue]{amount}[/blue] from wallet: [white]{wallet.name}[/white]" ) continue try: logging.info( - f"Staking [blue]{staking_balance}[/blue] to [magenta]{hotkey_ss58}[/magenta] on netuid [blue]{netuid}[/blue]" + f"Staking [blue]{amount}[/blue] to hotkey: [magenta]{hotkey_ss58}[/magenta] on netuid: " + f"[blue]{netuid}[/blue]" ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", call_function="add_stake", call_params={ "hotkey": hotkey_ss58, - "amount_staked": staking_balance.rao, + "amount_staked": amount.rao, "netuid": netuid, }, ) @@ -327,16 +318,12 @@ def add_stake_multiple_extrinsic( raise_error=raise_error, ) - if success is True: # If we successfully staked. + # If we successfully staked. + if success: # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - old_balance -= staking_balance + old_balance -= amount successful_stakes += 1 - if staking_all: - # If staked all, no need to continue - break - continue logging.success(":white_heavy_check_mark: [green]Finalized[/green]") @@ -359,10 +346,6 @@ def add_stake_multiple_extrinsic( ) old_balance = new_balance successful_stakes += 1 - if staking_all: - # If staked all, no need to continue - break - else: logging.error(f":cross_mark: [red]Failed[/red]: {message}") continue @@ -371,7 +354,6 @@ def add_stake_multiple_extrinsic( logging.error( f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]" ) - continue if successful_stakes != 0: logging.info( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index ff8c1c27d5..435498038c 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3232,7 +3232,7 @@ def add_stake_multiple( wallet: "Wallet", hotkey_ss58s: list[str], netuids: list[int], - amounts: Optional[list[Balance]] = None, + amounts: list[Balance], period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -3242,11 +3242,11 @@ def add_stake_multiple( Adds stakes to multiple neurons identified by their hotkey SS58 addresses. This bulk operation allows for efficient staking across different neurons from a single wallet. - Arguments: + Parameters: wallet: The wallet used for staking. hotkey_ss58s: List of ``SS58`` addresses of hotkeys to stake to. - netuids: list of subnet UIDs. - amounts: Corresponding amounts of TAO to stake for each hotkey. + netuids: List of subnet UIDs. + amounts: List of corresponding TAO amounts to bet for each netuid and hotkey. period: The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3263,8 +3263,8 @@ def add_stake_multiple( return add_stake_multiple_extrinsic( subtensor=self, wallet=wallet, - hotkey_ss58s=hotkey_ss58s, netuids=netuids, + hotkey_ss58s=hotkey_ss58s, amounts=amounts, period=period, raise_error=raise_error, diff --git a/migration.md b/migration.md index 3d9a3d9f1f..4ff532c5a7 100644 --- a/migration.md +++ b/migration.md @@ -203,6 +203,9 @@ wait_for_finalization: bool = False, - parameter `hotkey_ss58` required (no Optional anymore) - parameter `amount` required (no Optional anymore) - [x] `.add_stake_multiple_extrinsic` and `subtensor.add_stake_multiple` + - Changes in `.add_stake_multiple_extrinsic` and `subtensor.add_stake_multiple`: + - parameter `old_balance` removed from async version + - parameter `amounts` required (no Optional anymore) - [x] `.start_call_extrinsic` and `subtensor.start_call` - [x] `.increase_take_extrinsic`, `.decrease_take_extrinsic` and `subtenor.set_reveal_commitment` - [x] `.transfer_extrinsic` and `subtensor.transfer` diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index b940999825..b3847bb507 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -428,7 +428,7 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): alice_balance = balances[alice_wallet.coldkey.ss58_address] success = subtensor.staking.add_stake_multiple( - alice_wallet, + wallet=alice_wallet, hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], netuids=netuids, amounts=[Balance.from_tao(10_000) for _ in netuids], @@ -438,8 +438,8 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): stakes = [ subtensor.staking.get_stake( - alice_wallet.coldkey.ss58_address, - bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=netuid, ) for netuid in netuids From 37f49e6dbb65d30603f66f8740153da91191bf6a Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 21:58:45 -0700 Subject: [PATCH 46/49] limit `max-parallel` to 16 * 4 (py versions in reusable workflow) --- .github/workflows/e2e-subtensor-tests.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index dd819957f4..261c8bda3c 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -138,6 +138,7 @@ jobs: - pull-docker-image strategy: fail-fast: false + max-parallel: 16 matrix: include: ${{ fromJson(needs.find-tests.outputs.test-files) }} uses: ./.github/workflows/_run-e2e-single.yaml From 2afa67d2c60bb940660d7d30f23eb509e5fc2e86 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 23:13:28 -0700 Subject: [PATCH 47/49] `amount: Optional[Balance] = None` in `move_stake_extrinsic` bbc of logic required --- bittensor/core/extrinsics/asyncex/move_stake.py | 2 +- bittensor/core/extrinsics/move_stake.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index 0934a3ce9d..a401350251 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -308,7 +308,7 @@ async def move_stake_extrinsic( origin_netuid: int, destination_hotkey_ss58: str, destination_netuid: int, - amount: Balance, + amount: Optional[Balance] = None, move_all_stake: bool = False, period: Optional[int] = None, raise_error: bool = False, diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index bf7878083a..b3a9d54e95 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -304,7 +304,7 @@ def move_stake_extrinsic( origin_netuid: int, destination_hotkey_ss58: str, destination_netuid: int, - amount: Balance, + amount: Optional[Balance] = None, move_all_stake: bool = False, period: Optional[int] = None, raise_error: bool = False, From 70db4498c51e7f0ab849e212f2aee9325d7bb936 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 23:14:32 -0700 Subject: [PATCH 48/49] improved `unstake_multiple_extrinsic` logic --- .../core/extrinsics/asyncex/unstaking.py | 53 +++++++++++-------- bittensor/core/extrinsics/unstaking.py | 53 +++++++++++-------- migration.md | 2 + 3 files changed, 65 insertions(+), 43 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 7a0e828e34..bda16a0835 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -286,29 +286,19 @@ async def unstake_multiple_extrinsic( Returns: bool: True if the subnet registration was successful, False otherwise. """ + # Unlock coldkey. + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return False + + # or amounts or unstake_all (no both) if amounts and unstake_all: raise ValueError("Cannot specify both `amounts` and `unstake_all`.") - if not isinstance(hotkey_ss58s, list) or not all( - isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s - ): - raise TypeError("hotkey_ss58s must be a list of str") - - if len(hotkey_ss58s) == 0: - return True - - 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): - raise ValueError("netuids must be a list of the same length as hotkey_ss58s") - if amounts is not None and not all( - isinstance(amount, (Balance, float)) for amount in amounts + isinstance(amount, Balance) for amount in amounts ): - raise TypeError( - "amounts must be a [list of bittensor.Balance or float] or None" - ) + raise TypeError("amounts must be a [list of bittensor.Balance] or None") if amounts is None: amounts = [None] * len(hotkey_ss58s) @@ -319,10 +309,29 @@ async def unstake_multiple_extrinsic( # Staking 0 tao return True - # Unlock coldkey. - if not (unlock := unlock_key(wallet)).success: - logging.error(unlock.message) - return False + assert all( + [ + isinstance(netuids, list), + isinstance(hotkey_ss58s, list), + isinstance(amounts, list), + ] + ), "The `netuids`, `hotkey_ss58s` and `amounts` must be lists." + + if len(hotkey_ss58s) == 0: + return True + + assert len(netuids) == len(hotkey_ss58s) == len(amounts), ( + "The number of items in `netuids`, `hotkey_ss58s` and `amounts` must be the same." + ) + + if not all(isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s): + raise TypeError("hotkey_ss58s must be a list of str") + + 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): + raise ValueError("netuids must be a list of the same length as hotkey_ss58s") logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index 5aaf162ecc..d9539a8268 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -287,23 +287,15 @@ def unstake_multiple_extrinsic( Returns: bool: True if the subnet registration was successful, False otherwise. """ + # Unlock coldkey. + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return False + + # or amounts or unstake_all (no both) if amounts and unstake_all: raise ValueError("Cannot specify both `amounts` and `unstake_all`.") - if not isinstance(hotkey_ss58s, list) or not all( - isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s - ): - raise TypeError("hotkey_ss58s must be a list of str") - - if len(hotkey_ss58s) == 0: - return True - - if netuids is not None and len(netuids) != len(hotkey_ss58s): - raise ValueError("netuids must be a list of the same length as hotkey_ss58s") - - 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 amounts is not None and not all( isinstance(amount, Balance) for amount in amounts ): @@ -318,10 +310,29 @@ def unstake_multiple_extrinsic( # Staking 0 tao return True - # Unlock coldkey. - if not (unlock := unlock_key(wallet)).success: - logging.error(unlock.message) - return False + assert all( + [ + isinstance(netuids, list), + isinstance(hotkey_ss58s, list), + isinstance(amounts, list), + ] + ), "The `netuids`, `hotkey_ss58s` and `amounts` must be lists." + + if len(hotkey_ss58s) == 0: + return True + + assert len(netuids) == len(hotkey_ss58s) == len(amounts), ( + "The number of items in `netuids`, `hotkey_ss58s` and `amounts` must be the same." + ) + + if not all(isinstance(hotkey_ss58, str) for hotkey_ss58 in hotkey_ss58s): + raise TypeError("hotkey_ss58s must be a list of str") + + 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): + raise ValueError("netuids must be a list of the same length as hotkey_ss58s") logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" @@ -377,7 +388,7 @@ def unstake_multiple_extrinsic( f"[blue]{netuid}[/blue] for fee [blue]{fee}[/blue]" ) - staking_response, err_msg = subtensor.sign_and_send_extrinsic( + success, message = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -389,7 +400,7 @@ def unstake_multiple_extrinsic( raise_error=raise_error, ) - if staking_response: # If we successfully unstaked. + if success: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: @@ -413,7 +424,7 @@ def unstake_multiple_extrinsic( ) successful_unstakes += 1 else: - logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") continue except SubstrateRequestException as error: diff --git a/migration.md b/migration.md index 4ff532c5a7..cc06b7b66f 100644 --- a/migration.md +++ b/migration.md @@ -218,6 +218,8 @@ wait_for_finalization: bool = False, - parameter `unstake_all: bool` removed (use `unstake_all_extrinsic` for unstake all stake) - [x] `.unstake_all_extrinsic` and `subtensor.unstake_all` - [x] `.unstake_multiple_extrinsic` and `subtensor.unstake_multiple` + - Changes in `.unstake_multiple_extrinsic` and `subtensor.unstake_multiple`: + - parameter `amounts` is now required (no Optional anymore) - [x] `.commit_weights_extrinsic` and `subtensor.commit_weights` - [x] `.reveal_weights_extrinsic` and `subtensor.reveal_weights` - [x] `.set_weights_extrinsic` and `subtensor.set_weights` \ No newline at end of file From d01b2a9d3fe66568d34edfa788c9d81ad6ab28c3 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 4 Sep 2025 23:21:41 -0700 Subject: [PATCH 49/49] updated migration.md --- migration.md | 1 + 1 file changed, 1 insertion(+) diff --git a/migration.md b/migration.md index cc06b7b66f..3e4b0a7a1a 100644 --- a/migration.md +++ b/migration.md @@ -108,6 +108,7 @@ rename this variable in documentation. 12. Find and process all `TODOs` across the entire code base. If in doubt, discuss each one with the team separately. SDK has 29 TODOs. 13. ✅ The SDK is dropping support for `Python 3.9` starting with this release. 14. Remove `Default is` and `Default to` in docstrings bc parameters enough. +15. camfairchild: TODO, but we should have a grab_metadata if we don't already. Maybe don't decode, but can have a call that removes the Raw prefix, and another just doing grab_metadata_raw (no decoding) ## New features 1. Add `bittensor.utils.hex_to_ss58` function. SDK still doesn't have it. (Probably inner import `from scalecodec import ss58_encode, ss58_decode`)