From bcd53322cab7b0db28567765d0f6f69e64c58eff Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 15:55:18 -0700 Subject: [PATCH 01/41] method `get_stake_for_coldkey` removed, bc this is the same as `get_stake_info_for_coldkey` --- MIGRATION.md | 5 +-- bittensor/core/async_subtensor.py | 4 +-- bittensor/core/extrinsics/asyncex/staking.py | 2 +- .../core/extrinsics/asyncex/unstaking.py | 2 +- bittensor/core/extrinsics/staking.py | 2 +- bittensor/core/extrinsics/unstaking.py | 2 +- bittensor/core/subtensor.py | 4 +-- bittensor/core/subtensor_api/staking.py | 1 - bittensor/core/subtensor_api/utils.py | 1 - bittensor/core/subtensor_api/wallets.py | 1 - tests/e2e_tests/test_staking.py | 36 ++++++++----------- tests/unit_tests/extrinsics/test_staking.py | 2 +- 12 files changed, 24 insertions(+), 38 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 5065daceaf..d73a6377da 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -27,9 +27,9 @@ 1. In the synchronous Subtensor class, the `get_owned_hotkeys` method includes a `reuse_block` parameter that is inconsistent with other methods. Either remove this parameter from `get_owned_hotkeys`, or add it to all other methods that directly call self.substrate.* to maintain a consistent interface. 2. In all methods where we `get_stake_operations_fee` is called, remove unused arguments. Consider combining all methods using `get_stake_operations_fee` into one common one. 3. Delete deprecated `get_current_weight_commit_info` and `get_current_weight_commit_info_v2`. Rename `get_timelocked_weight_commits` to get_current_weight_commit_info. -4. Remove references like `get_stake_info_for_coldkey = get_stake_for_coldkey`. +4. ✅ Remove references like `get_stake_info_for_coldkey = get_stake_for_coldkey`. 5. Reconsider some methods naming across the entire subtensor module. -6. Add `hotkey_ss58` parameter to `get_liquidity_list` method. One wallet can have many HKs. Currently, the mentioned method uses default HK only. +6. ~~Add `hotkey_ss58` parameter to `get_liquidity_list` method. One wallet can have many HKs. Currently, the mentioned method uses default HK only.~~ wrong idea ## Metagraph 1. Remove verbose archival node warnings for blocks older than 300. Some users complained about many messages for them. @@ -234,6 +234,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - method `query_map_subtensor` has updated parameters order. - method `query_map` has updated parameters order. - method `add_stake_multiple` has updated parameters order. +- method `get_stake_for_coldkey` removed, bc this is the same as `get_stake_info_for_coldkey` ### Mechid related changes: In the next subtensor methods got updated the parameters order: diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 5c572c7d10..94e8b1f6a0 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -2829,7 +2829,7 @@ async def get_stake_for_coldkey_and_hotkey( for (netuid, result) in zip(all_netuids, results) } - async def get_stake_for_coldkey( + async def get_stake_info_for_coldkey( self, coldkey_ss58: str, block: Optional[int] = None, @@ -2863,8 +2863,6 @@ async def get_stake_for_coldkey( stakes: list[StakeInfo] = StakeInfo.list_from_dicts(result) return [stake for stake in stakes if stake.stake > 0] - get_stake_info_for_coldkey = get_stake_for_coldkey - async def get_stake_for_hotkey( self, hotkey_ss58: str, diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index 9219c903c0..a6dc7208eb 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -269,7 +269,7 @@ async def add_stake_multiple_extrinsic( block_hash = await subtensor.substrate.get_chain_head() - all_stakes = await subtensor.get_stake_for_coldkey( + all_stakes = await subtensor.get_stake_info_for_coldkey( coldkey_ss58=wallet.coldkeypub.ss58_address, block_hash=block_hash ) old_stakes: list[Balance] = get_old_stakes( diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index f6f5fce8bd..d3acf3f9bc 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -352,7 +352,7 @@ async def unstake_multiple_extrinsic( block_hash = await subtensor.substrate.get_chain_head() all_stakes, old_balance = await asyncio.gather( - subtensor.get_stake_for_coldkey( + subtensor.get_stake_info_for_coldkey( coldkey_ss58=wallet.coldkeypub.ss58_address, block_hash=block_hash ), subtensor.get_balance( diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 949166a994..190a3a7ed5 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -262,7 +262,7 @@ def add_stake_multiple_extrinsic( return ExtrinsicResponse(True, "Success") block = subtensor.get_current_block() - all_stakes = subtensor.get_stake_for_coldkey( + all_stakes = subtensor.get_stake_info_for_coldkey( coldkey_ss58=wallet.coldkeypub.ss58_address, ) old_stakes: list[Balance] = get_old_stakes( diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index 6e2d2bdb27..45bda590d0 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -349,7 +349,7 @@ def unstake_multiple_extrinsic( old_balance = subtensor.get_balance( address=wallet.coldkeypub.ss58_address, block=block ) - all_stakes = subtensor.get_stake_for_coldkey( + all_stakes = subtensor.get_stake_info_for_coldkey( coldkey_ss58=wallet.coldkeypub.ss58_address, block=block, ) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index ee70580390..7d39891c80 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2041,7 +2041,7 @@ def get_stake_for_coldkey_and_hotkey( for (netuid, result) in zip(all_netuids, results) } - def get_stake_for_coldkey( + def get_stake_info_for_coldkey( self, coldkey_ss58: str, block: Optional[int] = None ) -> list["StakeInfo"]: """ @@ -2066,8 +2066,6 @@ def get_stake_for_coldkey( stakes: list[StakeInfo] = StakeInfo.list_from_dicts(result) return [stake for stake in stakes if stake.stake > 0] - get_stake_info_for_coldkey = get_stake_for_coldkey - def get_stake_for_hotkey( self, hotkey_ss58: str, netuid: int, block: Optional[int] = None ) -> Balance: diff --git a/bittensor/core/subtensor_api/staking.py b/bittensor/core/subtensor_api/staking.py index a85d88250e..d84ecc1357 100644 --- a/bittensor/core/subtensor_api/staking.py +++ b/bittensor/core/subtensor_api/staking.py @@ -13,7 +13,6 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_minimum_required_stake = subtensor.get_minimum_required_stake self.get_stake = subtensor.get_stake self.get_stake_add_fee = subtensor.get_stake_add_fee - self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey self.get_stake_for_coldkey_and_hotkey = ( subtensor.get_stake_for_coldkey_and_hotkey ) diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/subtensor_api/utils.py index 212cdcebcc..d883f41689 100644 --- a/bittensor/core/subtensor_api/utils.py +++ b/bittensor/core/subtensor_api/utils.py @@ -74,7 +74,6 @@ def add_legacy_methods(subtensor: "SubtensorApi"): ) subtensor.get_stake = subtensor._subtensor.get_stake subtensor.get_stake_add_fee = subtensor._subtensor.get_stake_add_fee - subtensor.get_stake_for_coldkey = subtensor._subtensor.get_stake_for_coldkey subtensor.get_stake_for_coldkey_and_hotkey = ( subtensor._subtensor.get_stake_for_coldkey_and_hotkey ) diff --git a/bittensor/core/subtensor_api/wallets.py b/bittensor/core/subtensor_api/wallets.py index 9b3a3a058b..3ad195ae45 100644 --- a/bittensor/core/subtensor_api/wallets.py +++ b/bittensor/core/subtensor_api/wallets.py @@ -30,7 +30,6 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_parents = subtensor.get_parents self.get_stake = subtensor.get_stake self.get_stake_add_fee = subtensor.get_stake_add_fee - self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey self.get_stake_for_coldkey_and_hotkey = ( subtensor.get_stake_for_coldkey_and_hotkey ) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index cd739fa461..270642cc6c 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -80,7 +80,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): logging.console.info(f"Bob stake: {stake_bob}") assert stake_bob > Balance(0).set_unit(alice_subnet_netuid) - stakes = subtensor.staking.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) + stakes = subtensor.staking.get_stake_info_for_coldkey(alice_wallet.coldkey.ss58_address) expected_stakes = [ StakeInfo( @@ -117,10 +117,6 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): expected_stakes += fast_blocks_stake assert stakes == expected_stakes - assert ( - subtensor.staking.get_stake_for_coldkey - == subtensor.staking.get_stake_info_for_coldkey - ) stakes = subtensor.staking.get_stake_for_coldkey_and_hotkey( alice_wallet.coldkey.ss58_address, @@ -259,7 +255,7 @@ async def test_single_operation_async(async_subtensor, alice_wallet, bob_wallet) logging.console.info(f"Bob stake: {stake_bob}") assert stake_bob > Balance(0).set_unit(alice_subnet_netuid) - stakes = await async_subtensor.staking.get_stake_for_coldkey( + stakes = await async_subtensor.staking.get_stake_info_for_coldkey( alice_wallet.coldkey.ss58_address ) @@ -298,10 +294,6 @@ async def test_single_operation_async(async_subtensor, alice_wallet, bob_wallet) expected_stakes += fast_blocks_stake assert stakes == expected_stakes - assert ( - async_subtensor.staking.get_stake_for_coldkey - == async_subtensor.staking.get_stake_info_for_coldkey - ) stakes = await async_subtensor.staking.get_stake_for_coldkey_and_hotkey( alice_wallet.coldkey.ss58_address, @@ -1287,7 +1279,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): amount=Balance.from_tao(1_000), ).success - stakes = subtensor.staking.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) + stakes = subtensor.staking.get_stake_info_for_coldkey(alice_wallet.coldkey.ss58_address) assert stakes == [ StakeInfo( @@ -1332,7 +1324,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): ) assert response.success is True - stakes = subtensor.staking.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) + stakes = subtensor.staking.get_stake_info_for_coldkey(alice_wallet.coldkey.ss58_address) expected_stakes = [ StakeInfo( @@ -1446,7 +1438,7 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ ) ).success - stakes = await async_subtensor.staking.get_stake_for_coldkey( + stakes = await async_subtensor.staking.get_stake_info_for_coldkey( alice_wallet.coldkey.ss58_address ) @@ -1499,7 +1491,7 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ ) assert response.success is True - stakes = await async_subtensor.staking.get_stake_for_coldkey( + stakes = await async_subtensor.staking.get_stake_info_for_coldkey( alice_wallet.coldkey.ss58_address ) @@ -1618,7 +1610,7 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): amount=Balance.from_tao(1_000), ).success - alice_stakes = subtensor.staking.get_stake_for_coldkey( + alice_stakes = subtensor.staking.get_stake_info_for_coldkey( alice_wallet.coldkey.ss58_address ) @@ -1637,7 +1629,7 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): ), ] - bob_stakes = subtensor.staking.get_stake_for_coldkey( + bob_stakes = subtensor.staking.get_stake_info_for_coldkey( bob_wallet.coldkey.ss58_address ) @@ -1665,7 +1657,7 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): ) assert response.success is True - alice_stakes = subtensor.staking.get_stake_for_coldkey( + alice_stakes = subtensor.staking.get_stake_info_for_coldkey( alice_wallet.coldkey.ss58_address ) @@ -1692,7 +1684,7 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): assert alice_stakes == expected_alice_stake - bob_stakes = subtensor.staking.get_stake_for_coldkey( + bob_stakes = subtensor.staking.get_stake_info_for_coldkey( bob_wallet.coldkey.ss58_address ) @@ -1749,7 +1741,7 @@ async def test_transfer_stake_async( ) ).success - alice_stakes = await async_subtensor.staking.get_stake_for_coldkey( + alice_stakes = await async_subtensor.staking.get_stake_info_for_coldkey( alice_wallet.coldkey.ss58_address ) @@ -1768,7 +1760,7 @@ async def test_transfer_stake_async( ), ] - bob_stakes = await async_subtensor.staking.get_stake_for_coldkey( + bob_stakes = await async_subtensor.staking.get_stake_info_for_coldkey( bob_wallet.coldkey.ss58_address ) @@ -1800,7 +1792,7 @@ async def test_transfer_stake_async( ) assert response.success is True - alice_stakes = await async_subtensor.staking.get_stake_for_coldkey( + alice_stakes = await async_subtensor.staking.get_stake_info_for_coldkey( alice_wallet.coldkey.ss58_address ) @@ -1827,7 +1819,7 @@ async def test_transfer_stake_async( assert alice_stakes == expected_alice_stake - bob_stakes = await async_subtensor.staking.get_stake_for_coldkey( + bob_stakes = await async_subtensor.staking.get_stake_info_for_coldkey( bob_wallet.coldkey.ss58_address ) diff --git a/tests/unit_tests/extrinsics/test_staking.py b/tests/unit_tests/extrinsics/test_staking.py index 0240fdf560..9fb40bf26d 100644 --- a/tests/unit_tests/extrinsics/test_staking.py +++ b/tests/unit_tests/extrinsics/test_staking.py @@ -60,7 +60,7 @@ def test_add_stake_multiple_extrinsic(subtensor, mocker, fake_wallet): """Verify that sync `add_stake_multiple_extrinsic` method calls proper async method.""" # Preps mocked_get_stake_for_coldkey = mocker.patch.object( - subtensor, "get_stake_for_coldkey", return_value=[Balance(1.1), Balance(0.3)] + subtensor, "get_stake_info_for_coldkey", return_value=[Balance(1.1), Balance(0.3)] ) mocked_get_balance = mocker.patch.object( subtensor, "get_balance", return_value=Balance.from_tao(10) From f45df6c47d91733563cc9543f1da4ff5ffb93b29 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 16:10:27 -0700 Subject: [PATCH 02/41] `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` --- MIGRATION.md | 4 ++++ bittensor/core/addons/__init__.py | 10 ++++++++++ bittensor/core/{ => addons}/subtensor_api/__init__.py | 0 bittensor/core/{ => addons}/subtensor_api/chain.py | 0 .../core/{ => addons}/subtensor_api/commitments.py | 0 bittensor/core/{ => addons}/subtensor_api/delegates.py | 0 .../core/{ => addons}/subtensor_api/extrinsics.py | 0 .../core/{ => addons}/subtensor_api/metagraphs.py | 0 bittensor/core/{ => addons}/subtensor_api/neurons.py | 0 bittensor/core/{ => addons}/subtensor_api/queries.py | 0 bittensor/core/{ => addons}/subtensor_api/staking.py | 0 bittensor/core/{ => addons}/subtensor_api/subnets.py | 0 bittensor/core/{ => addons}/subtensor_api/utils.py | 0 bittensor/core/{ => addons}/subtensor_api/wallets.py | 0 bittensor/utils/easy_imports.py | 3 +-- tests/unit_tests/test_subtensor_api.py | 5 +++-- 16 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 bittensor/core/addons/__init__.py rename bittensor/core/{ => addons}/subtensor_api/__init__.py (100%) rename bittensor/core/{ => addons}/subtensor_api/chain.py (100%) rename bittensor/core/{ => addons}/subtensor_api/commitments.py (100%) rename bittensor/core/{ => addons}/subtensor_api/delegates.py (100%) rename bittensor/core/{ => addons}/subtensor_api/extrinsics.py (100%) rename bittensor/core/{ => addons}/subtensor_api/metagraphs.py (100%) rename bittensor/core/{ => addons}/subtensor_api/neurons.py (100%) rename bittensor/core/{ => addons}/subtensor_api/queries.py (100%) rename bittensor/core/{ => addons}/subtensor_api/staking.py (100%) rename bittensor/core/{ => addons}/subtensor_api/subnets.py (100%) rename bittensor/core/{ => addons}/subtensor_api/utils.py (100%) rename bittensor/core/{ => addons}/subtensor_api/wallets.py (100%) diff --git a/MIGRATION.md b/MIGRATION.md index d73a6377da..a4a3d454c1 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -236,6 +236,10 @@ Removing deprecated extrinsics and replacing them with consistent ones: - method `add_stake_multiple` has updated parameters order. - method `get_stake_for_coldkey` removed, bc this is the same as `get_stake_info_for_coldkey` +Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. + - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` + - + ### Mechid related changes: In the next subtensor methods got updated the parameters order: - `bonds` diff --git a/bittensor/core/addons/__init__.py b/bittensor/core/addons/__init__.py new file mode 100644 index 0000000000..daa1ce0ff0 --- /dev/null +++ b/bittensor/core/addons/__init__.py @@ -0,0 +1,10 @@ +""" +The `addons` sub-package contains optional extensions and logic augmentations for the core functionality of the project. + +Modules placed in this package may include experimental features, alternative implementations, developer tools, or +enhancements that extend or customize core behavior. These components are not always critical for the main application, +but can be enabled or imported as needed for advanced use cases, internal tooling, or feature expansion. + +Use this package to keep optional, modular, or feature-gated logic separate from the primary codebase while maintaining +discoverability and structure. +""" diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/addons/subtensor_api/__init__.py similarity index 100% rename from bittensor/core/subtensor_api/__init__.py rename to bittensor/core/addons/subtensor_api/__init__.py diff --git a/bittensor/core/subtensor_api/chain.py b/bittensor/core/addons/subtensor_api/chain.py similarity index 100% rename from bittensor/core/subtensor_api/chain.py rename to bittensor/core/addons/subtensor_api/chain.py diff --git a/bittensor/core/subtensor_api/commitments.py b/bittensor/core/addons/subtensor_api/commitments.py similarity index 100% rename from bittensor/core/subtensor_api/commitments.py rename to bittensor/core/addons/subtensor_api/commitments.py diff --git a/bittensor/core/subtensor_api/delegates.py b/bittensor/core/addons/subtensor_api/delegates.py similarity index 100% rename from bittensor/core/subtensor_api/delegates.py rename to bittensor/core/addons/subtensor_api/delegates.py diff --git a/bittensor/core/subtensor_api/extrinsics.py b/bittensor/core/addons/subtensor_api/extrinsics.py similarity index 100% rename from bittensor/core/subtensor_api/extrinsics.py rename to bittensor/core/addons/subtensor_api/extrinsics.py diff --git a/bittensor/core/subtensor_api/metagraphs.py b/bittensor/core/addons/subtensor_api/metagraphs.py similarity index 100% rename from bittensor/core/subtensor_api/metagraphs.py rename to bittensor/core/addons/subtensor_api/metagraphs.py diff --git a/bittensor/core/subtensor_api/neurons.py b/bittensor/core/addons/subtensor_api/neurons.py similarity index 100% rename from bittensor/core/subtensor_api/neurons.py rename to bittensor/core/addons/subtensor_api/neurons.py diff --git a/bittensor/core/subtensor_api/queries.py b/bittensor/core/addons/subtensor_api/queries.py similarity index 100% rename from bittensor/core/subtensor_api/queries.py rename to bittensor/core/addons/subtensor_api/queries.py diff --git a/bittensor/core/subtensor_api/staking.py b/bittensor/core/addons/subtensor_api/staking.py similarity index 100% rename from bittensor/core/subtensor_api/staking.py rename to bittensor/core/addons/subtensor_api/staking.py diff --git a/bittensor/core/subtensor_api/subnets.py b/bittensor/core/addons/subtensor_api/subnets.py similarity index 100% rename from bittensor/core/subtensor_api/subnets.py rename to bittensor/core/addons/subtensor_api/subnets.py diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/addons/subtensor_api/utils.py similarity index 100% rename from bittensor/core/subtensor_api/utils.py rename to bittensor/core/addons/subtensor_api/utils.py diff --git a/bittensor/core/subtensor_api/wallets.py b/bittensor/core/addons/subtensor_api/wallets.py similarity index 100% rename from bittensor/core/subtensor_api/wallets.py rename to bittensor/core/addons/subtensor_api/wallets.py diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index de3399aaa5..31bd503b56 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -28,6 +28,7 @@ from bittensor_wallet.wallet import Wallet from bittensor.core import settings, timelock +from bittensor.core.addons.subtensor_api import SubtensorApi from bittensor.core.async_subtensor import AsyncSubtensor from bittensor.core.axon import Axon from bittensor.core.chain_data import ( @@ -99,7 +100,6 @@ from bittensor.core.settings import BLOCKTIME from bittensor.core.stream import StreamingSynapse from bittensor.core.subtensor import Subtensor -from bittensor.core.subtensor_api import SubtensorApi from bittensor.core.synapse import TerminalInfo, Synapse from bittensor.core.tensor import Tensor from bittensor.core.threadpool import PriorityThreadPoolExecutor @@ -119,7 +119,6 @@ from bittensor.utils.mock.subtensor_mock import MockSubtensor from bittensor.utils.subnets import SubnetsAPI - # Backwards compatibility with previous bittensor versions. async_subtensor = AsyncSubtensor axon = Axon diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index 5007370d96..785aab0926 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -1,7 +1,8 @@ -from bittensor.core.subtensor import Subtensor -from bittensor.core.subtensor_api import SubtensorApi import pytest +from bittensor.core.addons.subtensor_api import SubtensorApi +from bittensor.core.subtensor import Subtensor + def test_properties_methods_comparable(other_class: "Subtensor" = None): """Verifies that methods in SubtensorApi and its properties contains all Subtensors methods.""" From 3dfccfcb070ea9f7cc7d00428e28cadf91dc7d2c Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 16:16:57 -0700 Subject: [PATCH 03/41] fix imports + move timelock impl to addons --- bittensor/core/addons/__init__.py | 9 +++++++++ bittensor/core/addons/subtensor_api/utils.py | 2 +- bittensor/core/{ => addons}/timelock.py | 0 bittensor/utils/easy_imports.py | 4 ++-- tests/e2e_tests/conftest.py | 2 +- tests/e2e_tests/test_transfer.py | 2 +- tests/e2e_tests/utils/chain_interactions.py | 2 +- tests/e2e_tests/utils/e2e_test_utils.py | 2 +- tests/integration_tests/test_timelock.py | 2 +- 9 files changed, 17 insertions(+), 8 deletions(-) rename bittensor/core/{ => addons}/timelock.py (100%) diff --git a/bittensor/core/addons/__init__.py b/bittensor/core/addons/__init__.py index daa1ce0ff0..3e3dd14b8f 100644 --- a/bittensor/core/addons/__init__.py +++ b/bittensor/core/addons/__init__.py @@ -8,3 +8,12 @@ Use this package to keep optional, modular, or feature-gated logic separate from the primary codebase while maintaining discoverability and structure. """ + +from bittensor.core.addons.subtensor_api import SubtensorApi +from bittensor.core.addons import timelock + + +__all__ = [ + "timelock", + "SubtensorApi", +] diff --git a/bittensor/core/addons/subtensor_api/utils.py b/bittensor/core/addons/subtensor_api/utils.py index d883f41689..63a26b0ced 100644 --- a/bittensor/core/addons/subtensor_api/utils.py +++ b/bittensor/core/addons/subtensor_api/utils.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from bittensor.core.subtensor_api import SubtensorApi + from bittensor.core.addons import SubtensorApi def add_legacy_methods(subtensor: "SubtensorApi"): diff --git a/bittensor/core/timelock.py b/bittensor/core/addons/timelock.py similarity index 100% rename from bittensor/core/timelock.py rename to bittensor/core/addons/timelock.py diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 31bd503b56..e84a84d786 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -27,8 +27,8 @@ ) from bittensor_wallet.wallet import Wallet -from bittensor.core import settings, timelock -from bittensor.core.addons.subtensor_api import SubtensorApi +from bittensor.core import settings +from bittensor.core.addons import timelock, SubtensorApi from bittensor.core.async_subtensor import AsyncSubtensor from bittensor.core.axon import Axon from bittensor.core.chain_data import ( diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 9fa8dfe8d4..dffcc510f6 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -14,7 +14,7 @@ import pytest_asyncio from async_substrate_interface import SubstrateInterface -from bittensor.core.subtensor_api import SubtensorApi +from bittensor.core.addons import SubtensorApi from bittensor.utils.btlogging import logging from tests.e2e_tests.utils.e2e_test_utils import ( Templates, diff --git a/tests/e2e_tests/test_transfer.py b/tests/e2e_tests/test_transfer.py index 4f65a5828d..d372f4d33d 100644 --- a/tests/e2e_tests/test_transfer.py +++ b/tests/e2e_tests/test_transfer.py @@ -7,7 +7,7 @@ from bittensor import logging if typing.TYPE_CHECKING: - from bittensor.core.subtensor_api import SubtensorApi + from bittensor.core.addons import SubtensorApi def test_transfer(subtensor, alice_wallet): diff --git a/tests/e2e_tests/utils/chain_interactions.py b/tests/e2e_tests/utils/chain_interactions.py index 43af7a1532..8fc0ecfe53 100644 --- a/tests/e2e_tests/utils/chain_interactions.py +++ b/tests/e2e_tests/utils/chain_interactions.py @@ -15,7 +15,7 @@ # for typing purposes if TYPE_CHECKING: from bittensor import Wallet - from bittensor.core.subtensor_api import SubtensorApi + from bittensor.core.addons import SubtensorApi from async_substrate_interface import ( AsyncSubstrateInterface, AsyncExtrinsicReceipt, diff --git a/tests/e2e_tests/utils/e2e_test_utils.py b/tests/e2e_tests/utils/e2e_test_utils.py index 3ad0b896c9..e0a19ada7e 100644 --- a/tests/e2e_tests/utils/e2e_test_utils.py +++ b/tests/e2e_tests/utils/e2e_test_utils.py @@ -6,7 +6,7 @@ from bittensor_wallet import Keypair, Wallet -from bittensor.core.subtensor_api import SubtensorApi +from bittensor.core.addons import SubtensorApi from bittensor.utils.btlogging import logging template_path = os.getcwd() + "/neurons/" diff --git a/tests/integration_tests/test_timelock.py b/tests/integration_tests/test_timelock.py index 33e31db782..b0545e3517 100644 --- a/tests/integration_tests/test_timelock.py +++ b/tests/integration_tests/test_timelock.py @@ -3,7 +3,7 @@ import pytest -from bittensor.core import timelock +from bittensor.core.addons import timelock def test_encrypt_returns_valid_tuple(): From 4782af63551485dbfbfe056ae1b2c4afbbba8662 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 16:19:57 -0700 Subject: [PATCH 04/41] more --- MIGRATION.md | 2 +- bittensor/core/addons/__init__.py | 3 +-- tests/e2e_tests/conftest.py | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index a4a3d454c1..aa5ea513b0 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -238,7 +238,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` - - + - `bittensor.core.timelock` moved to `bittensor.core.addons.timelock` ### Mechid related changes: In the next subtensor methods got updated the parameters order: diff --git a/bittensor/core/addons/__init__.py b/bittensor/core/addons/__init__.py index 3e3dd14b8f..b84bb3ae90 100644 --- a/bittensor/core/addons/__init__.py +++ b/bittensor/core/addons/__init__.py @@ -9,9 +9,8 @@ discoverability and structure. """ -from bittensor.core.addons.subtensor_api import SubtensorApi from bittensor.core.addons import timelock - +from bittensor.core.addons.subtensor_api import SubtensorApi __all__ = [ "timelock", diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index dffcc510f6..319b9d646a 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -1,4 +1,3 @@ -import asyncio import os import re import shlex @@ -12,7 +11,6 @@ import pytest import pytest_asyncio -from async_substrate_interface import SubstrateInterface from bittensor.core.addons import SubtensorApi from bittensor.utils.btlogging import logging From 9a99f3b2c9b707b562d3d786ed041c6bbf381b4d Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 16:24:57 -0700 Subject: [PATCH 05/41] rename `SubtensorApi._subtensor` -> `SubtensorApi.inner_subtensor` --- .../core/addons/subtensor_api/__init__.py | 50 +-- bittensor/core/addons/subtensor_api/utils.py | 304 ++++++++++-------- tests/e2e_tests/test_metagraph.py | 12 +- 3 files changed, 194 insertions(+), 172 deletions(-) diff --git a/bittensor/core/addons/subtensor_api/__init__.py b/bittensor/core/addons/subtensor_api/__init__.py index 63bfd137c3..f729998c9e 100644 --- a/bittensor/core/addons/subtensor_api/__init__.py +++ b/bittensor/core/addons/subtensor_api/__init__.py @@ -94,24 +94,24 @@ def __init__( # assigned only for async instance self.initialize = None - self._subtensor = self._get_subtensor() + self.inner_subtensor = self._get_subtensor() # fix naming collision - self._neurons = _Neurons(self._subtensor) + self._neurons = _Neurons(self.inner_subtensor) # define empty fields - self.substrate = self._subtensor.substrate - self.chain_endpoint = self._subtensor.chain_endpoint - self.close = self._subtensor.close - self.config = self._subtensor.config - self.setup_config = self._subtensor.setup_config - self.help = self._subtensor.help - - self.determine_block_hash = self._subtensor.determine_block_hash - self.encode_params = self._subtensor.encode_params - self.sign_and_send_extrinsic = self._subtensor.sign_and_send_extrinsic - self.start_call = self._subtensor.start_call - self.wait_for_block = self._subtensor.wait_for_block + self.substrate = self.inner_subtensor.substrate + self.chain_endpoint = self.inner_subtensor.chain_endpoint + self.close = self.inner_subtensor.close + self.config = self.inner_subtensor.config + self.setup_config = self.inner_subtensor.setup_config + self.help = self.inner_subtensor.help + + self.determine_block_hash = self.inner_subtensor.determine_block_hash + self.encode_params = self.inner_subtensor.encode_params + self.sign_and_send_extrinsic = self.inner_subtensor.sign_and_send_extrinsic + self.start_call = self.inner_subtensor.start_call + self.wait_for_block = self.inner_subtensor.wait_for_block # adds all Subtensor methods into main level os SubtensorApi class if legacy_methods: @@ -178,7 +178,7 @@ async def __aenter__(self): raise NotImplementedError( "Sync version of SubtensorApi cannot be used with async context manager." ) - await self._subtensor.__aenter__() + await self.inner_subtensor.__aenter__() return self async def __aexit__(self, exc_type, exc_val, exc_tb): @@ -195,32 +195,32 @@ def add_args(cls, parser): @property def block(self): """Returns current chain block number.""" - return self._subtensor.block + return self.inner_subtensor.block @property def chain(self): """Property of interaction with chain methods.""" - return _Chain(self._subtensor) + return _Chain(self.inner_subtensor) @property def commitments(self): """Property to access commitments methods.""" - return _Commitments(self._subtensor) + return _Commitments(self.inner_subtensor) @property def delegates(self): """Property to access delegates methods.""" - return _Delegates(self._subtensor) + return _Delegates(self.inner_subtensor) @property def extrinsics(self): """Property to access extrinsics methods.""" - return _Extrinsics(self._subtensor) + return _Extrinsics(self.inner_subtensor) @property def metagraphs(self): """Property to access metagraphs methods.""" - return _Metagraphs(self._subtensor) + return _Metagraphs(self.inner_subtensor) @property def neurons(self): @@ -235,19 +235,19 @@ def neurons(self, value): @property def queries(self): """Property to access subtensor queries methods.""" - return _Queries(self._subtensor) + return _Queries(self.inner_subtensor) @property def staking(self): """Property to access staking methods.""" - return _Staking(self._subtensor) + return _Staking(self.inner_subtensor) @property def subnets(self): """Property of interaction with subnets methods.""" - return _Subnets(self._subtensor) + return _Subnets(self.inner_subtensor) @property def wallets(self): """Property of interaction methods with cold/hotkeys, and balances, etc.""" - return _Wallets(self._subtensor) + return _Wallets(self.inner_subtensor) diff --git a/bittensor/core/addons/subtensor_api/utils.py b/bittensor/core/addons/subtensor_api/utils.py index 63a26b0ced..19e45857e5 100644 --- a/bittensor/core/addons/subtensor_api/utils.py +++ b/bittensor/core/addons/subtensor_api/utils.py @@ -6,175 +6,197 @@ def add_legacy_methods(subtensor: "SubtensorApi"): """If SubtensorApi get `subtensor_fields=True` arguments, then all classic Subtensor fields added to root level.""" - subtensor.add_liquidity = subtensor._subtensor.add_liquidity - subtensor.add_stake = subtensor._subtensor.add_stake - subtensor.add_stake_multiple = subtensor._subtensor.add_stake_multiple - subtensor.all_subnets = subtensor._subtensor.all_subnets - subtensor.blocks_since_last_step = subtensor._subtensor.blocks_since_last_step - subtensor.blocks_since_last_update = subtensor._subtensor.blocks_since_last_update - subtensor.bonds = subtensor._subtensor.bonds - subtensor.burned_register = subtensor._subtensor.burned_register - subtensor.chain_endpoint = subtensor._subtensor.chain_endpoint - 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 - subtensor.difficulty = subtensor._subtensor.difficulty - subtensor.does_hotkey_exist = subtensor._subtensor.does_hotkey_exist - subtensor.encode_params = subtensor._subtensor.encode_params + subtensor.add_liquidity = subtensor.inner_subtensor.add_liquidity + subtensor.add_stake = subtensor.inner_subtensor.add_stake + subtensor.add_stake_multiple = subtensor.inner_subtensor.add_stake_multiple + subtensor.all_subnets = subtensor.inner_subtensor.all_subnets + subtensor.blocks_since_last_step = subtensor.inner_subtensor.blocks_since_last_step + subtensor.blocks_since_last_update = ( + subtensor.inner_subtensor.blocks_since_last_update + ) + subtensor.bonds = subtensor.inner_subtensor.bonds + subtensor.burned_register = subtensor.inner_subtensor.burned_register + subtensor.chain_endpoint = subtensor.inner_subtensor.chain_endpoint + subtensor.commit_reveal_enabled = subtensor.inner_subtensor.commit_reveal_enabled + subtensor.commit_weights = subtensor.inner_subtensor.commit_weights + subtensor.determine_block_hash = subtensor.inner_subtensor.determine_block_hash + subtensor.difficulty = subtensor.inner_subtensor.difficulty + subtensor.does_hotkey_exist = subtensor.inner_subtensor.does_hotkey_exist + subtensor.encode_params = subtensor.inner_subtensor.encode_params subtensor.filter_netuids_by_registered_hotkeys = ( - subtensor._subtensor.filter_netuids_by_registered_hotkeys + subtensor.inner_subtensor.filter_netuids_by_registered_hotkeys + ) + subtensor.get_admin_freeze_window = ( + subtensor.inner_subtensor.get_admin_freeze_window + ) + subtensor.get_all_commitments = subtensor.inner_subtensor.get_all_commitments + subtensor.get_all_metagraphs_info = ( + subtensor.inner_subtensor.get_all_metagraphs_info ) - subtensor.get_admin_freeze_window = subtensor._subtensor.get_admin_freeze_window - subtensor.get_all_commitments = subtensor._subtensor.get_all_commitments - subtensor.get_all_metagraphs_info = subtensor._subtensor.get_all_metagraphs_info subtensor.get_all_neuron_certificates = ( - subtensor._subtensor.get_all_neuron_certificates + subtensor.inner_subtensor.get_all_neuron_certificates ) subtensor.get_all_revealed_commitments = ( - subtensor._subtensor.get_all_revealed_commitments - ) - subtensor.get_all_subnets_info = subtensor._subtensor.get_all_subnets_info - subtensor.get_balance = subtensor._subtensor.get_balance - subtensor.get_balances = subtensor._subtensor.get_balances - subtensor.get_block_hash = subtensor._subtensor.get_block_hash - subtensor.get_parents = subtensor._subtensor.get_parents - subtensor.get_children = subtensor._subtensor.get_children - subtensor.get_children_pending = subtensor._subtensor.get_children_pending - subtensor.get_commitment = subtensor._subtensor.get_commitment - subtensor.get_current_block = subtensor._subtensor.get_current_block + subtensor.inner_subtensor.get_all_revealed_commitments + ) + subtensor.get_all_subnets_info = subtensor.inner_subtensor.get_all_subnets_info + subtensor.get_balance = subtensor.inner_subtensor.get_balance + subtensor.get_balances = subtensor.inner_subtensor.get_balances + subtensor.get_block_hash = subtensor.inner_subtensor.get_block_hash + subtensor.get_parents = subtensor.inner_subtensor.get_parents + subtensor.get_children = subtensor.inner_subtensor.get_children + subtensor.get_children_pending = subtensor.inner_subtensor.get_children_pending + subtensor.get_commitment = subtensor.inner_subtensor.get_commitment + subtensor.get_current_block = subtensor.inner_subtensor.get_current_block subtensor.get_last_commitment_bonds_reset_block = ( - subtensor._subtensor.get_last_commitment_bonds_reset_block - ) - subtensor.get_delegate_by_hotkey = subtensor._subtensor.get_delegate_by_hotkey - subtensor.get_delegate_identities = subtensor._subtensor.get_delegate_identities - subtensor.get_delegate_take = subtensor._subtensor.get_delegate_take - subtensor.get_delegated = subtensor._subtensor.get_delegated - subtensor.get_delegates = subtensor._subtensor.get_delegates - subtensor.get_existential_deposit = subtensor._subtensor.get_existential_deposit - subtensor.get_hotkey_owner = subtensor._subtensor.get_hotkey_owner - subtensor.get_hotkey_stake = subtensor._subtensor.get_hotkey_stake - subtensor.get_hyperparameter = subtensor._subtensor.get_hyperparameter - subtensor.get_liquidity_list = subtensor._subtensor.get_liquidity_list - subtensor.get_metagraph_info = subtensor._subtensor.get_metagraph_info + subtensor.inner_subtensor.get_last_commitment_bonds_reset_block + ) + subtensor.get_delegate_by_hotkey = subtensor.inner_subtensor.get_delegate_by_hotkey + subtensor.get_delegate_identities = ( + subtensor.inner_subtensor.get_delegate_identities + ) + subtensor.get_delegate_take = subtensor.inner_subtensor.get_delegate_take + subtensor.get_delegated = subtensor.inner_subtensor.get_delegated + subtensor.get_delegates = subtensor.inner_subtensor.get_delegates + subtensor.get_existential_deposit = ( + subtensor.inner_subtensor.get_existential_deposit + ) + subtensor.get_hotkey_owner = subtensor.inner_subtensor.get_hotkey_owner + subtensor.get_hotkey_stake = subtensor.inner_subtensor.get_hotkey_stake + subtensor.get_hyperparameter = subtensor.inner_subtensor.get_hyperparameter + subtensor.get_liquidity_list = subtensor.inner_subtensor.get_liquidity_list + subtensor.get_metagraph_info = subtensor.inner_subtensor.get_metagraph_info subtensor.get_minimum_required_stake = ( - subtensor._subtensor.get_minimum_required_stake + subtensor.inner_subtensor.get_minimum_required_stake ) - subtensor.get_netuids_for_hotkey = subtensor._subtensor.get_netuids_for_hotkey - subtensor.get_neuron_certificate = subtensor._subtensor.get_neuron_certificate + subtensor.get_netuids_for_hotkey = subtensor.inner_subtensor.get_netuids_for_hotkey + subtensor.get_neuron_certificate = subtensor.inner_subtensor.get_neuron_certificate subtensor.get_neuron_for_pubkey_and_subnet = ( - subtensor._subtensor.get_neuron_for_pubkey_and_subnet + subtensor.inner_subtensor.get_neuron_for_pubkey_and_subnet ) subtensor.get_next_epoch_start_block = ( - subtensor._subtensor.get_next_epoch_start_block + subtensor.inner_subtensor.get_next_epoch_start_block + ) + subtensor.get_owned_hotkeys = subtensor.inner_subtensor.get_owned_hotkeys + subtensor.get_revealed_commitment = ( + subtensor.inner_subtensor.get_revealed_commitment ) - subtensor.get_owned_hotkeys = subtensor._subtensor.get_owned_hotkeys - subtensor.get_revealed_commitment = subtensor._subtensor.get_revealed_commitment subtensor.get_revealed_commitment_by_hotkey = ( - subtensor._subtensor.get_revealed_commitment_by_hotkey + subtensor.inner_subtensor.get_revealed_commitment_by_hotkey ) - subtensor.get_stake = subtensor._subtensor.get_stake - subtensor.get_stake_add_fee = subtensor._subtensor.get_stake_add_fee + subtensor.get_stake = subtensor.inner_subtensor.get_stake + subtensor.get_stake_add_fee = subtensor.inner_subtensor.get_stake_add_fee subtensor.get_stake_for_coldkey_and_hotkey = ( - subtensor._subtensor.get_stake_for_coldkey_and_hotkey + subtensor.inner_subtensor.get_stake_for_coldkey_and_hotkey ) - subtensor.get_stake_for_hotkey = subtensor._subtensor.get_stake_for_hotkey + subtensor.get_stake_for_hotkey = subtensor.inner_subtensor.get_stake_for_hotkey subtensor.get_stake_info_for_coldkey = ( - subtensor._subtensor.get_stake_info_for_coldkey + subtensor.inner_subtensor.get_stake_info_for_coldkey + ) + subtensor.get_stake_movement_fee = subtensor.inner_subtensor.get_stake_movement_fee + subtensor.get_stake_operations_fee = ( + subtensor.inner_subtensor.get_stake_operations_fee ) - subtensor.get_stake_movement_fee = subtensor._subtensor.get_stake_movement_fee - subtensor.get_stake_operations_fee = subtensor._subtensor.get_stake_operations_fee - subtensor.get_stake_weight = subtensor._subtensor.get_stake_weight + subtensor.get_stake_weight = subtensor.inner_subtensor.get_stake_weight subtensor.get_mechanism_emission_split = ( - subtensor._subtensor.get_mechanism_emission_split + subtensor.inner_subtensor.get_mechanism_emission_split ) - subtensor.get_mechanism_count = subtensor._subtensor.get_mechanism_count - subtensor.get_subnet_burn_cost = subtensor._subtensor.get_subnet_burn_cost + subtensor.get_mechanism_count = subtensor.inner_subtensor.get_mechanism_count + subtensor.get_subnet_burn_cost = subtensor.inner_subtensor.get_subnet_burn_cost subtensor.get_subnet_hyperparameters = ( - subtensor._subtensor.get_subnet_hyperparameters + subtensor.inner_subtensor.get_subnet_hyperparameters + ) + subtensor.get_subnet_info = subtensor.inner_subtensor.get_subnet_info + subtensor.get_subnet_price = subtensor.inner_subtensor.get_subnet_price + subtensor.get_subnet_prices = subtensor.inner_subtensor.get_subnet_prices + subtensor.get_subnet_owner_hotkey = ( + subtensor.inner_subtensor.get_subnet_owner_hotkey ) - subtensor.get_subnet_info = subtensor._subtensor.get_subnet_info - subtensor.get_subnet_price = subtensor._subtensor.get_subnet_price - subtensor.get_subnet_prices = subtensor._subtensor.get_subnet_prices - subtensor.get_subnet_owner_hotkey = subtensor._subtensor.get_subnet_owner_hotkey subtensor.get_subnet_reveal_period_epochs = ( - subtensor._subtensor.get_subnet_reveal_period_epochs + subtensor.inner_subtensor.get_subnet_reveal_period_epochs ) subtensor.get_subnet_validator_permits = ( - subtensor._subtensor.get_subnet_validator_permits + subtensor.inner_subtensor.get_subnet_validator_permits ) - subtensor.get_subnets = subtensor._subtensor.get_subnets + subtensor.get_subnets = subtensor.inner_subtensor.get_subnets subtensor.get_timelocked_weight_commits = ( - subtensor._subtensor.get_timelocked_weight_commits + subtensor.inner_subtensor.get_timelocked_weight_commits ) - subtensor.get_timestamp = subtensor._subtensor.get_timestamp - subtensor.get_total_subnets = subtensor._subtensor.get_total_subnets - subtensor.get_transfer_fee = subtensor._subtensor.get_transfer_fee + subtensor.get_timestamp = subtensor.inner_subtensor.get_timestamp + subtensor.get_total_subnets = subtensor.inner_subtensor.get_total_subnets + subtensor.get_transfer_fee = subtensor.inner_subtensor.get_transfer_fee subtensor.get_uid_for_hotkey_on_subnet = ( - subtensor._subtensor.get_uid_for_hotkey_on_subnet - ) - subtensor.get_unstake_fee = subtensor._subtensor.get_unstake_fee - subtensor.get_vote_data = subtensor._subtensor.get_vote_data - subtensor.immunity_period = subtensor._subtensor.immunity_period - subtensor.is_fast_blocks = subtensor._subtensor.is_fast_blocks - subtensor.is_hotkey_delegate = subtensor._subtensor.is_hotkey_delegate - subtensor.is_hotkey_registered = subtensor._subtensor.is_hotkey_registered - subtensor.is_hotkey_registered_any = subtensor._subtensor.is_hotkey_registered_any + subtensor.inner_subtensor.get_uid_for_hotkey_on_subnet + ) + subtensor.get_unstake_fee = subtensor.inner_subtensor.get_unstake_fee + subtensor.get_vote_data = subtensor.inner_subtensor.get_vote_data + subtensor.immunity_period = subtensor.inner_subtensor.immunity_period + subtensor.is_fast_blocks = subtensor.inner_subtensor.is_fast_blocks + subtensor.is_hotkey_delegate = subtensor.inner_subtensor.is_hotkey_delegate + subtensor.is_hotkey_registered = subtensor.inner_subtensor.is_hotkey_registered + subtensor.is_hotkey_registered_any = ( + subtensor.inner_subtensor.is_hotkey_registered_any + ) subtensor.is_hotkey_registered_on_subnet = ( - subtensor._subtensor.is_hotkey_registered_on_subnet - ) - subtensor.is_in_admin_freeze_window = subtensor._subtensor.is_in_admin_freeze_window - subtensor.is_subnet_active = subtensor._subtensor.is_subnet_active - subtensor.last_drand_round = subtensor._subtensor.last_drand_round - subtensor.log_verbose = subtensor._subtensor.log_verbose - subtensor.max_weight_limit = subtensor._subtensor.max_weight_limit - subtensor.metagraph = subtensor._subtensor.metagraph - subtensor.min_allowed_weights = subtensor._subtensor.min_allowed_weights - subtensor.modify_liquidity = subtensor._subtensor.modify_liquidity - subtensor.move_stake = subtensor._subtensor.move_stake - subtensor.network = subtensor._subtensor.network - subtensor.neurons = subtensor._subtensor.neurons - subtensor.neuron_for_uid = subtensor._subtensor.neuron_for_uid - subtensor.neurons_lite = subtensor._subtensor.neurons_lite - subtensor.query_constant = subtensor._subtensor.query_constant - subtensor.query_identity = subtensor._subtensor.query_identity - subtensor.query_map = subtensor._subtensor.query_map - subtensor.query_map_subtensor = subtensor._subtensor.query_map_subtensor - subtensor.query_module = subtensor._subtensor.query_module - subtensor.query_runtime_api = subtensor._subtensor.query_runtime_api - subtensor.query_subtensor = subtensor._subtensor.query_subtensor - subtensor.recycle = subtensor._subtensor.recycle - subtensor.remove_liquidity = subtensor._subtensor.remove_liquidity - subtensor.register = subtensor._subtensor.register - subtensor.register_subnet = subtensor._subtensor.register_subnet - subtensor.reveal_weights = subtensor._subtensor.reveal_weights - subtensor.root_register = subtensor._subtensor.root_register + subtensor.inner_subtensor.is_hotkey_registered_on_subnet + ) + subtensor.is_in_admin_freeze_window = ( + subtensor.inner_subtensor.is_in_admin_freeze_window + ) + subtensor.is_subnet_active = subtensor.inner_subtensor.is_subnet_active + subtensor.last_drand_round = subtensor.inner_subtensor.last_drand_round + subtensor.log_verbose = subtensor.inner_subtensor.log_verbose + subtensor.max_weight_limit = subtensor.inner_subtensor.max_weight_limit + subtensor.metagraph = subtensor.inner_subtensor.metagraph + subtensor.min_allowed_weights = subtensor.inner_subtensor.min_allowed_weights + subtensor.modify_liquidity = subtensor.inner_subtensor.modify_liquidity + subtensor.move_stake = subtensor.inner_subtensor.move_stake + subtensor.network = subtensor.inner_subtensor.network + subtensor.neurons = subtensor.inner_subtensor.neurons + subtensor.neuron_for_uid = subtensor.inner_subtensor.neuron_for_uid + subtensor.neurons_lite = subtensor.inner_subtensor.neurons_lite + subtensor.query_constant = subtensor.inner_subtensor.query_constant + subtensor.query_identity = subtensor.inner_subtensor.query_identity + subtensor.query_map = subtensor.inner_subtensor.query_map + subtensor.query_map_subtensor = subtensor.inner_subtensor.query_map_subtensor + subtensor.query_module = subtensor.inner_subtensor.query_module + subtensor.query_runtime_api = subtensor.inner_subtensor.query_runtime_api + subtensor.query_subtensor = subtensor.inner_subtensor.query_subtensor + subtensor.recycle = subtensor.inner_subtensor.recycle + subtensor.remove_liquidity = subtensor.inner_subtensor.remove_liquidity + subtensor.register = subtensor.inner_subtensor.register + subtensor.register_subnet = subtensor.inner_subtensor.register_subnet + subtensor.reveal_weights = subtensor.inner_subtensor.reveal_weights + subtensor.root_register = subtensor.inner_subtensor.root_register subtensor.root_set_pending_childkey_cooldown = ( - subtensor._subtensor.root_set_pending_childkey_cooldown - ) - subtensor.serve_axon = subtensor._subtensor.serve_axon - subtensor.set_children = subtensor._subtensor.set_children - subtensor.set_commitment = subtensor._subtensor.set_commitment - subtensor.set_delegate_take = subtensor._subtensor.set_delegate_take - subtensor.set_reveal_commitment = subtensor._subtensor.set_reveal_commitment - subtensor.set_subnet_identity = subtensor._subtensor.set_subnet_identity - subtensor.set_weights = subtensor._subtensor.set_weights - subtensor.setup_config = subtensor._subtensor.setup_config - subtensor.sign_and_send_extrinsic = subtensor._subtensor.sign_and_send_extrinsic - subtensor.start_call = subtensor._subtensor.start_call - subtensor.state_call = subtensor._subtensor.state_call - subtensor.subnet = subtensor._subtensor.subnet - subtensor.subnet_exists = subtensor._subtensor.subnet_exists - subtensor.subnetwork_n = subtensor._subtensor.subnetwork_n - subtensor.substrate = subtensor._subtensor.substrate - subtensor.swap_stake = subtensor._subtensor.swap_stake - subtensor.tempo = subtensor._subtensor.tempo - subtensor.toggle_user_liquidity = subtensor._subtensor.toggle_user_liquidity - subtensor.transfer = subtensor._subtensor.transfer - subtensor.transfer_stake = subtensor._subtensor.transfer_stake - subtensor.tx_rate_limit = subtensor._subtensor.tx_rate_limit - subtensor.unstake = subtensor._subtensor.unstake - subtensor.unstake_all = subtensor._subtensor.unstake_all - subtensor.unstake_multiple = subtensor._subtensor.unstake_multiple - subtensor.wait_for_block = subtensor._subtensor.wait_for_block - subtensor.weights = subtensor._subtensor.weights - subtensor.weights_rate_limit = subtensor._subtensor.weights_rate_limit + subtensor.inner_subtensor.root_set_pending_childkey_cooldown + ) + subtensor.serve_axon = subtensor.inner_subtensor.serve_axon + subtensor.set_children = subtensor.inner_subtensor.set_children + subtensor.set_commitment = subtensor.inner_subtensor.set_commitment + subtensor.set_delegate_take = subtensor.inner_subtensor.set_delegate_take + subtensor.set_reveal_commitment = subtensor.inner_subtensor.set_reveal_commitment + subtensor.set_subnet_identity = subtensor.inner_subtensor.set_subnet_identity + subtensor.set_weights = subtensor.inner_subtensor.set_weights + subtensor.setup_config = subtensor.inner_subtensor.setup_config + subtensor.sign_and_send_extrinsic = ( + subtensor.inner_subtensor.sign_and_send_extrinsic + ) + subtensor.start_call = subtensor.inner_subtensor.start_call + subtensor.state_call = subtensor.inner_subtensor.state_call + subtensor.subnet = subtensor.inner_subtensor.subnet + subtensor.subnet_exists = subtensor.inner_subtensor.subnet_exists + subtensor.subnetwork_n = subtensor.inner_subtensor.subnetwork_n + subtensor.substrate = subtensor.inner_subtensor.substrate + subtensor.swap_stake = subtensor.inner_subtensor.swap_stake + subtensor.tempo = subtensor.inner_subtensor.tempo + subtensor.toggle_user_liquidity = subtensor.inner_subtensor.toggle_user_liquidity + subtensor.transfer = subtensor.inner_subtensor.transfer + subtensor.transfer_stake = subtensor.inner_subtensor.transfer_stake + subtensor.tx_rate_limit = subtensor.inner_subtensor.tx_rate_limit + subtensor.unstake = subtensor.inner_subtensor.unstake + subtensor.unstake_all = subtensor.inner_subtensor.unstake_all + subtensor.unstake_multiple = subtensor.inner_subtensor.unstake_multiple + subtensor.wait_for_block = subtensor.inner_subtensor.wait_for_block + subtensor.weights = subtensor.inner_subtensor.weights + subtensor.weights_rate_limit = subtensor.inner_subtensor.weights_rate_limit diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 9553fda968..404c7d91f9 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -79,7 +79,7 @@ def test_metagraph(subtensor, alice_wallet, bob_wallet, dave_wallet): ) logging.console.info("Refresh the metagraph") - metagraph.sync(subtensor=subtensor._subtensor) + metagraph.sync(subtensor=subtensor.inner_subtensor) logging.console.info("Assert metagraph has Alice and Bob neurons") assert len(metagraph.uids) == 2, "Metagraph doesn't have exactly 2 neurons" @@ -118,7 +118,7 @@ def test_metagraph(subtensor, alice_wallet, bob_wallet, dave_wallet): dave_wallet, alice_subnet_netuid ).success, "Unable to register Dave as a neuron" - metagraph.sync(subtensor=subtensor._subtensor) + metagraph.sync(subtensor=subtensor.inner_subtensor) logging.console.info("Assert metagraph now includes Dave's neuron") assert len(metagraph.uids) == 3, ( @@ -147,7 +147,7 @@ def test_metagraph(subtensor, alice_wallet, bob_wallet, dave_wallet): ).success, "Failed to add stake for Bob" logging.console.info("Assert stake is added after updating metagraph") - metagraph.sync(subtensor=subtensor._subtensor) + metagraph.sync(subtensor=subtensor.inner_subtensor) assert 0.95 < metagraph.neurons[1].stake.rao / alpha.rao < 1.05, ( "Bob's stake not updated in metagraph" ) @@ -242,7 +242,7 @@ async def test_metagraph_async(async_subtensor, alice_wallet, bob_wallet, dave_w ).success, "Unable to register Bob as a neuron" logging.console.info("Refresh the metagraph") - await metagraph.sync(subtensor=async_subtensor._subtensor) + await metagraph.sync(subtensor=async_subtensor.inner_subtensor) logging.console.info("Assert metagraph has Alice and Bob neurons") assert len(metagraph.uids) == 2, "Metagraph doesn't have exactly 2 neurons" @@ -287,7 +287,7 @@ async def test_metagraph_async(async_subtensor, alice_wallet, bob_wallet, dave_w ) ).success, "Unable to register Dave as a neuron" - await metagraph.sync(subtensor=async_subtensor._subtensor) + await metagraph.sync(subtensor=async_subtensor.inner_subtensor) logging.console.info("Assert metagraph now includes Dave's neuron") assert len(metagraph.uids) == 3, ( @@ -320,7 +320,7 @@ async def test_metagraph_async(async_subtensor, alice_wallet, bob_wallet, dave_w ).success, "Failed to add stake for Bob" logging.console.info("Assert stake is added after updating metagraph") - await metagraph.sync(subtensor=async_subtensor._subtensor) + await metagraph.sync(subtensor=async_subtensor.inner_subtensor) assert 0.95 < metagraph.neurons[1].stake.rao / alpha.rao < 1.05, ( "Bob's stake not updated in metagraph" ) From 19add29293ffaf6bddbbc05dba77c87934e46bb4 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 16:28:03 -0700 Subject: [PATCH 06/41] updated order --- bittensor/core/addons/subtensor_api/chain.py | 4 +- bittensor/core/addons/subtensor_api/utils.py | 66 +++++++------------- 2 files changed, 24 insertions(+), 46 deletions(-) diff --git a/bittensor/core/addons/subtensor_api/chain.py b/bittensor/core/addons/subtensor_api/chain.py index 9f4c312f13..8a45169169 100644 --- a/bittensor/core/addons/subtensor_api/chain.py +++ b/bittensor/core/addons/subtensor_api/chain.py @@ -13,10 +13,10 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_delegate_identities = subtensor.get_delegate_identities self.get_existential_deposit = subtensor.get_existential_deposit self.get_minimum_required_stake = subtensor.get_minimum_required_stake - self.get_vote_data = subtensor.get_vote_data self.get_timestamp = subtensor.get_timestamp - self.is_in_admin_freeze_window = subtensor.is_in_admin_freeze_window + self.get_vote_data = subtensor.get_vote_data self.is_fast_blocks = subtensor.is_fast_blocks + self.is_in_admin_freeze_window = subtensor.is_in_admin_freeze_window self.last_drand_round = subtensor.last_drand_round self.state_call = subtensor.state_call self.tx_rate_limit = subtensor.tx_rate_limit diff --git a/bittensor/core/addons/subtensor_api/utils.py b/bittensor/core/addons/subtensor_api/utils.py index 19e45857e5..a070458493 100644 --- a/bittensor/core/addons/subtensor_api/utils.py +++ b/bittensor/core/addons/subtensor_api/utils.py @@ -11,9 +11,7 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.add_stake_multiple = subtensor.inner_subtensor.add_stake_multiple subtensor.all_subnets = subtensor.inner_subtensor.all_subnets subtensor.blocks_since_last_step = subtensor.inner_subtensor.blocks_since_last_step - subtensor.blocks_since_last_update = ( - subtensor.inner_subtensor.blocks_since_last_update - ) + subtensor.blocks_since_last_update = subtensor.inner_subtensor.blocks_since_last_update subtensor.bonds = subtensor.inner_subtensor.bonds subtensor.burned_register = subtensor.inner_subtensor.burned_register subtensor.chain_endpoint = subtensor.inner_subtensor.chain_endpoint @@ -26,13 +24,9 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.filter_netuids_by_registered_hotkeys = ( subtensor.inner_subtensor.filter_netuids_by_registered_hotkeys ) - subtensor.get_admin_freeze_window = ( - subtensor.inner_subtensor.get_admin_freeze_window - ) + subtensor.get_admin_freeze_window = subtensor.inner_subtensor.get_admin_freeze_window subtensor.get_all_commitments = subtensor.inner_subtensor.get_all_commitments - subtensor.get_all_metagraphs_info = ( - subtensor.inner_subtensor.get_all_metagraphs_info - ) + subtensor.get_all_metagraphs_info = subtensor.inner_subtensor.get_all_metagraphs_info subtensor.get_all_neuron_certificates = ( subtensor.inner_subtensor.get_all_neuron_certificates ) @@ -43,28 +37,27 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.get_balance = subtensor.inner_subtensor.get_balance subtensor.get_balances = subtensor.inner_subtensor.get_balances subtensor.get_block_hash = subtensor.inner_subtensor.get_block_hash - subtensor.get_parents = subtensor.inner_subtensor.get_parents subtensor.get_children = subtensor.inner_subtensor.get_children subtensor.get_children_pending = subtensor.inner_subtensor.get_children_pending subtensor.get_commitment = subtensor.inner_subtensor.get_commitment subtensor.get_current_block = subtensor.inner_subtensor.get_current_block - subtensor.get_last_commitment_bonds_reset_block = ( - subtensor.inner_subtensor.get_last_commitment_bonds_reset_block - ) subtensor.get_delegate_by_hotkey = subtensor.inner_subtensor.get_delegate_by_hotkey - subtensor.get_delegate_identities = ( - subtensor.inner_subtensor.get_delegate_identities - ) + subtensor.get_delegate_identities = subtensor.inner_subtensor.get_delegate_identities subtensor.get_delegate_take = subtensor.inner_subtensor.get_delegate_take subtensor.get_delegated = subtensor.inner_subtensor.get_delegated subtensor.get_delegates = subtensor.inner_subtensor.get_delegates - subtensor.get_existential_deposit = ( - subtensor.inner_subtensor.get_existential_deposit - ) + subtensor.get_existential_deposit = subtensor.inner_subtensor.get_existential_deposit subtensor.get_hotkey_owner = subtensor.inner_subtensor.get_hotkey_owner subtensor.get_hotkey_stake = subtensor.inner_subtensor.get_hotkey_stake subtensor.get_hyperparameter = subtensor.inner_subtensor.get_hyperparameter + subtensor.get_last_commitment_bonds_reset_block = ( + subtensor.inner_subtensor.get_last_commitment_bonds_reset_block + ) subtensor.get_liquidity_list = subtensor.inner_subtensor.get_liquidity_list + subtensor.get_mechanism_count = subtensor.inner_subtensor.get_mechanism_count + subtensor.get_mechanism_emission_split = ( + subtensor.inner_subtensor.get_mechanism_emission_split + ) subtensor.get_metagraph_info = subtensor.inner_subtensor.get_metagraph_info subtensor.get_minimum_required_stake = ( subtensor.inner_subtensor.get_minimum_required_stake @@ -78,9 +71,8 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.inner_subtensor.get_next_epoch_start_block ) subtensor.get_owned_hotkeys = subtensor.inner_subtensor.get_owned_hotkeys - subtensor.get_revealed_commitment = ( - subtensor.inner_subtensor.get_revealed_commitment - ) + subtensor.get_parents = subtensor.inner_subtensor.get_parents + subtensor.get_revealed_commitment = subtensor.inner_subtensor.get_revealed_commitment subtensor.get_revealed_commitment_by_hotkey = ( subtensor.inner_subtensor.get_revealed_commitment_by_hotkey ) @@ -94,24 +86,16 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.inner_subtensor.get_stake_info_for_coldkey ) subtensor.get_stake_movement_fee = subtensor.inner_subtensor.get_stake_movement_fee - subtensor.get_stake_operations_fee = ( - subtensor.inner_subtensor.get_stake_operations_fee - ) + subtensor.get_stake_operations_fee = subtensor.inner_subtensor.get_stake_operations_fee subtensor.get_stake_weight = subtensor.inner_subtensor.get_stake_weight - subtensor.get_mechanism_emission_split = ( - subtensor.inner_subtensor.get_mechanism_emission_split - ) - subtensor.get_mechanism_count = subtensor.inner_subtensor.get_mechanism_count subtensor.get_subnet_burn_cost = subtensor.inner_subtensor.get_subnet_burn_cost subtensor.get_subnet_hyperparameters = ( subtensor.inner_subtensor.get_subnet_hyperparameters ) subtensor.get_subnet_info = subtensor.inner_subtensor.get_subnet_info + subtensor.get_subnet_owner_hotkey = subtensor.inner_subtensor.get_subnet_owner_hotkey subtensor.get_subnet_price = subtensor.inner_subtensor.get_subnet_price subtensor.get_subnet_prices = subtensor.inner_subtensor.get_subnet_prices - subtensor.get_subnet_owner_hotkey = ( - subtensor.inner_subtensor.get_subnet_owner_hotkey - ) subtensor.get_subnet_reveal_period_epochs = ( subtensor.inner_subtensor.get_subnet_reveal_period_epochs ) @@ -134,15 +118,11 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.is_fast_blocks = subtensor.inner_subtensor.is_fast_blocks subtensor.is_hotkey_delegate = subtensor.inner_subtensor.is_hotkey_delegate subtensor.is_hotkey_registered = subtensor.inner_subtensor.is_hotkey_registered - subtensor.is_hotkey_registered_any = ( - subtensor.inner_subtensor.is_hotkey_registered_any - ) + subtensor.is_hotkey_registered_any = subtensor.inner_subtensor.is_hotkey_registered_any subtensor.is_hotkey_registered_on_subnet = ( subtensor.inner_subtensor.is_hotkey_registered_on_subnet ) - subtensor.is_in_admin_freeze_window = ( - subtensor.inner_subtensor.is_in_admin_freeze_window - ) + subtensor.is_in_admin_freeze_window = subtensor.inner_subtensor.is_in_admin_freeze_window subtensor.is_subnet_active = subtensor.inner_subtensor.is_subnet_active subtensor.last_drand_round = subtensor.inner_subtensor.last_drand_round subtensor.log_verbose = subtensor.inner_subtensor.log_verbose @@ -151,10 +131,10 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.min_allowed_weights = subtensor.inner_subtensor.min_allowed_weights subtensor.modify_liquidity = subtensor.inner_subtensor.modify_liquidity subtensor.move_stake = subtensor.inner_subtensor.move_stake - subtensor.network = subtensor.inner_subtensor.network - subtensor.neurons = subtensor.inner_subtensor.neurons subtensor.neuron_for_uid = subtensor.inner_subtensor.neuron_for_uid + subtensor.neurons = subtensor.inner_subtensor.neurons subtensor.neurons_lite = subtensor.inner_subtensor.neurons_lite + subtensor.network = subtensor.inner_subtensor.network subtensor.query_constant = subtensor.inner_subtensor.query_constant subtensor.query_identity = subtensor.inner_subtensor.query_identity subtensor.query_map = subtensor.inner_subtensor.query_map @@ -163,9 +143,9 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.query_runtime_api = subtensor.inner_subtensor.query_runtime_api subtensor.query_subtensor = subtensor.inner_subtensor.query_subtensor subtensor.recycle = subtensor.inner_subtensor.recycle - subtensor.remove_liquidity = subtensor.inner_subtensor.remove_liquidity subtensor.register = subtensor.inner_subtensor.register subtensor.register_subnet = subtensor.inner_subtensor.register_subnet + subtensor.remove_liquidity = subtensor.inner_subtensor.remove_liquidity subtensor.reveal_weights = subtensor.inner_subtensor.reveal_weights subtensor.root_register = subtensor.inner_subtensor.root_register subtensor.root_set_pending_childkey_cooldown = ( @@ -179,9 +159,7 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.set_subnet_identity = subtensor.inner_subtensor.set_subnet_identity subtensor.set_weights = subtensor.inner_subtensor.set_weights subtensor.setup_config = subtensor.inner_subtensor.setup_config - subtensor.sign_and_send_extrinsic = ( - subtensor.inner_subtensor.sign_and_send_extrinsic - ) + subtensor.sign_and_send_extrinsic = subtensor.inner_subtensor.sign_and_send_extrinsic subtensor.start_call = subtensor.inner_subtensor.start_call subtensor.state_call = subtensor.inner_subtensor.state_call subtensor.subnet = subtensor.inner_subtensor.subnet From ef628e498697d2858eec1239e469ead1eaf36558 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 16:45:33 -0700 Subject: [PATCH 07/41] - method `get_subnets` renamed to `get_all_subnets_netuid` (more obvious) --- MIGRATION.md | 5 +- .../core/addons/subtensor_api/subnets.py | 2 +- bittensor/core/addons/subtensor_api/utils.py | 46 ++++++++++++++----- bittensor/core/async_subtensor.py | 4 +- bittensor/core/metagraph.py | 5 +- bittensor/core/subtensor.py | 4 +- tests/e2e_tests/test_subtensor_functions.py | 10 ++-- tests/helpers/integration_websocket_data.py | 2 +- tests/unit_tests/test_async_subtensor.py | 6 +-- tests/unit_tests/test_subtensor.py | 16 +++---- 10 files changed, 62 insertions(+), 38 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index aa5ea513b0..ba81c79179 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -25,8 +25,8 @@ ## Subtensor 1. In the synchronous Subtensor class, the `get_owned_hotkeys` method includes a `reuse_block` parameter that is inconsistent with other methods. Either remove this parameter from `get_owned_hotkeys`, or add it to all other methods that directly call self.substrate.* to maintain a consistent interface. -2. In all methods where we `get_stake_operations_fee` is called, remove unused arguments. Consider combining all methods using `get_stake_operations_fee` into one common one. -3. Delete deprecated `get_current_weight_commit_info` and `get_current_weight_commit_info_v2`. Rename `get_timelocked_weight_commits` to get_current_weight_commit_info. +2. ✅ In all methods where we `get_stake_operations_fee` is called, remove unused arguments. Consider combining all methods using `get_stake_operations_fee` into one common one. +3. ✅ Delete deprecated `get_current_weight_commit_info` and `get_current_weight_commit_info_v2`. ~~Rename `get_timelocked_weight_commits` to `get_current_weight_commit_info`.~~ 4. ✅ Remove references like `get_stake_info_for_coldkey = get_stake_for_coldkey`. 5. Reconsider some methods naming across the entire subtensor module. 6. ~~Add `hotkey_ss58` parameter to `get_liquidity_list` method. One wallet can have many HKs. Currently, the mentioned method uses default HK only.~~ wrong idea @@ -235,6 +235,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - method `query_map` has updated parameters order. - method `add_stake_multiple` has updated parameters order. - method `get_stake_for_coldkey` removed, bc this is the same as `get_stake_info_for_coldkey` +- method `get_subnets` renamed to `get_all_subnets_netuid` (more obvious) Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` diff --git a/bittensor/core/addons/subtensor_api/subnets.py b/bittensor/core/addons/subtensor_api/subnets.py index 30c31ff7e1..75b8adbf13 100644 --- a/bittensor/core/addons/subtensor_api/subnets.py +++ b/bittensor/core/addons/subtensor_api/subnets.py @@ -16,6 +16,7 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.commit_reveal_enabled = subtensor.commit_reveal_enabled self.difficulty = subtensor.difficulty self.get_all_subnets_info = subtensor.get_all_subnets_info + self.get_all_subnets_netuid = subtensor.get_all_subnets_netuid self.get_parents = subtensor.get_parents self.get_children = subtensor.get_children self.get_children_pending = subtensor.get_children_pending @@ -35,7 +36,6 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_subnet_owner_hotkey = subtensor.get_subnet_owner_hotkey self.get_subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs self.get_subnet_validator_permits = subtensor.get_subnet_validator_permits - self.get_subnets = subtensor.get_subnets self.get_total_subnets = subtensor.get_total_subnets self.get_uid_for_hotkey_on_subnet = subtensor.get_uid_for_hotkey_on_subnet self.immunity_period = subtensor.immunity_period diff --git a/bittensor/core/addons/subtensor_api/utils.py b/bittensor/core/addons/subtensor_api/utils.py index a070458493..4b255e1b8c 100644 --- a/bittensor/core/addons/subtensor_api/utils.py +++ b/bittensor/core/addons/subtensor_api/utils.py @@ -11,7 +11,9 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.add_stake_multiple = subtensor.inner_subtensor.add_stake_multiple subtensor.all_subnets = subtensor.inner_subtensor.all_subnets subtensor.blocks_since_last_step = subtensor.inner_subtensor.blocks_since_last_step - subtensor.blocks_since_last_update = subtensor.inner_subtensor.blocks_since_last_update + subtensor.blocks_since_last_update = ( + subtensor.inner_subtensor.blocks_since_last_update + ) subtensor.bonds = subtensor.inner_subtensor.bonds subtensor.burned_register = subtensor.inner_subtensor.burned_register subtensor.chain_endpoint = subtensor.inner_subtensor.chain_endpoint @@ -24,9 +26,13 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.filter_netuids_by_registered_hotkeys = ( subtensor.inner_subtensor.filter_netuids_by_registered_hotkeys ) - subtensor.get_admin_freeze_window = subtensor.inner_subtensor.get_admin_freeze_window + subtensor.get_admin_freeze_window = ( + subtensor.inner_subtensor.get_admin_freeze_window + ) subtensor.get_all_commitments = subtensor.inner_subtensor.get_all_commitments - subtensor.get_all_metagraphs_info = subtensor.inner_subtensor.get_all_metagraphs_info + subtensor.get_all_metagraphs_info = ( + subtensor.inner_subtensor.get_all_metagraphs_info + ) subtensor.get_all_neuron_certificates = ( subtensor.inner_subtensor.get_all_neuron_certificates ) @@ -42,11 +48,15 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.get_commitment = subtensor.inner_subtensor.get_commitment subtensor.get_current_block = subtensor.inner_subtensor.get_current_block subtensor.get_delegate_by_hotkey = subtensor.inner_subtensor.get_delegate_by_hotkey - subtensor.get_delegate_identities = subtensor.inner_subtensor.get_delegate_identities + subtensor.get_delegate_identities = ( + subtensor.inner_subtensor.get_delegate_identities + ) subtensor.get_delegate_take = subtensor.inner_subtensor.get_delegate_take subtensor.get_delegated = subtensor.inner_subtensor.get_delegated subtensor.get_delegates = subtensor.inner_subtensor.get_delegates - subtensor.get_existential_deposit = subtensor.inner_subtensor.get_existential_deposit + subtensor.get_existential_deposit = ( + subtensor.inner_subtensor.get_existential_deposit + ) subtensor.get_hotkey_owner = subtensor.inner_subtensor.get_hotkey_owner subtensor.get_hotkey_stake = subtensor.inner_subtensor.get_hotkey_stake subtensor.get_hyperparameter = subtensor.inner_subtensor.get_hyperparameter @@ -72,7 +82,9 @@ def add_legacy_methods(subtensor: "SubtensorApi"): ) subtensor.get_owned_hotkeys = subtensor.inner_subtensor.get_owned_hotkeys subtensor.get_parents = subtensor.inner_subtensor.get_parents - subtensor.get_revealed_commitment = subtensor.inner_subtensor.get_revealed_commitment + subtensor.get_revealed_commitment = ( + subtensor.inner_subtensor.get_revealed_commitment + ) subtensor.get_revealed_commitment_by_hotkey = ( subtensor.inner_subtensor.get_revealed_commitment_by_hotkey ) @@ -86,14 +98,18 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.inner_subtensor.get_stake_info_for_coldkey ) subtensor.get_stake_movement_fee = subtensor.inner_subtensor.get_stake_movement_fee - subtensor.get_stake_operations_fee = subtensor.inner_subtensor.get_stake_operations_fee + subtensor.get_stake_operations_fee = ( + subtensor.inner_subtensor.get_stake_operations_fee + ) subtensor.get_stake_weight = subtensor.inner_subtensor.get_stake_weight subtensor.get_subnet_burn_cost = subtensor.inner_subtensor.get_subnet_burn_cost subtensor.get_subnet_hyperparameters = ( subtensor.inner_subtensor.get_subnet_hyperparameters ) subtensor.get_subnet_info = subtensor.inner_subtensor.get_subnet_info - subtensor.get_subnet_owner_hotkey = subtensor.inner_subtensor.get_subnet_owner_hotkey + subtensor.get_subnet_owner_hotkey = ( + subtensor.inner_subtensor.get_subnet_owner_hotkey + ) subtensor.get_subnet_price = subtensor.inner_subtensor.get_subnet_price subtensor.get_subnet_prices = subtensor.inner_subtensor.get_subnet_prices subtensor.get_subnet_reveal_period_epochs = ( @@ -102,7 +118,7 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.get_subnet_validator_permits = ( subtensor.inner_subtensor.get_subnet_validator_permits ) - subtensor.get_subnets = subtensor.inner_subtensor.get_subnets + subtensor.get_all_subnets_netuid = subtensor.inner_subtensor.get_all_subnets_netuid subtensor.get_timelocked_weight_commits = ( subtensor.inner_subtensor.get_timelocked_weight_commits ) @@ -118,11 +134,15 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.is_fast_blocks = subtensor.inner_subtensor.is_fast_blocks subtensor.is_hotkey_delegate = subtensor.inner_subtensor.is_hotkey_delegate subtensor.is_hotkey_registered = subtensor.inner_subtensor.is_hotkey_registered - subtensor.is_hotkey_registered_any = subtensor.inner_subtensor.is_hotkey_registered_any + subtensor.is_hotkey_registered_any = ( + subtensor.inner_subtensor.is_hotkey_registered_any + ) subtensor.is_hotkey_registered_on_subnet = ( subtensor.inner_subtensor.is_hotkey_registered_on_subnet ) - subtensor.is_in_admin_freeze_window = subtensor.inner_subtensor.is_in_admin_freeze_window + subtensor.is_in_admin_freeze_window = ( + subtensor.inner_subtensor.is_in_admin_freeze_window + ) subtensor.is_subnet_active = subtensor.inner_subtensor.is_subnet_active subtensor.last_drand_round = subtensor.inner_subtensor.last_drand_round subtensor.log_verbose = subtensor.inner_subtensor.log_verbose @@ -159,7 +179,9 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.set_subnet_identity = subtensor.inner_subtensor.set_subnet_identity subtensor.set_weights = subtensor.inner_subtensor.set_weights subtensor.setup_config = subtensor.inner_subtensor.setup_config - subtensor.sign_and_send_extrinsic = subtensor.inner_subtensor.sign_and_send_extrinsic + subtensor.sign_and_send_extrinsic = ( + subtensor.inner_subtensor.sign_and_send_extrinsic + ) subtensor.start_call = subtensor.inner_subtensor.start_call subtensor.state_call = subtensor.inner_subtensor.state_call subtensor.subnet = subtensor.inner_subtensor.subnet diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 94e8b1f6a0..5b73ab76d9 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -2810,7 +2810,7 @@ async def get_stake_for_coldkey_and_hotkey( elif not block_hash: block_hash = await self.substrate.get_chain_head() if netuids is None: - all_netuids = await self.get_subnets(block_hash=block_hash) + all_netuids = await self.get_all_subnets_netuid(block_hash=block_hash) else: all_netuids = netuids results = await asyncio.gather( @@ -3035,7 +3035,7 @@ async def get_subnet_reveal_period_epochs( param_name="RevealPeriodEpochs", block_hash=block_hash, netuid=netuid ) - async def get_subnets( + async def get_all_subnets_netuid( self, block: Optional[int] = None, block_hash: Optional[str] = None, diff --git a/bittensor/core/metagraph.py b/bittensor/core/metagraph.py index 0231411df5..494ef24909 100644 --- a/bittensor/core/metagraph.py +++ b/bittensor/core/metagraph.py @@ -1556,7 +1556,8 @@ async def _process_root_weights( """ data_array = [] n_subnets_, subnets = await asyncio.gather( - subtensor.get_total_subnets(block=block), subtensor.get_subnets(block=block) + subtensor.get_total_subnets(block=block), + subtensor.get_all_subnets_netuid(block=block), ) n_subnets = n_subnets_ or 0 for item in data: @@ -1867,7 +1868,7 @@ def _process_root_weights( """ data_array = [] n_subnets = subtensor.get_total_subnets(block=block) or 0 - subnets = subtensor.get_subnets(block=block) + subnets = subtensor.get_all_subnets_netuid(block=block) for item in data: if len(item) == 0: if use_torch(): diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 7d39891c80..24128d0b12 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2024,7 +2024,7 @@ def get_stake_for_coldkey_and_hotkey( A {netuid: StakeInfo} pairing of all stakes across all subnets. """ if netuids is None: - all_netuids = self.get_subnets(block=block) + all_netuids = self.get_all_subnets_netuid(block=block) else: all_netuids = netuids results = [ @@ -2198,7 +2198,7 @@ def get_subnet_reveal_period_epochs( ), ) - def get_subnets(self, block: Optional[int] = None) -> UIDs: + def get_all_subnets_netuid(self, block: Optional[int] = None) -> UIDs: """ Retrieves the list of all subnet unique identifiers (netuids) currently present in the Bittensor network. diff --git a/tests/e2e_tests/test_subtensor_functions.py b/tests/e2e_tests/test_subtensor_functions.py index 654f69c035..570171da8c 100644 --- a/tests/e2e_tests/test_subtensor_functions.py +++ b/tests/e2e_tests/test_subtensor_functions.py @@ -20,7 +20,7 @@ """ Verifies: -* get_subnets() +* get_all_subnets_netuid() * get_total_subnets() * subnet_exists() * get_netuids_for_hotkey() @@ -59,7 +59,7 @@ async def test_subtensor_extrinsics(subtensor, templates, alice_wallet, bob_wall existential_deposit = Balance.from_tao(0.000_000_500) # Subnets 0 and 1 are bootstrapped from the start - assert subtensor.subnets.get_subnets() == [0, 1] + assert subtensor.subnets.get_all_subnets_netuid() == [0, 1] assert subtensor.subnets.get_total_subnets() == 2 # Assert correct balance is fetched for Alice @@ -93,7 +93,7 @@ async def test_subtensor_extrinsics(subtensor, templates, alice_wallet, bob_wall ), "Balance is the same even after registering a subnet." # Subnet 2 is added after registration - assert subtensor.subnets.get_subnets() == [0, 1, 2] + assert subtensor.subnets.get_all_subnets_netuid() == [0, 1, 2] assert subtensor.subnets.get_total_subnets() == 3 # Verify subnet 2 created successfully @@ -245,7 +245,7 @@ async def test_subtensor_extrinsics_async( existential_deposit = Balance.from_tao(0.000_000_500) # Subnets 0 and 1 are bootstrapped from the start - assert await async_subtensor.subnets.get_subnets() == [0, 1] + assert await async_subtensor.subnets.get_all_subnets_netuid() == [0, 1] assert await async_subtensor.subnets.get_total_subnets() == 2 # Assert correct balance is fetched for Alice @@ -281,7 +281,7 @@ async def test_subtensor_extrinsics_async( ), "Balance is the same even after registering a subnet." # Subnet 2 is added after registration - assert await async_subtensor.subnets.get_subnets() == [0, 1, 2] + assert await async_subtensor.subnets.get_all_subnets_netuid() == [0, 1, 2] assert await async_subtensor.subnets.get_total_subnets() == 3 # Verify subnet 2 created successfully diff --git a/tests/helpers/integration_websocket_data.py b/tests/helpers/integration_websocket_data.py index ce450ad03c..c90b75ad48 100644 --- a/tests/helpers/integration_websocket_data.py +++ b/tests/helpers/integration_websocket_data.py @@ -5303,7 +5303,7 @@ }, "system_chain": {"[]": {"jsonrpc": "2.0", "result": "Bittensor"}}, }, - "get_subnets": { + "get_all_subnets_netuid": { "chain_getHead": { "[]": { "jsonrpc": "2.0", diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index fab019fc1c..338ac22aac 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -390,7 +390,7 @@ async def test_get_total_subnets(subtensor, mocker): ) @pytest.mark.asyncio async def test_get_subnets(subtensor, mocker, records, response): - """Tests get_subnets method with any return.""" + """Tests get_all_subnets_netuid method with any return.""" # Preps fake_result = mocker.AsyncMock(autospec=list) fake_result.records = records @@ -405,7 +405,7 @@ async def test_get_subnets(subtensor, mocker, records, response): fake_block_hash = None # Call - result = await subtensor.get_subnets(block_hash=fake_block_hash) + result = await subtensor.get_all_subnets_netuid(block_hash=fake_block_hash) # Asserts mocked_substrate_query_map.assert_called_once_with( @@ -559,7 +559,7 @@ async def test_get_stake_for_coldkey_and_hotkey(subtensor, mocker): subtensor.substrate, "get_chain_head", return_value=block_hash ) mocked_get_subnets = mocker.patch.object( - subtensor, "get_subnets", return_value=netuids + subtensor, "get_all_subnets_netuid", return_value=netuids ) result = await subtensor.get_stake_for_coldkey_and_hotkey( diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 07d8796987..de7554965c 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -759,9 +759,9 @@ def test_get_total_subnets_no_block(mocker, subtensor): subtensor.substrate.get_block_hash.assert_not_called() -# `get_subnets` tests +# `get_all_subnets_netuid` tests def test_get_subnets_success(mocker, subtensor): - """Test get_subnets returns correct list when subnet information is found.""" + """Test get_all_subnets_netuid returns correct list when subnet information is found.""" # Prep block = 123 mock_result = mocker.MagicMock() @@ -770,7 +770,7 @@ def test_get_subnets_success(mocker, subtensor): mocker.patch.object(subtensor.substrate, "query_map", return_value=mock_result) # Call - result = subtensor.get_subnets(block) + result = subtensor.get_all_subnets_netuid(block) # Asserts assert result == [1, 2] @@ -783,7 +783,7 @@ def test_get_subnets_success(mocker, subtensor): def test_get_subnets_no_data(mocker, subtensor): - """Test get_subnets returns empty list when no subnet information is found.""" + """Test get_all_subnets_netuid returns empty list when no subnet information is found.""" # Prep block = 123 mock_result = mocker.MagicMock() @@ -791,7 +791,7 @@ def test_get_subnets_no_data(mocker, subtensor): mocker.patch.object(subtensor.substrate, "query_map", return_value=mock_result) # Call - result = subtensor.get_subnets(block) + result = subtensor.get_all_subnets_netuid(block) # Asserts assert result == [] @@ -804,7 +804,7 @@ def test_get_subnets_no_data(mocker, subtensor): def test_get_subnets_no_block_specified(mocker, subtensor): - """Test get_subnets with no block specified.""" + """Test get_all_subnets_netuid with no block specified.""" # Prep mock_result = mocker.MagicMock() mock_result.records = [(1, True), (2, True)] @@ -812,7 +812,7 @@ def test_get_subnets_no_block_specified(mocker, subtensor): mocker.patch.object(subtensor.substrate, "query_map", return_value=mock_result) # Call - result = subtensor.get_subnets() + result = subtensor.get_all_subnets_netuid() # Asserts assert result == [1, 2] @@ -2087,7 +2087,7 @@ def test_get_stake_for_coldkey_and_hotkey(subtensor, mocker): subtensor, "query_runtime_api", side_effect=query_fetcher ) mocked_get_subnets = mocker.patch.object( - subtensor, "get_subnets", return_value=netuids + subtensor, "get_all_subnets_netuid", return_value=netuids ) result = subtensor.get_stake_for_coldkey_and_hotkey( From bac4f7b67375d74cf91543b6170bd3c897e1ed48 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 16:50:09 -0700 Subject: [PATCH 08/41] method `get_owned_hotkeys` get rid `reuse_block` parameter to be consistent with other sync methods. --- MIGRATION.md | 3 ++- bittensor/core/subtensor.py | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index ba81c79179..608b109cf5 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -53,7 +53,7 @@ rename this variable in documentation. 5. Rename `non-/fast-blocks` to `non-/fast-runtime` in related places to be consistent with subtensor repo. Related with testing, subtensor scripts, documentation. -6. To be consistent throughout the SDK `(in progress)`: +6. ✅ To be consistent throughout the SDK `(in progress)`: `hotkey`, `coldkey`, `hotkeypub`, and `coldkeypub` are keypairs `hotkey_ss58`, `coldkey_ss58`, `hotkeypub_ss58`, and `coldkeypub_ss58` are SS58 addresses of keypair. @@ -236,6 +236,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - method `add_stake_multiple` has updated parameters order. - method `get_stake_for_coldkey` removed, bc this is the same as `get_stake_info_for_coldkey` - method `get_subnets` renamed to `get_all_subnets_netuid` (more obvious) +- method `get_owned_hotkeys` get rid `reuse_block` parameter to be consistent with other sync methods. Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 24128d0b12..a3808c3d12 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1694,7 +1694,6 @@ def get_owned_hotkeys( self, coldkey_ss58: str, block: Optional[int] = None, - reuse_block: bool = False, ) -> list[str]: """ Retrieves all hotkeys owned by a specific coldkey address. @@ -1702,7 +1701,6 @@ def get_owned_hotkeys( Parameters: coldkey_ss58: The SS58 address of the coldkey to query. block: The blockchain block number for the query. - reuse_block: Whether to reuse the last-used blockchain block hash. Returns: list[str]: A list of hotkey SS58 addresses owned by the coldkey. @@ -1713,7 +1711,6 @@ def get_owned_hotkeys( storage_function="OwnedHotkeys", params=[coldkey_ss58], block_hash=block_hash, - reuse_block_hash=reuse_block, ) return [decode_account_id(hotkey[0]) for hotkey in owned_hotkeys or []] From 9942c1975e9e1961004c8ba9ddcbd72a7591cadd Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 16:59:17 -0700 Subject: [PATCH 09/41] All liquidity methods and related extrinsics received a renamed parameter: `hotkey` -> `hotkey_ss58` --- MIGRATION.md | 4 ++-- bittensor/core/async_subtensor.py | 18 +++++++++--------- bittensor/core/extrinsics/asyncex/liquidity.py | 18 +++++++++--------- bittensor/core/extrinsics/liquidity.py | 18 +++++++++--------- bittensor/core/subtensor.py | 18 +++++++++--------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 608b109cf5..bc1fda3408 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -28,7 +28,7 @@ 2. ✅ In all methods where we `get_stake_operations_fee` is called, remove unused arguments. Consider combining all methods using `get_stake_operations_fee` into one common one. 3. ✅ Delete deprecated `get_current_weight_commit_info` and `get_current_weight_commit_info_v2`. ~~Rename `get_timelocked_weight_commits` to `get_current_weight_commit_info`.~~ 4. ✅ Remove references like `get_stake_info_for_coldkey = get_stake_for_coldkey`. -5. Reconsider some methods naming across the entire subtensor module. +5. ✅ Reconsider some methods naming across the entire subtensor module. 6. ~~Add `hotkey_ss58` parameter to `get_liquidity_list` method. One wallet can have many HKs. Currently, the mentioned method uses default HK only.~~ wrong idea ## Metagraph @@ -227,7 +227,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: ### Subtensor changes - method `all_subnets` has renamed parameter from `block_number` to `block` (consistency in the codebase). -- The `hotkey` parameter, which meant ss58 key address, was renamed to `hotkey_ss58` in all methods (consistency in the codebase). +- The `hotkey` parameter, which meant ss58 key address, was renamed to `hotkey_ss58` in all methods and related extrinsics (consistency in the codebase). - The `coldkey` parameter, which meant ss58 key address, was renamed to `coldkey_ss58` in all methods (consistency in the codebase). - method `query_subtensor` has updated parameters order. - method `query_module` has updated parameters order. diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 5b73ab76d9..fb6395f253 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4317,7 +4317,7 @@ async def add_liquidity( liquidity: Balance, price_low: Balance, price_high: Balance, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4332,7 +4332,7 @@ async def add_liquidity( 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. + hotkey_ss58: 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. @@ -4353,7 +4353,7 @@ async def add_liquidity( liquidity=liquidity, price_low=price_low, price_high=price_high, - hotkey=hotkey, + hotkey_ss58=hotkey_ss58, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4539,7 +4539,7 @@ async def modify_liquidity( netuid: int, position_id: int, liquidity_delta: Balance, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4552,7 +4552,7 @@ async def modify_liquidity( 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. + hotkey_ss58: 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. @@ -4598,7 +4598,7 @@ async def modify_liquidity( netuid=netuid, position_id=position_id, liquidity_delta=liquidity_delta, - hotkey=hotkey, + hotkey_ss58=hotkey_ss58, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4759,7 +4759,7 @@ async def remove_liquidity( wallet: "Wallet", netuid: int, position_id: int, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -4771,7 +4771,7 @@ async def remove_liquidity( 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. + hotkey_ss58: 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. @@ -4792,7 +4792,7 @@ async def remove_liquidity( wallet=wallet, netuid=netuid, position_id=position_id, - hotkey=hotkey, + hotkey_ss58=hotkey_ss58, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, diff --git a/bittensor/core/extrinsics/asyncex/liquidity.py b/bittensor/core/extrinsics/asyncex/liquidity.py index f655da7a21..eb42ce2679 100644 --- a/bittensor/core/extrinsics/asyncex/liquidity.py +++ b/bittensor/core/extrinsics/asyncex/liquidity.py @@ -16,7 +16,7 @@ async def add_liquidity_extrinsic( liquidity: Balance, price_low: Balance, price_high: Balance, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -32,7 +32,7 @@ async def add_liquidity_extrinsic( 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. + hotkey_ss58: 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. @@ -59,7 +59,7 @@ async def add_liquidity_extrinsic( call_module="Swap", call_function="add_liquidity", call_params={ - "hotkey": hotkey or wallet.hotkey.ss58_address, + "hotkey": hotkey_ss58 or wallet.hotkey.ss58_address, "netuid": netuid, "tick_low": tick_low, "tick_high": tick_high, @@ -85,7 +85,7 @@ async def modify_liquidity_extrinsic( netuid: int, position_id: int, liquidity_delta: Balance, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -99,7 +99,7 @@ async def modify_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. 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. + hotkey_ss58: 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. @@ -123,7 +123,7 @@ async def modify_liquidity_extrinsic( call_module="Swap", call_function="modify_position", call_params={ - "hotkey": hotkey or wallet.hotkey.ss58_address, + "hotkey": hotkey_ss58 or wallet.hotkey.ss58_address, "netuid": netuid, "position_id": position_id, "liquidity_delta": liquidity_delta.rao, @@ -147,7 +147,7 @@ async def remove_liquidity_extrinsic( wallet: "Wallet", netuid: int, position_id: int, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -160,7 +160,7 @@ async def remove_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. 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. + hotkey_ss58: 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. @@ -184,7 +184,7 @@ async def remove_liquidity_extrinsic( call_module="Swap", call_function="remove_liquidity", call_params={ - "hotkey": hotkey or wallet.hotkey.ss58_address, + "hotkey": hotkey_ss58 or wallet.hotkey.ss58_address, "netuid": netuid, "position_id": position_id, }, diff --git a/bittensor/core/extrinsics/liquidity.py b/bittensor/core/extrinsics/liquidity.py index 64ebc8c674..f86604a202 100644 --- a/bittensor/core/extrinsics/liquidity.py +++ b/bittensor/core/extrinsics/liquidity.py @@ -16,7 +16,7 @@ def add_liquidity_extrinsic( liquidity: Balance, price_low: Balance, price_high: Balance, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -32,7 +32,7 @@ def add_liquidity_extrinsic( 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. + hotkey_ss58: 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. @@ -59,7 +59,7 @@ def add_liquidity_extrinsic( call_module="Swap", call_function="add_liquidity", call_params={ - "hotkey": hotkey or wallet.hotkey.ss58_address, + "hotkey": hotkey_ss58 or wallet.hotkey.ss58_address, "netuid": netuid, "tick_low": tick_low, "tick_high": tick_high, @@ -85,7 +85,7 @@ def modify_liquidity_extrinsic( netuid: int, position_id: int, liquidity_delta: Balance, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -99,7 +99,7 @@ def modify_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. 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. + hotkey_ss58: 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. @@ -123,7 +123,7 @@ def modify_liquidity_extrinsic( call_module="Swap", call_function="modify_position", call_params={ - "hotkey": hotkey or wallet.hotkey.ss58_address, + "hotkey": hotkey_ss58 or wallet.hotkey.ss58_address, "netuid": netuid, "position_id": position_id, "liquidity_delta": liquidity_delta.rao, @@ -147,7 +147,7 @@ def remove_liquidity_extrinsic( wallet: "Wallet", netuid: int, position_id: int, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -160,7 +160,7 @@ def remove_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. 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. + hotkey_ss58: 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. @@ -184,7 +184,7 @@ def remove_liquidity_extrinsic( call_module="Swap", call_function="remove_liquidity", call_params={ - "hotkey": hotkey or wallet.hotkey.ss58_address, + "hotkey": hotkey_ss58 or wallet.hotkey.ss58_address, "netuid": netuid, "position_id": position_id, }, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index a3808c3d12..a824273a22 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3206,7 +3206,7 @@ def add_liquidity( liquidity: Balance, price_low: Balance, price_high: Balance, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -3221,7 +3221,7 @@ def add_liquidity( 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. + hotkey_ss58: 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. @@ -3242,7 +3242,7 @@ def add_liquidity( liquidity=liquidity, price_low=price_low, price_high=price_high, - hotkey=hotkey, + hotkey_ss58=hotkey_ss58, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -3425,7 +3425,7 @@ def modify_liquidity( netuid: int, position_id: int, liquidity_delta: Balance, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -3438,7 +3438,7 @@ def modify_liquidity( 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. + hotkey_ss58: 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. @@ -3484,7 +3484,7 @@ def modify_liquidity( netuid=netuid, position_id=position_id, liquidity_delta=liquidity_delta, - hotkey=hotkey, + hotkey_ss58=hotkey_ss58, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -3645,7 +3645,7 @@ def remove_liquidity( wallet: "Wallet", netuid: int, position_id: int, - hotkey: Optional[str] = None, + hotkey_ss58: Optional[str] = None, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, @@ -3657,7 +3657,7 @@ def remove_liquidity( 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. + hotkey_ss58: 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. @@ -3678,7 +3678,7 @@ def remove_liquidity( wallet=wallet, netuid=netuid, position_id=position_id, - hotkey=hotkey, + hotkey_ss58=hotkey_ss58, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, From 2c4f7758e82600aac5a71d1b68e6b1e438e284ca Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 17:06:22 -0700 Subject: [PATCH 10/41] deprecated `bittensor.utils.version.version_checking` removed --- bittensor/utils/__init__.py | 3 +-- bittensor/utils/easy_imports.py | 2 -- bittensor/utils/version.py | 20 -------------------- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index c75a0b7936..0ffcdb37c1 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -18,7 +18,7 @@ from bittensor.core.settings import SS58_FORMAT from bittensor.utils.btlogging import logging from .registration import torch, use_torch -from .version import version_checking, check_version, VersionCheckError +from .version import check_version, VersionCheckError if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -36,7 +36,6 @@ logging = logging torch = torch use_torch = use_torch -version_checking = version_checking check_version = check_version VersionCheckError = VersionCheckError ss58_decode = ss58_decode diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index e84a84d786..57bc9ae2bc 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -105,7 +105,6 @@ from bittensor.core.threadpool import PriorityThreadPoolExecutor from bittensor.utils import ( ss58_to_vec_u8, - version_checking, strtobool, get_explorer_url_for_network, ss58_address_to_bytes, @@ -273,7 +272,6 @@ def info(on: bool = True): "Tensor", "PriorityThreadPoolExecutor", "ss58_to_vec_u8", - "version_checking", "strtobool", "get_explorer_url_for_network", "ss58_address_to_bytes", diff --git a/bittensor/utils/version.py b/bittensor/utils/version.py index 3f920e8333..e78fd109e8 100644 --- a/bittensor/utils/version.py +++ b/bittensor/utils/version.py @@ -98,26 +98,6 @@ def check_version(timeout: int = 15): raise VersionCheckError("Version check failed") from e -def version_checking(timeout: int = 15): - """Deprecated, kept for backwards compatibility. Use check_version() instead. - - Parameters: - timeout: The timeout for calling :func:``check_version`` function. - """ - - from warnings import warn - - warn( - "version_checking() is deprecated, please use check_version() instead", - DeprecationWarning, - ) - - try: - check_version(timeout) - except VersionCheckError: - logging.exception("Version check failed") - - def check_latest_version_in_pypi(): """Check for the latest version of the package on PyPI.""" package_name = __name__ From 7e4140d1c2ee95bf532bb5dfe94b7d5420188225 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 17:07:01 -0700 Subject: [PATCH 11/41] rename `hotkey` > `hotkey_ss58` in liquidity methods and extrinsics + fixed unit tests --- tests/unit_tests/test_async_subtensor.py | 10 +++++----- tests/unit_tests/test_subtensor.py | 8 +++----- tests/unit_tests/utils/test_version.py | 17 ----------------- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 338ac22aac..3105e2af8d 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3499,8 +3499,8 @@ async def test_add_liquidity(subtensor, fake_wallet, mocker): wallet=fake_wallet, netuid=netuid, liquidity=Balance.from_tao(150), - price_low=Balance.from_tao(180).rao, - price_high=Balance.from_tao(130).rao, + price_low=Balance.from_tao(180), + price_high=Balance.from_tao(130), ) # Asserts @@ -3511,7 +3511,7 @@ async def test_add_liquidity(subtensor, fake_wallet, mocker): liquidity=Balance.from_tao(150), price_low=Balance.from_tao(180).rao, price_high=Balance.from_tao(130).rao, - hotkey=None, + hotkey_ss58=None, wait_for_inclusion=True, wait_for_finalization=True, period=None, @@ -3545,7 +3545,7 @@ async def test_modify_liquidity(subtensor, fake_wallet, mocker): netuid=netuid, position_id=position_id, liquidity_delta=Balance.from_tao(150), - hotkey=None, + hotkey_ss58=None, wait_for_inclusion=True, wait_for_finalization=True, period=None, @@ -3577,7 +3577,7 @@ async def test_remove_liquidity(subtensor, fake_wallet, mocker): wallet=fake_wallet, netuid=netuid, position_id=position_id, - hotkey=None, + hotkey_ss58=None, wait_for_inclusion=True, wait_for_finalization=True, period=None, diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index de7554965c..86f00f8c61 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3094,7 +3094,6 @@ def test_get_owned_hotkeys_happy_path(subtensor, mocker): storage_function="OwnedHotkeys", params=[fake_coldkey], block_hash=None, - reuse_block_hash=False, ) assert result == [mocked_decode_account_id.return_value] mocked_decode_account_id.assert_called_once_with(fake_hotkey) @@ -3116,7 +3115,6 @@ def test_get_owned_hotkeys_return_empty(subtensor, mocker): storage_function="OwnedHotkeys", params=[fake_coldkey], block_hash=None, - reuse_block_hash=False, ) assert result == [] @@ -3738,7 +3736,7 @@ def test_add_liquidity(subtensor, fake_wallet, mocker): liquidity=Balance.from_tao(150), price_low=Balance.from_tao(180).rao, price_high=Balance.from_tao(130).rao, - hotkey=None, + hotkey_ss58=None, wait_for_inclusion=True, wait_for_finalization=True, period=None, @@ -3771,7 +3769,7 @@ def test_modify_liquidity(subtensor, fake_wallet, mocker): netuid=netuid, position_id=position_id, liquidity_delta=Balance.from_tao(150), - hotkey=None, + hotkey_ss58=None, wait_for_inclusion=True, wait_for_finalization=True, period=None, @@ -3802,7 +3800,7 @@ def test_remove_liquidity(subtensor, fake_wallet, mocker): wallet=fake_wallet, netuid=netuid, position_id=position_id, - hotkey=None, + hotkey_ss58=None, wait_for_inclusion=True, wait_for_finalization=True, period=None, diff --git a/tests/unit_tests/utils/test_version.py b/tests/unit_tests/utils/test_version.py index 6d1785b0bd..6d8d535d18 100644 --- a/tests/unit_tests/utils/test_version.py +++ b/tests/unit_tests/utils/test_version.py @@ -133,20 +133,3 @@ def test_check_version_up_to_date( assert captured.out == "" - -def test_version_checking(mocker: MockerFixture): - mock = mocker.patch("bittensor.utils.version.check_version") - - version.version_checking() - - mock.assert_called_once() - - -def test_version_checking_exception(mocker: MockerFixture): - mock = mocker.patch( - "bittensor.utils.version.check_version", side_effect=version.VersionCheckError - ) - - version.version_checking() - - mock.assert_called_once() From 656c3bab98079a486a0436d7a5320805964452fc Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 17:24:17 -0700 Subject: [PATCH 12/41] method `blocks_since_last_update` improved. Currently it can be used to get historical data from archive node. --- MIGRATION.md | 1 + bittensor/core/async_subtensor.py | 24 +++++++++++++++++++++--- bittensor/core/subtensor.py | 12 +++++++++--- tests/unit_tests/test_async_subtensor.py | 24 +++++++++++++++++------- tests/unit_tests/test_subtensor.py | 2 +- 5 files changed, 49 insertions(+), 14 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index bc1fda3408..c11fb34aa9 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -237,6 +237,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - method `get_stake_for_coldkey` removed, bc this is the same as `get_stake_info_for_coldkey` - method `get_subnets` renamed to `get_all_subnets_netuid` (more obvious) - method `get_owned_hotkeys` get rid `reuse_block` parameter to be consistent with other sync methods. +- method `blocks_since_last_update` improved. Currently it can be used to get historical data from archive node. Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index fb6395f253..3209013100 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -846,12 +846,22 @@ async def blocks_since_last_step( ) return query.value if query is not None and hasattr(query, "value") else query - async def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int]: + async def blocks_since_last_update( + self, + netuid: int, + uid: int, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, + ) -> Optional[int]: """Returns the number of blocks since the last update, or ``None`` if the subnetwork or UID does not exist. Parameters: netuid: The unique identifier of the subnetwork. uid: The unique identifier of the neuron. + block: The block number for this query. Do not specify if using block_hash or reuse_block. + block_hash: The hash of the block for the query. Do not specify if using reuse_block or block. + reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block. Returns: The number of blocks since the last update, or None if the subnetwork or UID does not exist. @@ -863,8 +873,16 @@ async def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int] # Check if neuron needs updating blocks_since_update = await subtensor.blocks_since_last_update(netuid=1, uid=10) """ - call = await self.get_hyperparameter(param_name="LastUpdate", netuid=netuid) - return None if call is None else await self.get_current_block() - int(call[uid]) + block_hash = await self.determine_block_hash(block, block_hash, reuse_block) + block = block or await self.substrate.get_block_number(block_hash) + call = await self.get_hyperparameter( + param_name="LastUpdate", + netuid=netuid, + block=block, + block_hash=block_hash, + reuse_block=reuse_block, + ) + return None if call is None else (block - int(call[uid])) async def bonds( self, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index a824273a22..d9ae0fc525 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -485,19 +485,25 @@ def blocks_since_last_step( ) return query.value if query is not None and hasattr(query, "value") else query - def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int]: + def blocks_since_last_update( + self, netuid: int, uid: int, block: Optional[int] = None + ) -> Optional[int]: """ Returns the number of blocks since the last update for a specific UID in the subnetwork. Parameters: netuid: The unique identifier of the subnetwork. uid: The unique identifier of the neuron. + block: the block number for this query. Returns: The number of blocks since the last update, or ``None`` if the subnetwork or UID does not exist. """ - call = self.get_hyperparameter(param_name="LastUpdate", netuid=netuid) - return None if not call else (self.get_current_block() - int(call[uid])) + block = block or self.get_current_block() + call = self.get_hyperparameter( + param_name="LastUpdate", netuid=netuid, block=block + ) + return None if not call else (block - int(call[uid])) def bonds( self, diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 3105e2af8d..c120544ba5 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2508,23 +2508,26 @@ async def test_blocks_since_last_update_success(subtensor, mocker): current_block = 100 fake_blocks_since_update = current_block - last_update_block + mocker.patch.object( + subtensor.substrate, "get_block_number", return_value=current_block, + ) mocked_get_hyperparameter = mocker.patch.object( subtensor, "get_hyperparameter", return_value={fake_uid: last_update_block}, ) - mocked_get_current_block = mocker.AsyncMock(return_value=current_block) - subtensor.get_current_block = mocked_get_current_block - # Call result = await subtensor.blocks_since_last_update(netuid=fake_netuid, uid=fake_uid) # Asserts mocked_get_hyperparameter.assert_called_once_with( - param_name="LastUpdate", netuid=fake_netuid + param_name="LastUpdate", + netuid=fake_netuid, + block=subtensor.substrate.get_block_number.return_value, + block_hash=None, + reuse_block=False, ) - mocked_get_current_block.assert_called_once() assert result == fake_blocks_since_update @@ -2543,11 +2546,18 @@ async def test_blocks_since_last_update_no_last_update(subtensor, mocker): ) # Call - result = await subtensor.blocks_since_last_update(netuid=fake_netuid, uid=fake_uid) + result = await subtensor.blocks_since_last_update( + netuid=fake_netuid, + uid=fake_uid, + ) # Asserts mocked_get_hyperparameter.assert_called_once_with( - param_name="LastUpdate", netuid=fake_netuid + param_name="LastUpdate", + netuid=fake_netuid, + block=subtensor.substrate.get_block_number.return_value, + block_hash=None, + reuse_block=False, ) assert result is None diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 86f00f8c61..2f92281cd2 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -368,7 +368,7 @@ def test_blocks_since_last_update_success_calls(subtensor, mocker): # Assertions mocked_get_current_block.assert_called_once() - mocked_get_hyperparameter.assert_called_once_with(param_name="LastUpdate", netuid=7) + mocked_get_hyperparameter.assert_called_once_with(param_name="LastUpdate", netuid=7, block=mocked_current_block) assert result == 1 # if we change the methods logic in the future we have to be make sure the returned type is correct assert isinstance(result, int) From 303448c7b9667efe4a060453f2dc1eb3acf2afbe Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 17:40:43 -0700 Subject: [PATCH 13/41] method `blocks_since_last_update` improved. Currently it can be used to get historical data from archive node. --- MIGRATION.md | 1 + bittensor/core/async_subtensor.py | 26 ++++++++++++++++++++---- tests/unit_tests/test_async_subtensor.py | 8 ++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index c11fb34aa9..20095fd196 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -238,6 +238,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - method `get_subnets` renamed to `get_all_subnets_netuid` (more obvious) - method `get_owned_hotkeys` get rid `reuse_block` parameter to be consistent with other sync methods. - method `blocks_since_last_update` improved. Currently it can be used to get historical data from archive node. +- methods (async) `get_subnet_validator_permits` and `get_subnet_owner_hotkey` got `block_hash` and `reuse_block` parameters. Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 3209013100..0d9cfcc881 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4121,7 +4121,11 @@ async def get_timestamp( return datetime.fromtimestamp(unix / 1000, tz=timezone.utc) async def get_subnet_owner_hotkey( - self, netuid: int, block: Optional[int] = None + self, + netuid: int, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, ) -> Optional[str]: """ Retrieves the hotkey of the subnet owner for a given network UID. @@ -4131,17 +4135,27 @@ async def get_subnet_owner_hotkey( Parameters: netuid: The network UID of the subnet to fetch the owner's hotkey for. - block: The specific block number to query the data from. + block: The blockchain block number for the query. + block_hash: The blockchain block_hash representation of the block id. + reuse_block: Whether to reuse the last-used blockchain block hash. Returns: The hotkey of the subnet owner if available; None otherwise. """ return await self.query_subtensor( - name="SubnetOwnerHotkey", params=[netuid], block=block + name="SubnetOwnerHotkey", + params=[netuid], + block=block, + block_hash=block_hash, + reuse_block=reuse_block, ) async def get_subnet_validator_permits( - self, netuid: int, block: Optional[int] = None + self, + netuid: int, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, ) -> Optional[list[bool]]: """ Retrieves the list of validator permits for a given subnet as boolean values. @@ -4149,6 +4163,8 @@ async def get_subnet_validator_permits( Parameters: netuid: The unique identifier of the subnetwork. block: The blockchain block number for the query. + block_hash: The blockchain block_hash representation of the block id. + reuse_block: Whether to reuse the last-used blockchain block hash. Returns: A list of boolean values representing validator permits, or None if not available. @@ -4157,6 +4173,8 @@ async def get_subnet_validator_permits( name="ValidatorPermit", params=[netuid], block=block, + block_hash=block_hash, + reuse_block=reuse_block, ) return query.value if query is not None and hasattr(query, "value") else query diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index c120544ba5..caebce8a1c 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3113,6 +3113,8 @@ async def test_get_subnet_owner_hotkey_has_return(subtensor, mocker): name="SubnetOwnerHotkey", block=block, params=[netuid], + block_hash=None, + reuse_block=False, ) assert result == expected_owner_hotkey @@ -3135,6 +3137,8 @@ async def test_get_subnet_owner_hotkey_is_none(subtensor, mocker): name="SubnetOwnerHotkey", block=block, params=[netuid], + block_hash=None, + reuse_block=False, ) assert result is None @@ -3158,6 +3162,8 @@ async def test_get_subnet_validator_permits_has_values(subtensor, mocker): name="ValidatorPermit", block=block, params=[netuid], + block_hash=None, + reuse_block=False, ) assert result == expected_validator_permits @@ -3181,6 +3187,8 @@ async def test_get_subnet_validator_permits_is_none(subtensor, mocker): name="ValidatorPermit", block=block, params=[netuid], + block_hash=None, + reuse_block=False, ) assert result is None From e7f277444789afd94da364b5c44fbfb7f1eb1f23 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 17:48:35 -0700 Subject: [PATCH 14/41] attribute `DelegateInfo/lite.total_daily_return` has been deleted (Vune confirmed that we shouldn't use it) --- MIGRATION.md | 1 + bittensor/core/chain_data/delegate_info.py | 6 ------ bittensor/core/chain_data/delegate_info_lite.py | 3 --- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 20095fd196..4776e2bd95 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -239,6 +239,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - method `get_owned_hotkeys` get rid `reuse_block` parameter to be consistent with other sync methods. - method `blocks_since_last_update` improved. Currently it can be used to get historical data from archive node. - methods (async) `get_subnet_validator_permits` and `get_subnet_owner_hotkey` got `block_hash` and `reuse_block` parameters. +- attribute `DelegateInfo/lite.total_daily_return` has been deleted (Vune confirmed that we shouldn't use it) Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` diff --git a/bittensor/core/chain_data/delegate_info.py b/bittensor/core/chain_data/delegate_info.py index cfc4e50654..91301b6034 100644 --- a/bittensor/core/chain_data/delegate_info.py +++ b/bittensor/core/chain_data/delegate_info.py @@ -18,7 +18,6 @@ class DelegateInfoBase(InfoBase): validator_permits: List of subnets that the delegate is allowed to validate on. registrations: List of subnets that the delegate is registered on. return_per_1000: Return per 1000 tao of the delegate over a day. - total_daily_return: Total daily return of the delegate. """ hotkey_ss58: str # Hotkey of delegate @@ -29,7 +28,6 @@ class DelegateInfoBase(InfoBase): ] # List of subnets that the delegate is allowed to validate on registrations: list[int] # list of subnets that the delegate is registered on return_per_1000: Balance # Return per 1000 tao of the delegate over a day - total_daily_return: Balance # Total daily return of the delegate @dataclass @@ -77,7 +75,6 @@ def _from_dict(cls, decoded: dict) -> Optional["DelegateInfo"]: validator_permits=list(decoded.get("validator_permits", [])), registrations=list(decoded.get("registrations", [])), return_per_1000=Balance.from_rao(decoded.get("return_per_1000")), - total_daily_return=Balance.from_rao(decoded.get("total_daily_return")), ) @@ -108,9 +105,6 @@ def _from_dict( validator_permits=list(delegate_info.get("validator_permits", [])), registrations=list(delegate_info.get("registrations", [])), return_per_1000=Balance.from_rao(delegate_info.get("return_per_1000")), - total_daily_return=Balance.from_rao( - delegate_info.get("total_daily_return") - ), netuid=int(netuid), stake=Balance.from_rao(int(stake)).set_unit(int(netuid)), ) diff --git a/bittensor/core/chain_data/delegate_info_lite.py b/bittensor/core/chain_data/delegate_info_lite.py index aa61f44abd..06666769dc 100644 --- a/bittensor/core/chain_data/delegate_info_lite.py +++ b/bittensor/core/chain_data/delegate_info_lite.py @@ -19,7 +19,6 @@ class DelegateInfoLite(InfoBase): registrations: List of subnets that the delegate is registered on. validator_permits: List of subnets that the delegate is allowed to validate on. return_per_1000: Return per 1000 TAO, for the delegate over a day. - total_daily_return: Total daily return of the delegate. """ delegate_ss58: str # Hotkey of delegate @@ -31,7 +30,6 @@ class DelegateInfoLite(InfoBase): int ] # List of subnets that the delegate is allowed to validate on return_per_1000: Balance # Return per 1000 tao for the delegate over a day - total_daily_return: Balance # Total daily return of the delegate @classmethod def _from_dict(cls, decoded: dict) -> "DelegateInfoLite": @@ -43,5 +41,4 @@ def _from_dict(cls, decoded: dict) -> "DelegateInfoLite": registrations=decoded["registrations"], validator_permits=decoded["validator_permits"], return_per_1000=Balance.from_rao(decoded["return_per_1000"]), - total_daily_return=Balance.from_rao(decoded["total_daily_return"]), ) From ed553ee95f23428e4933da464d09d769ec3b881d Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 17:48:48 -0700 Subject: [PATCH 15/41] fix integration tests --- tests/helpers/integration_websocket_data.py | 47 ++++++++++--------- .../test_subtensor_integration.py | 2 +- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/tests/helpers/integration_websocket_data.py b/tests/helpers/integration_websocket_data.py index c90b75ad48..3bbf359819 100644 --- a/tests/helpers/integration_websocket_data.py +++ b/tests/helpers/integration_websocket_data.py @@ -9,48 +9,48 @@ "blocks_since_last_update": { "chain_getHead": { "[]": { - "result": "0xf658002393b00476606e6b6e1eb8c0a2979e53eacb8318b4b5f73fa73ff20398" + "result": "0x2830271e820b419253b79cbafea83f726354bbef26eb318835ffa774288de9ff" } }, "chain_getHeader": { - '["0xf658002393b00476606e6b6e1eb8c0a2979e53eacb8318b4b5f73fa73ff20398"]': { + '["0x2830271e820b419253b79cbafea83f726354bbef26eb318835ffa774288de9ff"]': { "result": { - "parentHash": "0xf620bf7b416a49079f0817a15fe5b1243bb5200416bf1b6c7934114551504517", - "number": "0x63856c", - "stateRoot": "0xb05a9a4ab12adda8c91d63242f9925da3735e5fa79b31094d2c61a6ac85db9a4", - "extrinsicsRoot": "0xedf4302417e5be22e2eff506e624db7807e5c98f1e4941d31d4a4e54527222ec", + "parentHash": "0xb5a3b72cc914b520a12e45e9f73fc3076b86fbae1be20b191fb7da4967c6ac47", + "number": "0x63fe87", + "stateRoot": "0x800d3090504b36e496fc579edf9db972a9cbc7522d52d96d4804e3396b21be7d", + "extrinsicsRoot": "0xe3ccef094f2e6176bc4a2d2f42a2d0b0c5f815c8506cf4299c8390c0eac62d39", "digest": { "logs": [ - "0x066175726120ec74bc0800000000", - "0x0466726f6e88011d87918002e1c26ed6d9a77885b14f77e4077cd9188cf398549cc978a295ad2b00", - "0x05617572610101961dd0050612d51f0aab580ee3ca9c7441ac91a7055716c1db496c418fc9d8431f51fe358b2833d613e6f69d5d783a0e89438511bc848a43845e8daa3d4ed980", + "0x06617572612007eebc0800000000", + "0x0466726f6e8801e4783b7f433d5462d1b84cfc851cbcf2fe886103b02fab1cce30f82cdaf0d37000", + "0x056175726101015e77e14c4e02e720bbd213a708dac67de5ea3494b41b6a87f03eee2d2d71557f2c960bd462e14433e758c6fe2584abec9b22b0d550bd25c76f836b413c526b8d", ] }, } }, "[null]": { "result": { - "parentHash": "0xf620bf7b416a49079f0817a15fe5b1243bb5200416bf1b6c7934114551504517", - "number": "0x63856c", - "stateRoot": "0xb05a9a4ab12adda8c91d63242f9925da3735e5fa79b31094d2c61a6ac85db9a4", - "extrinsicsRoot": "0xedf4302417e5be22e2eff506e624db7807e5c98f1e4941d31d4a4e54527222ec", + "parentHash": "0xb5a3b72cc914b520a12e45e9f73fc3076b86fbae1be20b191fb7da4967c6ac47", + "number": "0x63fe87", + "stateRoot": "0x800d3090504b36e496fc579edf9db972a9cbc7522d52d96d4804e3396b21be7d", + "extrinsicsRoot": "0xe3ccef094f2e6176bc4a2d2f42a2d0b0c5f815c8506cf4299c8390c0eac62d39", "digest": { "logs": [ - "0x066175726120ec74bc0800000000", - "0x0466726f6e88011d87918002e1c26ed6d9a77885b14f77e4077cd9188cf398549cc978a295ad2b00", - "0x05617572610101961dd0050612d51f0aab580ee3ca9c7441ac91a7055716c1db496c418fc9d8431f51fe358b2833d613e6f69d5d783a0e89438511bc848a43845e8daa3d4ed980", + "0x06617572612007eebc0800000000", + "0x0466726f6e8801e4783b7f433d5462d1b84cfc851cbcf2fe886103b02fab1cce30f82cdaf0d37000", + "0x056175726101015e77e14c4e02e720bbd213a708dac67de5ea3494b41b6a87f03eee2d2d71557f2c960bd462e14433e758c6fe2584abec9b22b0d550bd25c76f836b413c526b8d", ] }, } }, }, "state_getRuntimeVersion": { - '["0xf620bf7b416a49079f0817a15fe5b1243bb5200416bf1b6c7934114551504517"]': { + '["0xb5a3b72cc914b520a12e45e9f73fc3076b86fbae1be20b191fb7da4967c6ac47"]': { "result": { "specName": "node-subtensor", "implName": "node-subtensor", "authoringVersion": 1, - "specVersion": 315, + "specVersion": 320, "implVersion": 1, "apis": [ ["0xdf6acb689907609b", 5], @@ -81,12 +81,17 @@ } } }, + "chain_getBlockHash": { + "[6553223]": { + "result": "0x2830271e820b419253b79cbafea83f726354bbef26eb318835ffa774288de9ff" + } + }, "state_getStorageAt": { - '["0x658faa385070e074c85bf6b568cf05550e30450fc4d507a846032a7fa65d9a430100", null]': { + '["0x658faa385070e074c85bf6b568cf05550e30450fc4d507a846032a7fa65d9a430100", "0x2830271e820b419253b79cbafea83f726354bbef26eb318835ffa774288de9ff"]': { "result": "0x01" }, - '["0x658faa385070e074c85bf6b568cf0555696e262a16e52255a69d8acd793541460100", null]': { - "result": "0x0110a1cf2600000000009bc7260000000000b6fc2d0000000000b43c40000000000036b9260000000000e9445e00000000004ec5330000000000a2d02600000000008dc726000000000057d02600000000002ac826000000000069ca260000000000988a470000000000a8812600000000009cce26000000000080d026000000000057ca26000000000060cb26000000000059ce260000000000a9cc2600000000003ad02600000000007fd0260000000000c5c9260000000000fccf260000000000218563000000000011c726000000000015be26000000000039cf260000000000a3d02600000000007bb226000000000057ce2600000000006783260000000000bb093b0000000000b3d026000000000094ce2600000000004a8f2500000000000cc726000000000095c626000000000013b9260000000000fec926000000000070d026000000000078cf26000000000028d026000000000052cb2600000000004aaa240000000000a4c526000000000046ce260000000000c5cd26000000000004ce260000000000f2cc260000000000bcb72b00000000006acc260000000000c7cc260000000000d11c2600000000000dcf2600000000001fcb26000000000000cf26000000000030c1260000000000f3cd2600000000004dd0260000000000ea134b0000000000323d300000000000afcf26000000000016b426000000000087134b000000000094d026000000000012cc2600000000009dc3260000000000c9c3260000000000ecba260000000000f9c6260000000000e0c8260000000000218f25000000000051c8260000000000d2ce2600000000005dce280000000000a2ca260000000000f0cc26000000000089bf260000000000d8cb260000000000ba2526000000000076ab260000000000f8c9260000000000cdc8260000000000b3b32600000000005ed0260000000000f2b4260000000000a4cc26000000000016c926000000000019c6260000000000f1cc260000000000b3d0260000000000f4cc2600000000008db62600000000004fcf2600000000002bc52600000000009a27260000000000848726000000000020c4260000000000ebcb260000000000ea0d290000000000edc4260000000000a2c026000000000078c6260000000000111b2600000000009fc226000000000091d026000000000036c726000000000014c6290000000000a1cc260000000000dfa5260000000000ebc8260000000000ff782600000000001c8f250000000000fe6d260000000000aec0260000000000f5ca260000000000d7cb260000000000a3bd2600000000005dc82600000000009485260000000000c87826000000000090c72600000000002acf260000000000b2c626000000000030ba26000000000010c926000000000035c2260000000000f1c92600000000003acc26000000000095d02600000000001cab2600000000000fce2600000000000fce260000000000bdcc2600000000008fcc260000000000b45f3d000000000085ce26000000000056c326000000000053ce260000000000aec526000000000021bc260000000000acc82600000000009bd02600000000005ad026000000000038c426000000000019c3260000000000e1cd260000000000b0ce26000000000034c526000000000020be2600000000000b7d320000000000aecd26000000000048c6260000000000c1cf26000000000063d026000000000003cd260000000000ddc62600000000000d2b280000000000b3aa2400000000001ac82600000000001c522600000000008ac726000000000042ce2600000000004fce260000000000aabf26000000000017cc2600000000009e753300000000009cce2600000000008ecc26000000000009ca26000000000044cd26000000000024d02600000000009fbb260000000000a7c72600000000009cc6260000000000a6d026000000000061d0260000000000eac326000000000084b526000000000086cd2600000000000cc2260000000000d9c62600000000003ec32600000000002ec8260000000000c3cb26000000000085cf260000000000d7ce2600000000009dc226000000000033cc26000000000037ca26000000000055ce26000000000085ec44000000000095d0260000000000c9c6260000000000b7ca260000000000fcbd26000000000024d026000000000075bb260000000000e4b7260000000000087d32000000000020cf26000000000019ce26000000000054e644000000000022c62600000000009cc72600000000009dd0260000000000c8af4e000000000062ce260000000000019e26000000000049cc260000000000abcb260000000000c05026000000000066cb260000000000adcf260000000000aa9626000000000072cd2600000000003dc226000000000040573b00000000000dca260000000000adcc2600000000006bd0260000000000accc260000000000ccc9260000000000638f250000000000ea7c2600000000008b4a290000000000adcd26000000000061c126000000000079cf2600000000009dc5260000000000cece2600000000004dcd260000000000b2d026000000000014cc26000000000004ce2600000000003cce26000000000075cf260000000000d2ba2600000000000ecb2600000000009093260000000000a1c526000000000028ce2600000000007bd026000000000088aa2400000000001f85630000000000acc926000000000008ca260000000000cfc926000000000069cc260000000000c4c42600000000001bd02600000000005ed0260000000000a4cc2600000000008ace26000000000005bc260000000000d5c7260000000000f5c02600000000001dd0260000000000cccb2600000000006bcc260000000000cfce26000000000043ca260000000000b9ca26000000000054c226000000000015cf26000000000005f6250000000000c8cb4000000000009f93260000000000bacd260000000000ecb1260000000000c6c6260000000000f1cd2600000000000ec026000000000052cd2600000000000ed02600000000009ccd260000000000dac52600000000002ec226000000000088d0260000000000eccd26000000000008cf260000000000a6ce26000000000006c5260000000000e6c12600000000002fc7260000000000a9d026000000000036d02600000000009ac826000000000061cf260000000000aac826000000000028c3260000000000029f2600000000000cd02600000000009733260000000000692b3400000000007dc326000000000064cf2600000000001ac5260000000000a86d260000000000f9c926000000000019d02600000000009ec7260000000000a9f52400000000000dc6260000000000e6cf260000000000edce26000000000016c42600000000009bf52400000000009888260000000000acaa240000000000b2cd26000000000011cd260000000000ffba260000000000f2c9260000000000a6073c0000000000bf3d26000000000077aa240000000000dc94260000000000cbf5240000000000a6d02600000000005fc5260000000000b273260000000000b7b62600000000003cc6260000000000b3c72600000000001ecc260000000000669e260000000000cacf26000000000039ca2600000000006ecd260000000000f9a7260000000000eb3326000000000020c4260000000000a9cb26000000000076cf26000000000067cc26000000000023f02900000000008bd0260000000000bfc9260000000000c4c72600000000001fd0260000000000a21026000000000097c72600000000006e9a2600000000007e932600000000009acf26000000000076ce260000000000819226000000000014ca2600000000008ecb260000000000becb2600000000000acc2600000000003b453a000000000039cf260000000000e5772600000000009fcb260000000000bf344e00000000005ace26000000000077d0260000000000f4bf260000000000cbcf260000000000e0cf260000000000e8ea2b000000000084ce26000000000012c9260000000000f2c7260000000000a5d0260000000000b9c926000000000018c826000000000093592600000000009f8a260000000000afd0260000000000a9c726000000000091ce26000000000000cd26000000000045aa2400000000006acc260000000000da9f6100000000001acf260000000000a1ac25000000000059cf260000000000ceca260000000000bc2726000000000028aa26000000000072c4260000000000ac7626000000000084c726000000000057c426000000000073c2260000000000389e260000000000ff8b26000000000051d02600000000000ec5260000000000428f25000000000029c42600000000003d8f25000000000048ca26000000000098c8260000000000f896260000000000ffb7260000000000c1cc26000000000018ca260000000000f0c42600000000006993260000000000b0cc260000000000a5c726000000000031cf26000000000081cf2600000000003c73270000000000edc02600000000001ac426000000000000cc26000000000022ce26000000000014cd260000000000e45226000000000010cf26000000000002cf260000000000d9cb26000000000075cb2600000000005dd02600000000003193260000000000a1ca26000000000056cd260000000000cccb2600000000000cc826000000000096d02600000000009fd02600000000004ec526000000000026d026000000000081d026000000000078cf260000000000becf2600000000009ace26000000000042d026000000000060c326000000000097cf26000000000088cd2600000000004b3d260000000000c8cf2600000000000abd2600000000001dcc2600000000007bd02600000000008bb726000000000011c526000000000059d02600000000009078260000000000fbcb26000000000055c3260000000000e6c62600000000009bd0260000000000f8cb2600000000000a7926000000000055cd260000000000d8cd260000000000a4cd260000000000afcc260000000000eac826000000000036cf26000000000053ce2600000000009cca260000000000f0d92a000000000054ce26000000000021c426000000000030cd26000000000020cc26000000000041c226000000000052cc260000000000f2ce260000000000f87626000000000094b92600000000004ec8260000000000e5ca260000000000e5bb260000000000bdce2600000000000dcd26000000000058d026000000000018c726000000000040cf260000000000228f2500000000007eca2600000000007bcd26000000000078b926000000000088d026000000000016ca260000000000fcb326000000000082cb260000000000d5cc2600000000004dc1260000000000bbc9260000000000c9cf2600000000005dd026000000000053cd2600000000000ace260000000000b7ae3100000000005acf260000000000dfca260000000000e5cf2600000000002dc72600000000008fc626000000000024f6250000000000f4c92600000000002b856300000000001fc3260000000000cfbe260000000000decf2600000000006ece26000000000022a02600000000003cc3260000000000b4cd26000000000078512600000000004fcd26000000000097d02600000000004dcc26000000000051cf260000000000438f2600000000004bc626000000000049ca2600000000005f856300000000007f3b61000000000063b2560000000000b6c92600000000008ebb2600000000004bce260000000000f7cd2600000000002ccd260000000000f0cf260000000000c7cd26000000000004c526000000000098cc2600000000004dd02600000000003dbd2600000000006ecd26000000000029cf260000000000b9c326000000000081be260000000000f6ce26000000000056c826000000000096cc2600000000001b3d30000000000061d026000000000002b8260000000000ff90260000000000c1c42600000000002ccc260000000000d0c926000000000035c6260000000000eccb26000000000061cc26000000000072cc260000000000eccd2600000000005fcc2600000000004dd026000000000091cc2600000000009ec0260000000000049e26000000000060cd26000000000094d02600000000006dce260000000000b8cf370000000000bbca26000000000051c326000000000068ca260000000000f6852600000000002f61490000000000cccc26000000000065b926000000000061cc260000000000b4aa2400000000002fcd260000000000b23c30000000000002c7260000000000c78926000000000004be260000000000ffcc2600000000004bb83400000000008cc72600000000003ed0260000000000b5cb2600000000005a474000000000007dc32600000000001ec92600000000002ad0260000000000d8cd260000000000e4cb260000000000ac95260000000000cbcf26000000000002c82600000000009dd0260000000000a683260000000000bdc8260000000000bf092600000000009f7b2600000000000bce260000000000241526000000000097ce260000000000accc260000000000d3ee3900000000006bcd2600000000002b4f26000000000091c62600000000003f8f25000000000044c82600000000001ecb260000000000a9d026000000000050c826000000000051ce260000000000bbdd4200000000006b8226000000000089cb6100000000008bd0260000000000b2cd26000000000076cb260000000000f9cc26000000000029d02600000000007bc7260000000000fbcd2600000000007fca26000000000020b72400000000005bc526000000000016cf26000000000086be26000000000045d0260000000000c3cb26000000000092c4260000000000f8cf26000000000066cd260000000000d9cf260000000000c1873300000000002dc92600000000005fca26000000000082c72600000000003cc72600000000002dd02600000000002a85630000000000aff5240000000000c58226000000000097ca26000000000038cf260000000000937426000000000002ca26000000000022c6260000000000d0cf26000000000080cb26000000000029cd260000000000afcf26000000000034ca260000000000e0c72600000000002cce260000000000bacd26000000000065c82600000000000bcf2600000000009bcc260000000000454e260000000000f0cd2600000000003fcc26000000000004cc26000000000051d026000000000008c126000000000031ce26000000000022be260000000000cacf260000000000cb772600000000009c3e2b000000000076aa240000000000facb2600000000009bc326000000000059b7260000000000b1ca26000000000039cd260000000000aeb42600000000007ed02600000000002bd026000000000018cb260000000000010c590000000000decd260000000000c7c726000000000046d0260000000000b5c8260000000000facc26000000000058c826000000000012b626000000000094cd260000000000d5b92600000000003ed026000000000087d026000000000086d02600000000000fc0260000000000a1c726000000000063ce2600000000001cc02600000000007fc5260000000000e9cd2600000000004ed0260000000000a0b2260000000000aaca260000000000357a260000000000658563000000000048cd260000000000f5f52400000000004dc72600000000006ac426000000000098c226000000000043cc2600000000006293260000000000a1c2260000000000cfc426000000000068af260000000000f2b9260000000000a1d0260000000000ccc9260000000000f0cd26000000000072cd2600000000005fbf2600000000005ebf2600000000005ecf2600000000006e4d2600000000006e0b31000000000011bb26000000000002be26000000000041ca260000000000b6cd260000000000c0c42600000000007ece26000000000081cc260000000000deb12600000000008ccf26000000000063cc260000000000cfad2600000000009cd02600000000003dd026000000000045c9260000000000e4cb2600000000008d8b26000000000040d026000000000069d0260000000000d047260000000000fbcb26000000000021c7260000000000c9ce26000000000012b9260000000000aec7260000000000d2cc2600000000008bc4260000000000deee330000000000962726000000000056c226000000000099cf2600000000008e8f250000000000cbcb26000000000090c2260000000000adcd26000000000094a1260000000000d3b42600000000009cca260000000000ddf524000000000023182f0000000000b04026000000000064d0260000000000a5d026000000000069ca26000000000008ce2600000000000dce26000000000054cc2600000000009dcf26000000000043c5260000000000b9cd2600000000007dd02600000000005ad026000000000086d02600000000003fd0260000000000fd63260000000000228f2500000000004dd0260000000000bdcf260000000000a2cb260000000000c0c92600000000002ccf260000000000d19244000000000015d0260000000000becf26000000000008c72600000000000cd0260000000000abc3260000000000cdbc260000000000a7c82600000000007ac826000000000004792600000000000e9e490000000000e5be2600000000001f83280000000000bbbc2600000000007cd02600000000007dd0260000000000bccd26000000000006c8260000000000fac7260000000000f1c2260000000000f3ce26000000000067c726000000000068445d0000000000ecce260000000000d3c9260000000000e0c226000000000029cf2600000000000ccd260000000000becf26000000000048cb2600000000006186280000000000a7d02600000000007bc9260000000000dadf530000000000b3d026000000000083cf260000000000b1cf26000000000060424b0000000000eacb260000000000fbc826000000000045c3260000000000cdcf260000000000e1ce2600000000002fcb2600000000004dc426000000000095cd26000000000087362700000000004bcb260000000000afc4260000000000f5c626000000000038cd260000000000f9ce260000000000f6733a0000000000cbc9260000000000b4cd260000000000b4cb26000000000014cc26000000000000cf260000000000aac4260000000000ddc72600000000002efc340000000000b7b12600000000008479260000000000f3cf260000000000b5bd2600000000005ace260000000000d6c7260000000000e0c226000000000050ca260000000000f3c3260000000000f18e2600000000000bcc260000000000232e2600000000004b8f2500000000008fc726000000000096cc26000000000004cf2600000000006fc926000000000094ca26000000000024ac26000000000008cb260000000000d2c7260000000000c1cf26000000000079cc260000000000313d30000000000029ce2600000000006cc9260000000000c8cc260000000000e1c626000000000049cb260000000000b1ce260000000000afc5260000000000e1c6260000000000b3c8260000000000fbce2600000000009e733600000000000dcf26000000000079c826000000000030c5260000000000a7aa2400000000006cd026000000000098c6260000000000a4c4260000000000e9ce260000000000d3ce26000000000069cd260000000000f5c526000000000088cd260000000000d8cd260000000000c2cd260000000000df7b260000000000afc8260000000000943f2600000000000253280000000000dbcd2600000000006d7b260000000000dec7260000000000858126000000000073b526000000000025c4260000000000f0bc2600000000000dc126000000000087b726000000000002bd260000000000c7ce26000000000044c4260000000000d5cf260000000000aacd260000000000c5c826000000000028c126000000000078c52600000000004eef5400000000003ccf260000000000ef32300000000000c25145000000000067b52600000000009dc3260000000000def5240000000000e2ce260000000000108126000000000073c5260000000000ebcd26000000000034c8260000000000d2c12600000000006bcc260000000000f69c26000000000026c9260000000000909426000000000021cc260000000000ffc2260000000000fec72600000000007ecc260000000000adc7260000000000773e4000000000009ac226000000000063c5260000000000f7c3260000000000d0cf260000000000c4d9390000000000ea492900000000007acb26000000000017cb2600000000006abd260000000000eec4260000000000a2c726000000000008d026000000000099c826000000000022c72600000000009fca2600000000004ecc2600000000004cd026000000000007d026000000000000ca2600000000008dd026000000000055c226000000000091d02600000000004184260000000000e2cb26000000000006be2600000000002079260000000000cdc72600000000004ac42600000000003fc426000000000040a73e000000000070aa240000000000d3bd26000000000045ce260000000000bbf5240000000000328563000000000002ce260000000000bdcb2600000000007eaa24000000000059c226000000000061bc260000000000f6cd260000000000d6d93400000000005210340000000000f74a31000000000013c8260000000000fd8f260000000000bbc0260000000000a6d02600000000001dcd26000000000019ca260000000000e9cb26000000000068ce26000000000007bc260000000000b6cc260000000000e9ce260000000000cacf26000000000013cc260000000000a319260000000000b174260000000000b9cc260000000000b3cb260000000000d28463000000000048cd26000000000055b826000000000089c726000000000070d02600000000004ccc260000000000bac9260000000000a9d0260000000000c2cf26000000000044cc260000000000b9ee4b000000000098bb260000000000aacc26000000000062ca2600000000009163250000000000a1b42600000000008dcc2600000000008ace260000000000b9ce2600000000008bcd2600000000002bc22600000000005ed0260000000000fdc8260000000000ecc72600000000000dcd26000000000071cb26000000000056cd260000000000a9ca26000000000064c92600000000005a902d00000000002dcf260000000000" + '["0x658faa385070e074c85bf6b568cf0555696e262a16e52255a69d8acd793541460100", "0x2830271e820b419253b79cbafea83f726354bbef26eb318835ffa774288de9ff"]': { + "result": "0x0110a1cf2600000000009bc7260000000000b6fc2d0000000000b43c40000000000036b9260000000000e9445e00000000004ec5330000000000a2d02600000000008dc726000000000057d02600000000002ac826000000000069ca260000000000988a470000000000a8812600000000009cce26000000000080d026000000000057ca26000000000060cb26000000000059ce260000000000a9cc2600000000003ad02600000000007fd0260000000000c5c9260000000000fccf2600000000000efe63000000000011c726000000000015be26000000000039cf260000000000a3d02600000000007bb226000000000057ce2600000000006783260000000000bb093b0000000000b3d026000000000094ce2600000000004a8f2500000000000cc726000000000095c626000000000013b9260000000000fec926000000000070d026000000000078cf26000000000028d026000000000052cb2600000000004aaa240000000000a4c526000000000046ce260000000000c5cd26000000000004ce260000000000f2cc260000000000bcb72b00000000006acc260000000000c7cc260000000000d11c2600000000000dcf2600000000001fcb26000000000000cf26000000000030c1260000000000f3cd2600000000004dd0260000000000ea134b0000000000323d300000000000afcf26000000000016b426000000000087134b000000000094d026000000000012cc2600000000009dc3260000000000c9c3260000000000ecba260000000000f9c6260000000000e0c8260000000000218f25000000000051c8260000000000d2ce2600000000005dce280000000000a2ca260000000000f0cc26000000000089bf260000000000d8cb260000000000ba2526000000000076ab260000000000f8c9260000000000cdc8260000000000b3b32600000000005ed0260000000000f2b4260000000000a4cc26000000000016c926000000000019c6260000000000f1cc260000000000b3d0260000000000f4cc2600000000008db62600000000004fcf2600000000002bc52600000000009a27260000000000848726000000000020c4260000000000ebcb260000000000ea0d290000000000edc4260000000000a2c026000000000078c6260000000000111b2600000000009fc226000000000091d026000000000036c726000000000014c6290000000000a1cc260000000000dfa5260000000000ebc8260000000000ff782600000000001c8f250000000000fe6d260000000000aec0260000000000f5ca260000000000d7cb260000000000a3bd2600000000005dc82600000000009485260000000000c87826000000000090c72600000000002acf260000000000b2c626000000000030ba26000000000010c926000000000035c2260000000000f1c92600000000003acc26000000000095d02600000000001cab2600000000000fce2600000000000fce260000000000bdcc2600000000008fcc260000000000b45f3d000000000085ce26000000000056c326000000000053ce260000000000aec526000000000021bc260000000000acc82600000000009bd02600000000005ad026000000000038c426000000000019c3260000000000e1cd260000000000b0ce26000000000034c526000000000020be2600000000000b7d320000000000aecd26000000000048c6260000000000c1cf26000000000063d026000000000003cd260000000000ddc62600000000000d2b280000000000b3aa2400000000001ac82600000000001c522600000000008ac726000000000042ce2600000000004fce260000000000aabf26000000000017cc2600000000009e753300000000009cce2600000000008ecc26000000000009ca26000000000044cd26000000000024d02600000000009fbb260000000000a7c72600000000009cc6260000000000a6d026000000000061d0260000000000eac326000000000084b526000000000086cd2600000000000cc2260000000000d9c62600000000003ec32600000000002ec8260000000000c3cb26000000000085cf260000000000d7ce2600000000009dc226000000000033cc26000000000037ca26000000000055ce26000000000085ec44000000000095d0260000000000c9c6260000000000b7ca260000000000fcbd26000000000024d026000000000075bb260000000000e4b7260000000000087d32000000000020cf26000000000019ce26000000000054e644000000000022c62600000000009cc72600000000009dd0260000000000c8af4e000000000062ce260000000000019e26000000000049cc260000000000abcb260000000000c05026000000000066cb260000000000adcf260000000000aa9626000000000072cd2600000000003dc226000000000040573b00000000000dca260000000000adcc2600000000006bd0260000000000accc260000000000ccc9260000000000638f250000000000ea7c2600000000008b4a290000000000adcd26000000000061c126000000000079cf2600000000009dc5260000000000cece2600000000004dcd260000000000b2d026000000000014cc26000000000004ce2600000000003cce26000000000075cf260000000000d2ba2600000000000ecb2600000000009093260000000000a1c526000000000028ce2600000000007bd026000000000088aa2400000000001bfe630000000000acc926000000000008ca260000000000cfc926000000000069cc260000000000c4c42600000000001bd02600000000005ed0260000000000a4cc2600000000008ace26000000000005bc260000000000d5c7260000000000f5c02600000000001dd0260000000000cccb2600000000006bcc260000000000cfce26000000000043ca260000000000b9ca26000000000054c226000000000015cf26000000000005f6250000000000c8cb4000000000009f93260000000000bacd260000000000ecb1260000000000c6c6260000000000f1cd2600000000000ec026000000000052cd2600000000000ed02600000000009ccd260000000000dac52600000000002ec226000000000088d0260000000000eccd26000000000008cf260000000000a6ce26000000000006c5260000000000e6c12600000000002fc7260000000000a9d026000000000036d02600000000009ac826000000000061cf260000000000aac826000000000028c3260000000000029f2600000000000cd02600000000009733260000000000692b3400000000007dc326000000000064cf2600000000001ac5260000000000a86d260000000000f9c926000000000019d02600000000009ec7260000000000a9f52400000000000dc6260000000000e6cf260000000000edce26000000000016c42600000000009bf52400000000009888260000000000acaa240000000000b2cd26000000000011cd260000000000ffba260000000000f2c9260000000000a6073c0000000000bf3d26000000000077aa240000000000dc94260000000000cbf5240000000000a6d02600000000005fc5260000000000b273260000000000b7b62600000000003cc6260000000000b3c72600000000001ecc260000000000669e260000000000cacf26000000000039ca2600000000006ecd260000000000f9a7260000000000eb3326000000000020c4260000000000a9cb26000000000076cf26000000000067cc26000000000023f02900000000008bd0260000000000bfc9260000000000c4c72600000000001fd0260000000000a21026000000000097c72600000000006e9a2600000000007e932600000000009acf26000000000076ce260000000000819226000000000014ca2600000000008ecb260000000000becb2600000000000acc2600000000003b453a000000000039cf260000000000e5772600000000009fcb260000000000bf344e00000000005ace26000000000077d0260000000000f4bf260000000000cbcf260000000000e0cf260000000000e8ea2b000000000084ce26000000000012c9260000000000f2c7260000000000a5d0260000000000b9c926000000000018c826000000000093592600000000009f8a260000000000afd0260000000000a9c726000000000091ce26000000000000cd26000000000045aa2400000000006acc260000000000da9f6100000000001acf260000000000a1ac25000000000059cf260000000000ceca260000000000bc2726000000000028aa26000000000072c4260000000000ac7626000000000084c726000000000057c426000000000073c2260000000000389e260000000000ff8b26000000000051d02600000000000ec5260000000000428f25000000000029c42600000000003d8f25000000000048ca26000000000098c8260000000000f896260000000000ffb7260000000000c1cc26000000000018ca260000000000f0c42600000000006993260000000000b0cc260000000000a5c726000000000031cf26000000000081cf2600000000003c73270000000000edc02600000000001ac426000000000000cc26000000000022ce26000000000014cd260000000000e45226000000000010cf26000000000002cf260000000000d9cb26000000000075cb2600000000005dd02600000000003193260000000000a1ca26000000000056cd260000000000cccb2600000000000cc826000000000096d02600000000009fd02600000000004ec526000000000026d026000000000081d026000000000078cf260000000000becf2600000000009ace26000000000042d026000000000060c326000000000097cf26000000000088cd2600000000004b3d260000000000c8cf2600000000000abd2600000000001dcc2600000000007bd02600000000008bb726000000000011c526000000000059d0260000000000907826000000000017fe63000000000055c3260000000000e6c62600000000009bd0260000000000f8cb2600000000000a7926000000000055cd260000000000d8cd260000000000a4cd260000000000afcc260000000000eac826000000000036cf26000000000053ce2600000000009cca260000000000f0d92a000000000054ce26000000000021c426000000000030cd26000000000020cc26000000000041c226000000000052cc260000000000f2ce260000000000f87626000000000094b92600000000004ec8260000000000e5ca260000000000e5bb260000000000bdce2600000000000dcd26000000000058d026000000000018c726000000000040cf260000000000228f2500000000007eca2600000000007bcd26000000000078b926000000000088d026000000000016ca260000000000fcb326000000000082cb260000000000d5cc2600000000004dc1260000000000bbc9260000000000c9cf2600000000005dd026000000000053cd2600000000000ace260000000000b7ae3100000000005acf260000000000dfca260000000000e5cf2600000000002dc72600000000008fc626000000000024f6250000000000f4c926000000000026fe6300000000001fc3260000000000cfbe260000000000decf2600000000006ece26000000000022a02600000000003cc3260000000000b4cd26000000000078512600000000004fcd26000000000097d02600000000004dcc26000000000051cf260000000000438f2600000000004bc626000000000049ca26000000000037fe6300000000007f3b61000000000063b2560000000000b6c92600000000008ebb2600000000004bce260000000000f7cd2600000000002ccd260000000000f0cf260000000000c7cd26000000000004c526000000000098cc2600000000004dd02600000000003dbd2600000000006ecd26000000000029cf260000000000b9c326000000000081be260000000000f6ce26000000000056c826000000000096cc2600000000001b3d30000000000061d026000000000002b8260000000000ff90260000000000c1c42600000000002ccc260000000000d0c926000000000035c6260000000000eccb26000000000061cc26000000000072cc260000000000eccd2600000000005fcc2600000000004dd026000000000091cc2600000000009ec0260000000000049e26000000000060cd26000000000094d02600000000006dce260000000000b8cf370000000000bbca26000000000051c326000000000068ca260000000000f6852600000000002f61490000000000cccc26000000000065b926000000000061cc260000000000b4aa2400000000002fcd260000000000b23c30000000000002c7260000000000c78926000000000004be260000000000ffcc2600000000004bb83400000000008cc72600000000003ed0260000000000b5cb2600000000005a474000000000007dc32600000000001ec92600000000002ad0260000000000d8cd260000000000e4cb260000000000ac95260000000000cbcf26000000000002c82600000000009dd0260000000000a683260000000000bdc8260000000000bf092600000000009f7b2600000000000bce260000000000241526000000000097ce260000000000accc260000000000d3ee3900000000006bcd2600000000002b4f26000000000091c62600000000003f8f25000000000044c82600000000001ecb260000000000a9d026000000000050c826000000000051ce260000000000bbdd4200000000006b8226000000000089cb6100000000008bd0260000000000b2cd26000000000076cb260000000000f9cc26000000000029d02600000000007bc7260000000000fbcd2600000000007fca26000000000020b72400000000005bc526000000000016cf26000000000086be26000000000045d0260000000000c3cb26000000000092c4260000000000f8cf26000000000066cd260000000000d9cf260000000000c1873300000000002dc92600000000005fca26000000000082c72600000000003cc72600000000002dd02600000000009d85630000000000aff5240000000000c58226000000000097ca26000000000038cf260000000000937426000000000002ca26000000000022c6260000000000d0cf26000000000080cb26000000000029cd260000000000afcf26000000000034ca260000000000e0c72600000000002cce260000000000bacd26000000000065c82600000000000bcf2600000000009bcc260000000000454e260000000000f0cd2600000000003fcc26000000000004cc26000000000051d026000000000008c126000000000031ce26000000000022be260000000000cacf260000000000cb772600000000009c3e2b000000000076aa240000000000facb2600000000009bc326000000000059b7260000000000b1ca26000000000039cd260000000000aeb42600000000007ed02600000000002bd026000000000018cb260000000000010c590000000000decd260000000000c7c726000000000046d0260000000000b5c8260000000000facc26000000000058c826000000000012b626000000000094cd260000000000d5b92600000000003ed026000000000087d026000000000086d02600000000000fc0260000000000a1c726000000000063ce2600000000001cc02600000000007fc5260000000000e9cd2600000000004ed0260000000000a0b2260000000000aaca260000000000357a26000000000019fe63000000000048cd260000000000f5f52400000000004dc72600000000006ac426000000000098c226000000000043cc2600000000006293260000000000a1c2260000000000cfc426000000000068af260000000000f2b9260000000000a1d0260000000000ccc9260000000000f0cd26000000000072cd2600000000005fbf2600000000005ebf2600000000005ecf2600000000006e4d2600000000006e0b31000000000011bb26000000000002be26000000000041ca260000000000b6cd260000000000c0c42600000000007ece26000000000081cc260000000000deb12600000000008ccf26000000000063cc260000000000cfad2600000000009cd02600000000003dd026000000000045c9260000000000e4cb2600000000008d8b26000000000040d026000000000069d0260000000000d047260000000000fbcb26000000000021c7260000000000c9ce26000000000012b9260000000000aec7260000000000d2cc2600000000008bc4260000000000deee330000000000962726000000000056c226000000000099cf2600000000008e8f250000000000cbcb26000000000090c2260000000000adcd26000000000094a1260000000000d3b42600000000009cca260000000000ddf524000000000023182f0000000000b04026000000000064d0260000000000a5d026000000000069ca26000000000008ce2600000000000dce26000000000054cc2600000000009dcf26000000000043c5260000000000b9cd2600000000007dd02600000000005ad026000000000086d02600000000003fd0260000000000fd63260000000000228f2500000000004dd0260000000000bdcf260000000000a2cb260000000000c0c92600000000002ccf260000000000d19244000000000015d0260000000000becf26000000000008c72600000000000cd0260000000000abc3260000000000cdbc260000000000a7c82600000000007ac826000000000004792600000000000e9e490000000000e5be2600000000001f83280000000000bbbc2600000000007cd02600000000007dd0260000000000bccd26000000000006c8260000000000fac7260000000000f1c2260000000000f3ce26000000000067c726000000000068445d0000000000ecce260000000000d3c9260000000000e0c226000000000029cf2600000000000ccd260000000000becf26000000000048cb2600000000006186280000000000a7d02600000000007bc9260000000000dadf530000000000b3d026000000000083cf260000000000b1cf26000000000060424b0000000000eacb260000000000fbc826000000000045c3260000000000cdcf260000000000e1ce2600000000002fcb2600000000004dc426000000000095cd26000000000087362700000000004bcb260000000000afc4260000000000f5c626000000000038cd260000000000f9ce260000000000f6733a0000000000cbc9260000000000b4cd260000000000b4cb26000000000014cc26000000000000cf260000000000aac4260000000000ddc72600000000002efc340000000000b7b12600000000008479260000000000f3cf260000000000b5bd2600000000005ace260000000000d6c7260000000000e0c226000000000050ca260000000000f3c3260000000000f18e2600000000000bcc260000000000232e2600000000004b8f2500000000008fc726000000000096cc26000000000004cf2600000000006fc926000000000094ca26000000000024ac26000000000008cb260000000000d2c7260000000000c1cf26000000000079cc260000000000313d30000000000029ce2600000000006cc9260000000000c8cc260000000000e1c626000000000049cb260000000000b1ce260000000000afc5260000000000e1c6260000000000b3c8260000000000fbce2600000000009e733600000000000dcf26000000000079c826000000000030c5260000000000a7aa2400000000006cd026000000000098c6260000000000a4c4260000000000e9ce260000000000d3ce26000000000069cd260000000000f5c526000000000088cd260000000000d8cd260000000000c2cd260000000000df7b260000000000afc8260000000000943f2600000000000253280000000000dbcd2600000000006d7b260000000000dec7260000000000858126000000000073b526000000000025c4260000000000f0bc2600000000000dc126000000000087b726000000000002bd260000000000c7ce26000000000044c4260000000000d5cf260000000000aacd260000000000c5c826000000000028c126000000000078c52600000000004eef5400000000003ccf260000000000ef32300000000000c25145000000000067b52600000000009dc3260000000000def5240000000000e2ce260000000000108126000000000073c5260000000000ebcd26000000000034c8260000000000d2c12600000000006bcc260000000000f69c26000000000026c9260000000000909426000000000021cc260000000000ffc2260000000000fec72600000000007ecc260000000000adc7260000000000773e4000000000009ac226000000000063c5260000000000f7c3260000000000d0cf260000000000c4d9390000000000ea492900000000007acb26000000000017cb2600000000006abd260000000000eec4260000000000a2c726000000000008d026000000000099c826000000000022c72600000000009fca2600000000004ecc2600000000004cd026000000000007d026000000000000ca2600000000008dd026000000000055c226000000000091d02600000000004184260000000000e2cb26000000000006be2600000000002079260000000000cdc72600000000004ac42600000000003fc426000000000040a73e000000000070aa240000000000d3bd26000000000045ce260000000000bbf524000000000059fe63000000000002ce260000000000bdcb2600000000007eaa24000000000059c226000000000061bc260000000000f6cd260000000000d6d93400000000005210340000000000f74a31000000000013c8260000000000fd8f260000000000bbc0260000000000a6d02600000000001dcd26000000000019ca260000000000e9cb26000000000068ce26000000000007bc260000000000b6cc260000000000e9ce260000000000cacf26000000000013cc260000000000a319260000000000b174260000000000b9cc260000000000b3cb2600000000004efe63000000000048cd26000000000055b826000000000089c726000000000070d02600000000004ccc260000000000bac9260000000000a9d0260000000000c2cf26000000000044cc260000000000b9ee4b000000000098bb260000000000aacc26000000000062ca2600000000009163250000000000a1b42600000000008dcc2600000000008ace260000000000b9ce2600000000008bcd2600000000002bc22600000000005ed0260000000000fdc8260000000000ecc72600000000000dcd26000000000071cb26000000000056cd260000000000a9ca26000000000064c92600000000005a902d00000000002dcf260000000000" }, }, }, diff --git a/tests/integration_tests/test_subtensor_integration.py b/tests/integration_tests/test_subtensor_integration.py index d907890389..fac4b907de 100644 --- a/tests/integration_tests/test_subtensor_integration.py +++ b/tests/integration_tests/test_subtensor_integration.py @@ -96,7 +96,7 @@ async def test_is_hotkey_registered(mocker): async def test_blocks_since_last_update(mocker): subtensor = await prepare_test(mocker, "blocks_since_last_update") result = subtensor.blocks_since_last_update(1, 0) - assert result == 3978699 + assert result == 4009702 @pytest.mark.asyncio From 0ec842d438a17ed596d18bff0535c2186b50a0c7 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 17:55:20 -0700 Subject: [PATCH 16/41] fix integration tests + - `Async/Subtensor` parameter `_mock` renamed to `mock`, also moved to last one in order. Community can use mocked `Async/Subtensor` in their tests in the same way as in we use it in the codebase. --- MIGRATION.md | 3 ++- bittensor/core/async_subtensor.py | 6 +++--- bittensor/core/subtensor.py | 6 +++--- tests/integration_tests/test_subtensor_integration.py | 2 +- tests/unit_tests/conftest.py | 2 +- tests/unit_tests/test_subtensor.py | 4 ++-- tests/unit_tests/test_subtensor_api.py | 6 +++--- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 4776e2bd95..e1bfcc3218 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -240,7 +240,8 @@ Removing deprecated extrinsics and replacing them with consistent ones: - method `blocks_since_last_update` improved. Currently it can be used to get historical data from archive node. - methods (async) `get_subnet_validator_permits` and `get_subnet_owner_hotkey` got `block_hash` and `reuse_block` parameters. - attribute `DelegateInfo/lite.total_daily_return` has been deleted (Vune confirmed that we shouldn't use it) - +- `Async/Subtensor` parameter `_mock` renamed to `mock`, also moved to last one in order. Community can use mocked `Async/Subtensor` in their tests in the same way as in we use it in the codebase. + Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` - `bittensor.core.timelock` moved to `bittensor.core.addons.timelock` diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 0d9cfcc881..8b86ca2510 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -146,9 +146,9 @@ def __init__( log_verbose: bool = False, fallback_endpoints: Optional[list[str]] = None, retry_forever: bool = False, - _mock: bool = False, archive_endpoints: Optional[list[str]] = None, websocket_shutdown_timer: float = 5.0, + mock: bool = False, ): """Initializes an AsyncSubtensor instance for blockchain interaction. @@ -159,7 +159,7 @@ def __init__( log_verbose: Enables or disables verbose logging. fallback_endpoints: List of fallback endpoints to use if default or provided network is not available. retry_forever: Whether to retry forever on connection errors. - _mock: Whether this is a mock instance. Mainly for testing purposes. + mock: Whether this is a mock instance. Mainly for testing purposes. archive_endpoints: Similar to fallback_endpoints, but specifically only archive nodes. Will be used in cases where you are requesting a block that is too old for your current (presumably lite) node. websocket_shutdown_timer: Amount of time, in seconds, to wait after the last response from the chain to @@ -198,7 +198,7 @@ async def main(): self.substrate = self._get_substrate( fallback_endpoints=fallback_endpoints, retry_forever=retry_forever, - _mock=_mock, + _mock=mock, archive_endpoints=archive_endpoints, ws_shutdown_timer=websocket_shutdown_timer, ) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index d9ae0fc525..78ba592f7b 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -143,8 +143,8 @@ def __init__( log_verbose: bool = False, fallback_endpoints: Optional[list[str]] = None, retry_forever: bool = False, - _mock: bool = False, archive_endpoints: Optional[list[str]] = None, + mock: bool = False, ): """ Initializes an instance of the Subtensor class. @@ -155,9 +155,9 @@ def __init__( log_verbose: Enables or disables verbose logging. fallback_endpoints: List of fallback endpoints to use if default or provided network is not available. retry_forever: Whether to retry forever on connection errors. - _mock: Whether this is a mock instance. Mainly just for use in testing. archive_endpoints: Similar to fallback_endpoints, but specifically only archive nodes. Will be used in cases where you are requesting a block that is too old for your current (presumably lite) node. + mock: Whether this is a mock instance. Mainly just for use in testing. Raises: Any exceptions raised during the setup, configuration, or connection process. @@ -177,7 +177,7 @@ def __init__( self.substrate = self._get_substrate( fallback_endpoints=fallback_endpoints, retry_forever=retry_forever, - _mock=_mock, + _mock=mock, archive_endpoints=archive_endpoints, ) if self.log_verbose: diff --git a/tests/integration_tests/test_subtensor_integration.py b/tests/integration_tests/test_subtensor_integration.py index fac4b907de..c95c15a1af 100644 --- a/tests/integration_tests/test_subtensor_integration.py +++ b/tests/integration_tests/test_subtensor_integration.py @@ -25,7 +25,7 @@ async def prepare_test(mocker, seed, **subtensor_args): "async_substrate_interface.sync_substrate.connect", mocker.Mock(return_value=FakeWebsocket(seed=seed)), ) - subtensor = Subtensor("unknown", _mock=True, **subtensor_args) + subtensor = Subtensor("unknown", mock=True, **subtensor_args) return subtensor diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index bed6f28bb3..7b13c7893e 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -28,7 +28,7 @@ def mock_substrate(mocker): @pytest.fixture def subtensor(mock_substrate): - return bittensor.core.subtensor.Subtensor(_mock=True) + return bittensor.core.subtensor.Subtensor(mock=True) @pytest.fixture diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 2f92281cd2..cf9a74f729 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -61,8 +61,8 @@ def call_params_with_certificate(): def test_methods_comparable(mock_substrate): """Verifies that methods in sync and async Subtensors are comparable.""" # Preps - subtensor = Subtensor(_mock=True) - async_subtensor = AsyncSubtensor(_mock=True) + subtensor = Subtensor(mock=True) + async_subtensor = AsyncSubtensor(mock=True) # methods which lives in async subtensor only excluded_async_subtensor_methods = ["initialize"] diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index 785aab0926..aead8c7ce1 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -8,9 +8,9 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): """Verifies that methods in SubtensorApi and its properties contains all Subtensors methods.""" # Preps subtensor = ( - other_class(network="latent-lite", _mock=True) + other_class(network="latent-lite", mock=True) if other_class - else Subtensor(network="latent-lite", _mock=True) + else Subtensor(network="latent-lite", mock=True) ) subtensor_api = SubtensorApi(network="latent-lite", mock=True) @@ -70,7 +70,7 @@ def test__methods_comparable_with_passed_legacy_methods( subtensor = ( other_class(network="latent-lite", mock=True) if other_class - else Subtensor(network="latent-lite", _mock=True) + else Subtensor(network="latent-lite", mock=True) ) subtensor_api = SubtensorApi(network="latent-lite", mock=True, legacy_methods=True) From b16af2ee8f363af23e300000553e42354afef0d4 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 17:55:59 -0700 Subject: [PATCH 17/41] update MIGRATION.md --- MIGRATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION.md b/MIGRATION.md index e1bfcc3218..725b353f46 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -67,7 +67,7 @@ rename this variable in documentation. 10. Revise `bittensor/utils/easy_imports.py` module to remove deprecated backwards compatibility objects. Use this module as a functionality for exporting existing objects to the package root to keep __init__.py minimal and simple. -11. Remove `bittensor.utils.version.version_checking` +11. ✅ Remove deprecated `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. From 88139730cbb3ffd637c4adead198328a3aafe992 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 17:56:45 -0700 Subject: [PATCH 18/41] update MIGRATION.md --- MIGRATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATION.md b/MIGRATION.md index 725b353f46..515f9b1c12 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -51,7 +51,7 @@ rename this variable in documentation. 4. ✅ Common refactoring (improve type annotations, etc) -5. Rename `non-/fast-blocks` to `non-/fast-runtime` in related places to be consistent with subtensor repo. Related with testing, subtensor scripts, documentation. +5. ~~Rename `non-/fast-blocks` to `non-/fast-runtime` in related places to be consistent with subtensor repo. Related with testing, subtensor scripts, documentation.~~ done across many PRs. 6. ✅ To be consistent throughout the SDK `(in progress)`: `hotkey`, `coldkey`, `hotkeypub`, and `coldkeypub` are keypairs From ef6cdd41b1119305740eb2f5d1954467014ef80a Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 18:07:13 -0700 Subject: [PATCH 19/41] ops, _mock --- bittensor/core/addons/subtensor_api/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/addons/subtensor_api/__init__.py b/bittensor/core/addons/subtensor_api/__init__.py index f729998c9e..7f9a8e500a 100644 --- a/bittensor/core/addons/subtensor_api/__init__.py +++ b/bittensor/core/addons/subtensor_api/__init__.py @@ -126,7 +126,7 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: log_verbose=self.log_verbose, fallback_endpoints=self._fallback_endpoints, retry_forever=self._retry_forever, - _mock=self._mock, + mock=self._mock, archive_endpoints=self._archive_endpoints, websocket_shutdown_timer=self._ws_shutdown_timer, ) @@ -139,7 +139,7 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: log_verbose=self.log_verbose, fallback_endpoints=self._fallback_endpoints, retry_forever=self._retry_forever, - _mock=self._mock, + mock=self._mock, archive_endpoints=self._archive_endpoints, ) From 1ef8161381bc3a0f7819a49d3df450f109b7567b Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 18:19:00 -0700 Subject: [PATCH 20/41] fix `tests/e2e_tests/test_delegate.py` --- tests/e2e_tests/test_delegate.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/e2e_tests/test_delegate.py b/tests/e2e_tests/test_delegate.py index 176522dea8..9659985413 100644 --- a/tests/e2e_tests/test_delegate.py +++ b/tests/e2e_tests/test_delegate.py @@ -389,7 +389,6 @@ def test_delegates(subtensor, alice_wallet, bob_wallet): validator_permits=[], registrations=[0], return_per_1000=Balance(0), - total_daily_return=Balance(0), total_stake={}, nominators={}, ) @@ -405,7 +404,6 @@ def test_delegates(subtensor, alice_wallet, bob_wallet): validator_permits=[], registrations=[0], return_per_1000=Balance(0), - total_daily_return=Balance(0), total_stake={}, nominators={}, ) @@ -461,9 +459,6 @@ def test_delegates(subtensor, alice_wallet, bob_wallet): validator_permits=[alice_subnet_netuid], registrations=[0, alice_subnet_netuid], return_per_1000=Balance(0), - total_daily_return=get_dynamic_balance( - bob_delegated[0].total_daily_return.rao - ), netuid=alice_subnet_netuid, stake=get_dynamic_balance(bob_delegated[0].stake.rao, alice_subnet_netuid), ), @@ -549,7 +544,6 @@ async def test_delegates_async(async_subtensor, alice_wallet, bob_wallet): validator_permits=[], registrations=[0], return_per_1000=Balance(0), - total_daily_return=Balance(0), total_stake={}, nominators={}, ) @@ -565,7 +559,6 @@ async def test_delegates_async(async_subtensor, alice_wallet, bob_wallet): validator_permits=[], registrations=[0], return_per_1000=Balance(0), - total_daily_return=Balance(0), total_stake={}, nominators={}, ) @@ -631,9 +624,6 @@ async def test_delegates_async(async_subtensor, alice_wallet, bob_wallet): validator_permits=[alice_subnet_netuid], registrations=[0, alice_subnet_netuid], return_per_1000=Balance(0), - total_daily_return=get_dynamic_balance( - bob_delegated[0].total_daily_return.rao - ), netuid=alice_subnet_netuid, stake=get_dynamic_balance(bob_delegated[0].stake.rao, alice_subnet_netuid), ), From b5965dfefa8133018670bb08ba8e991f12a21ba0 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 20:34:00 -0700 Subject: [PATCH 21/41] BT_SUBTENSOR_CHAIN_ENDPOINT + MIGRATION.md --- MIGRATION.md | 11 ++++++----- bittensor/core/settings.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 515f9b1c12..ea71bd58f7 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -24,7 +24,7 @@ 9. ✅ ~~`subtensor.get_transfer_fee` calls extrinsic inside the subtensor module. Actually the method could be updated by using `bittensor.core.extrinsics.utils.get_extrinsic_fee`.~~ `get_transfer_fee` isn't `get_extrinsic_fee` ## Subtensor -1. In the synchronous Subtensor class, the `get_owned_hotkeys` method includes a `reuse_block` parameter that is inconsistent with other methods. Either remove this parameter from `get_owned_hotkeys`, or add it to all other methods that directly call self.substrate.* to maintain a consistent interface. +1. ✅ In the synchronous Subtensor class, the `get_owned_hotkeys` method includes a `reuse_block` parameter that is inconsistent with other methods. Either remove this parameter from `get_owned_hotkeys`, or add it to all other methods that directly call self.substrate.* to maintain a consistent interface. 2. ✅ In all methods where we `get_stake_operations_fee` is called, remove unused arguments. Consider combining all methods using `get_stake_operations_fee` into one common one. 3. ✅ Delete deprecated `get_current_weight_commit_info` and `get_current_weight_commit_info_v2`. ~~Rename `get_timelocked_weight_commits` to `get_current_weight_commit_info`.~~ 4. ✅ Remove references like `get_stake_info_for_coldkey = get_stake_for_coldkey`. @@ -41,17 +41,17 @@ This may seem like a harsh decision at first, but ultimately we will push the community to use Balance and there will be fewer errors in their calculations. Confusion with TAO and Alpha in calculations and display/printing/logging will be eliminated. ## Common things -1. Reduce the amount of logging.info or transfer part of logging.info to logging.debug `(in progress)` +1. ✅ Reduce the amount of logging.info or transfer part of logging.info to logging.debug `(in progress)` -2. To be consistent across all SDK regarding local environment variables name: +2. ✅ To be consistent across all SDK regarding local environment variables name: remove `BT_CHAIN_ENDPOINT` (settings.py :line 124) and use `BT_SUBTENSOR_CHAIN_ENDPOINT` instead of that. rename this variable in documentation. -3. Move `bittensor.utils.get_transfer_fn_params` to `bittensor.core.extrinsics.utils`. +3. ~~Move `bittensor.utils.get_transfer_fn_params` to `bittensor.core.extrinsics.utils`.~~ it's on the right place. 4. ✅ Common refactoring (improve type annotations, etc) -5. ~~Rename `non-/fast-blocks` to `non-/fast-runtime` in related places to be consistent with subtensor repo. Related with testing, subtensor scripts, documentation.~~ done across many PRs. +5. ✅ ~~Rename `non-/fast-blocks` to `non-/fast-runtime` in related places to be consistent with subtensor repo. Related with testing, subtensor scripts, documentation.~~ done across many PRs. 6. ✅ To be consistent throughout the SDK `(in progress)`: `hotkey`, `coldkey`, `hotkeypub`, and `coldkeypub` are keypairs @@ -245,6 +245,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` - `bittensor.core.timelock` moved to `bittensor.core.addons.timelock` + - local env variable `BT_CHAIN_ENDPOINT` replaced with `BT_SUBTENSOR_CHAIN_ENDPOINT`. ### Mechid related changes: In the next subtensor methods got updated the parameters order: diff --git a/bittensor/core/settings.py b/bittensor/core/settings.py index 3e5edae0d6..3bb6c4a447 100644 --- a/bittensor/core/settings.py +++ b/bittensor/core/settings.py @@ -121,7 +121,7 @@ "maxsize": int(_BT_PRIORITY_MAXSIZE) if _BT_PRIORITY_MAXSIZE else 10, }, "subtensor": { - "chain_endpoint": os.getenv("BT_CHAIN_ENDPOINT") or DEFAULT_ENDPOINT, + "chain_endpoint": os.getenv("BT_SUBTENSOR_CHAIN_ENDPOINT") or DEFAULT_ENDPOINT, "network": os.getenv("BT_NETWORK") or DEFAULT_NETWORK, "_mock": False, }, From 6405718d43e8ba5155f02ec2f40259a885e3eb23 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 20:35:00 -0700 Subject: [PATCH 22/41] ruff --- bittensor/core/settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bittensor/core/settings.py b/bittensor/core/settings.py index 3bb6c4a447..0407e80001 100644 --- a/bittensor/core/settings.py +++ b/bittensor/core/settings.py @@ -121,7 +121,8 @@ "maxsize": int(_BT_PRIORITY_MAXSIZE) if _BT_PRIORITY_MAXSIZE else 10, }, "subtensor": { - "chain_endpoint": os.getenv("BT_SUBTENSOR_CHAIN_ENDPOINT") or DEFAULT_ENDPOINT, + "chain_endpoint": os.getenv("BT_SUBTENSOR_CHAIN_ENDPOINT") + or DEFAULT_ENDPOINT, "network": os.getenv("BT_NETWORK") or DEFAULT_NETWORK, "_mock": False, }, From 83c45ce5f7b0ecd6b14c346426808658a96764f0 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 22:21:37 -0700 Subject: [PATCH 23/41] fix `get_async_subtensor` --- bittensor/core/async_subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 8b86ca2510..9acc8c75b3 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -5868,14 +5868,14 @@ async def unstake_multiple( async def get_async_subtensor( network: Optional[str] = None, config: Optional["Config"] = None, - _mock: bool = False, + mock: bool = False, log_verbose: bool = False, ) -> "AsyncSubtensor": """Factory method to create an initialized AsyncSubtensor. Mainly useful for when you don't want to run `await subtensor.initialize()` after instantiation. """ sub = AsyncSubtensor( - network=network, config=config, _mock=_mock, log_verbose=log_verbose + network=network, config=config, mock=mock, log_verbose=log_verbose ) await sub.initialize() return sub From ba1f6ddf00018fa71513a575738763c1c27ce351 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 22:58:18 -0700 Subject: [PATCH 24/41] improve `easy_import.py`: removing inconsistent class names + references to overridden subpackages --- MIGRATION.md | 22 ++++++ bittensor/utils/btlogging/levels.py | 42 ++++++++++++ bittensor/utils/easy_imports.py | 100 +++++----------------------- tests/unit_tests/test_config.py | 12 ++-- tests/unit_tests/test_deprecated.py | 34 ---------- 5 files changed, 88 insertions(+), 122 deletions(-) create mode 100644 bittensor/utils/btlogging/levels.py delete mode 100644 tests/unit_tests/test_deprecated.py diff --git a/MIGRATION.md b/MIGRATION.md index ea71bd58f7..1361c3cc03 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -263,3 +263,25 @@ Additional: ### Renames parameters: - `get_metagraph_info`: `field_indices` -> `selected_indices` (to be consistent) + + +### `easy_import.py` module +Added: +- `from bittenosor import extrinsics` +- `from bittenosor import mock` +- `from bittenosor import get_async_subtensor` + +Next variables removed: +- `async_subtensor` not -> `AsyncSubtensor` +- `axon` not -> `Axon` +- `config` not -> `Config` +- `dendrite` not -> `Dendrite` +- `keyfile` not -> `Keyfile` +- `metagraph` not -> `Metagraph` +- `wallet` not -> `Wallet` +- `subtensor` not -> `Subtensor` +- `synapse` not -> `Synapse` + +Links to subpackages removed: +- `bittensor.mock` (available in `bittensor.core.mock`) +- `bittensor.extrinsics` (available in `bittensor.core.extrinsics`) diff --git a/bittensor/utils/btlogging/levels.py b/bittensor/utils/btlogging/levels.py new file mode 100644 index 0000000000..6115de1473 --- /dev/null +++ b/bittensor/utils/btlogging/levels.py @@ -0,0 +1,42 @@ +from bittensor.utils.btlogging import logging + + +# Logging level setup helpers. +def trace(on: bool = True): + """ + Enables or disables trace logging. + + Parameters: + on: If True, enables trace logging. If False, disables trace logging. + """ + logging.set_trace(on) + + +def debug(on: bool = True): + """ + Enables or disables debug logging. + + Parameters: + on: If True, enables debug logging. If False, disables debug logging. + """ + logging.set_debug(on) + + +def warning(on: bool = True): + """ + Enables or disables warning logging. + + Parameters: + on: If True, enables warning logging. If False, disables warning logging and sets default (WARNING) level. + """ + logging.set_warning(on) + + +def info(on: bool = True): + """ + Enables or disables info logging. + + Parameters: + on: If True, enables info logging. If False, disables info logging and sets default (WARNING) level. + """ + logging.set_info(on) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 57bc9ae2bc..b0f9578656 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -1,13 +1,15 @@ """ -The Bittensor Compatibility Module is designed to ensure seamless integration and functionality with legacy versions of -the Bittensor framework, specifically up to and including version 7.3.0. This module addresses changes and deprecated -features in recent versions, allowing users to maintain compatibility with older systems and projects. -""" +The Bittensor Compatibility Module serves as a centralized import hub for internal and external classes, functions, +constants, and utilities that are frequently accessed via the top-level `bittensor` namespace +(e.g., `from bittensor import Wallet`). + +It consolidates these widely used symbols into `bittensor/__init__.py`, enabling a cleaner and more intuitive public API +for developers and the broader community. -import importlib -import sys +Note: + Direct imports from their respective submodules are recommended for improved clarity and long-term maintainability. +""" -from bittensor_wallet import Keypair from bittensor_wallet.errors import KeyFileError from bittensor_wallet.keyfile import ( serialized_keypair_to_keyfile_data, @@ -25,11 +27,12 @@ decrypt_keyfile_data, Keyfile, ) +from bittensor_wallet.keypair import Keypair from bittensor_wallet.wallet import Wallet -from bittensor.core import settings +from bittensor.core import settings, extrinsics from bittensor.core.addons import timelock, SubtensorApi -from bittensor.core.async_subtensor import AsyncSubtensor +from bittensor.core.async_subtensor import AsyncSubtensor, get_async_subtensor from bittensor.core.axon import Axon from bittensor.core.chain_data import ( AxonInfo, @@ -104,6 +107,7 @@ from bittensor.core.tensor import Tensor from bittensor.core.threadpool import PriorityThreadPoolExecutor from bittensor.utils import ( + mock, ss58_to_vec_u8, strtobool, get_explorer_url_for_network, @@ -112,72 +116,12 @@ u64_normalized_float, get_hash, ) -from bittensor.utils.balance import Balance -from bittensor.utils.balance import tao, rao +from bittensor.utils.balance import Balance, tao, rao from bittensor.utils.btlogging import logging +from bittensor.utils.btlogging.levels import trace, debug, warning, info from bittensor.utils.mock.subtensor_mock import MockSubtensor from bittensor.utils.subnets import SubnetsAPI -# Backwards compatibility with previous bittensor versions. -async_subtensor = AsyncSubtensor -axon = Axon -config = Config -dendrite = Dendrite -keyfile = Keyfile -metagraph = Metagraph -wallet = Wallet -subtensor = Subtensor -synapse = Synapse - -# Makes the `bittensor.utils.mock` subpackage available as `bittensor.mock` for backwards compatibility. -mock_subpackage = importlib.import_module("bittensor.utils.mock") -sys.modules["bittensor.mock"] = mock_subpackage - -# Makes the `bittensor.core.extrinsics` subpackage available as `bittensor.extrinsics` for backwards compatibility. -extrinsics_subpackage = importlib.import_module("bittensor.core.extrinsics") -sys.modules["bittensor.extrinsics"] = extrinsics_subpackage - - -# Logging helpers. -def trace(on: bool = True): - """ - Enables or disables trace logging. - - Parameters: - on: If True, enables trace logging. If False, disables trace logging. - """ - logging.set_trace(on) - - -def debug(on: bool = True): - """ - Enables or disables debug logging. - - Parameters: - on: If True, enables debug logging. If False, disables debug logging. - """ - logging.set_debug(on) - - -def warning(on: bool = True): - """ - Enables or disables warning logging. - - Parameters: - on: If True, enables warning logging. If False, disables warning logging and sets default (WARNING) level. - """ - logging.set_warning(on) - - -def info(on: bool = True): - """ - Enables or disables info logging. - - Parameters: - on: If True, enables info logging. If False, disables info logging and sets default (WARNING) level. - """ - logging.set_info(on) - __all__ = [ "Keypair", @@ -284,19 +228,11 @@ def info(on: bool = True): "logging", "MockSubtensor", "SubnetsAPI", - "async_subtensor", - "axon", - "config", - "dendrite", - "keyfile", - "metagraph", - "wallet", - "subtensor", - "synapse", "trace", "debug", "warning", "info", - "mock_subpackage", - "extrinsics_subpackage", + "extrinsics", + "mock", + "get_async_subtensor", ] diff --git a/tests/unit_tests/test_config.py b/tests/unit_tests/test_config.py index 5c1c2a0edf..18b8ee36d5 100644 --- a/tests/unit_tests/test_config.py +++ b/tests/unit_tests/test_config.py @@ -6,12 +6,12 @@ def test_py_config_parsed_successfully_rust_wallet(): """Verify that python based config object is successfully parsed with rust-based wallet object.""" parser = argparse.ArgumentParser() - bittensor.wallet.add_args(parser) - bittensor.subtensor.add_args(parser) - bittensor.axon.add_args(parser) + bittensor.Wallet.add_args(parser) + bittensor.Subtensor.add_args(parser) + bittensor.Axon.add_args(parser) bittensor.logging.add_args(parser) - config = bittensor.config(parser) + config = bittensor.Config(parser) # override config manually since we can't apply mocking to rust objects easily config.wallet.name = "new_wallet_name" @@ -19,13 +19,13 @@ def test_py_config_parsed_successfully_rust_wallet(): config.wallet.path = "/some/not_default/path" # Pass in the whole bittensor config - wallet = bittensor.wallet(config=config) + wallet = bittensor.Wallet(config=config) assert wallet.name == config.wallet.name assert wallet.hotkey_str == config.wallet.hotkey assert wallet.path == config.wallet.path # Pass in only the btwallet's config - wallet_two = bittensor.wallet(config=config.wallet) + wallet_two = bittensor.Wallet(config=config.wallet) assert wallet_two.name == config.wallet.name assert wallet_two.hotkey_str == config.wallet.hotkey assert wallet_two.path == config.wallet.path diff --git a/tests/unit_tests/test_deprecated.py b/tests/unit_tests/test_deprecated.py deleted file mode 100644 index f47337e019..0000000000 --- a/tests/unit_tests/test_deprecated.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys - - -def test_mock_import(): - """ - Tests that `bittensor.mock` can be imported and is the same as `bittensor.utils.mock`. - """ - import bittensor.mock as redirected_mock - import bittensor.utils.mock as real_mock - - assert "bittensor.mock" in sys.modules - assert redirected_mock is real_mock - - -def test_extrinsics_import(): - """Tests that `bittensor.extrinsics` can be imported and is the same as `bittensor.utils.deprecated.extrinsics`.""" - import bittensor.extrinsics as redirected_extrinsics - import bittensor.core.extrinsics as real_extrinsics - - assert "bittensor.extrinsics" in sys.modules - assert redirected_extrinsics is real_extrinsics - - -def test_object_aliases_are_correctly_mapped(): - """Ensures all object aliases correctly map to their respective classes in Bittensor package.""" - import bittensor - - assert issubclass(bittensor.axon, bittensor.Axon) - assert issubclass(bittensor.config, bittensor.Config) - assert issubclass(bittensor.dendrite, bittensor.Dendrite) - assert issubclass(bittensor.keyfile, bittensor.Keyfile) - assert issubclass(bittensor.metagraph, bittensor.Metagraph) - assert issubclass(bittensor.wallet, bittensor.Wallet) - assert issubclass(bittensor.synapse, bittensor.Synapse) From 4dd4e079ea5d693da713a28f45c2c909982aa9ec Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 23:00:11 -0700 Subject: [PATCH 25/41] update MIGRATION.md --- MIGRATION.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index 1361c3cc03..bd0d064ee4 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -285,3 +285,9 @@ Next variables removed: Links to subpackages removed: - `bittensor.mock` (available in `bittensor.core.mock`) - `bittensor.extrinsics` (available in `bittensor.core.extrinsics`) + + +New subpackage `bittensor.core.addons` created to host optional extensions and experimental logic enhancing the core functionality. +Currently it contains: +- `bittensor.core.addons.subtensor_api` +- `bittensor.core.addons.timelock` \ No newline at end of file From 18c3def492003331a2e67094e03c6910d1818bf5 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 23:21:44 -0700 Subject: [PATCH 26/41] use updated `subnet-template` --- tests/e2e_tests/utils/e2e_test_utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/utils/e2e_test_utils.py b/tests/e2e_tests/utils/e2e_test_utils.py index e0a19ada7e..9d3671ce58 100644 --- a/tests/e2e_tests/utils/e2e_test_utils.py +++ b/tests/e2e_tests/utils/e2e_test_utils.py @@ -214,7 +214,10 @@ async def _reader(self): self.set_weights.set() def __init__(self): - self.dir = clone_or_update_templates() + # self.dir = clone_or_update_templates() + # keep it until https://github.com/opentensor/subnet-template/commits/feat/roman/no-low-case-classes-anymore/ + # merged to main + self.dir = clone_or_update_templates(specific_commit="d972b47ba870c2a9f32760a83bdb7041c2752f01") def __enter__(self): return self From 84199e414ca032cc16eefcfd06e934828eeb1576 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Mon, 29 Sep 2025 23:32:06 -0700 Subject: [PATCH 27/41] use regular `subnet-template` (merged) --- tests/e2e_tests/utils/e2e_test_utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/e2e_tests/utils/e2e_test_utils.py b/tests/e2e_tests/utils/e2e_test_utils.py index 9d3671ce58..e0a19ada7e 100644 --- a/tests/e2e_tests/utils/e2e_test_utils.py +++ b/tests/e2e_tests/utils/e2e_test_utils.py @@ -214,10 +214,7 @@ async def _reader(self): self.set_weights.set() def __init__(self): - # self.dir = clone_or_update_templates() - # keep it until https://github.com/opentensor/subnet-template/commits/feat/roman/no-low-case-classes-anymore/ - # merged to main - self.dir = clone_or_update_templates(specific_commit="d972b47ba870c2a9f32760a83bdb7041c2752f01") + self.dir = clone_or_update_templates() def __enter__(self): return self From 1a8eb6bc3c101ee23e03d6039a89ba90d46a6ccb Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 30 Sep 2025 08:50:26 -0700 Subject: [PATCH 28/41] mode addons from `bittensor.core.addons` to `bittensor.addons` (logically correct place) --- MIGRATION.md | 14 ++++----- bittensor/{core => }/addons/__init__.py | 4 +-- .../addons/subtensor_api/__init__.py | 0 .../{core => }/addons/subtensor_api/chain.py | 0 .../addons/subtensor_api/commitments.py | 0 .../addons/subtensor_api/delegates.py | 0 .../addons/subtensor_api/extrinsics.py | 0 .../addons/subtensor_api/metagraphs.py | 0 .../addons/subtensor_api/neurons.py | 0 .../addons/subtensor_api/queries.py | 0 .../addons/subtensor_api/staking.py | 0 .../addons/subtensor_api/subnets.py | 0 .../{core => }/addons/subtensor_api/utils.py | 2 +- .../addons/subtensor_api/wallets.py | 0 bittensor/{core => }/addons/timelock.py | 0 bittensor/utils/balance.py | 29 +++++++++---------- bittensor/utils/easy_imports.py | 3 +- tests/e2e_tests/conftest.py | 2 +- tests/e2e_tests/utils/chain_interactions.py | 2 +- tests/e2e_tests/utils/e2e_test_utils.py | 2 +- tests/integration_tests/test_timelock.py | 2 +- .../extrinsics/asyncex/test_unstaking.py | 22 +++++++------- tests/unit_tests/extrinsics/test_unstaking.py | 22 +++++++------- tests/unit_tests/test_async_subtensor.py | 2 +- tests/unit_tests/test_subtensor.py | 2 +- tests/unit_tests/test_subtensor_api.py | 2 +- 26 files changed, 54 insertions(+), 56 deletions(-) rename bittensor/{core => }/addons/__init__.py (86%) rename bittensor/{core => }/addons/subtensor_api/__init__.py (100%) rename bittensor/{core => }/addons/subtensor_api/chain.py (100%) rename bittensor/{core => }/addons/subtensor_api/commitments.py (100%) rename bittensor/{core => }/addons/subtensor_api/delegates.py (100%) rename bittensor/{core => }/addons/subtensor_api/extrinsics.py (100%) rename bittensor/{core => }/addons/subtensor_api/metagraphs.py (100%) rename bittensor/{core => }/addons/subtensor_api/neurons.py (100%) rename bittensor/{core => }/addons/subtensor_api/queries.py (100%) rename bittensor/{core => }/addons/subtensor_api/staking.py (100%) rename bittensor/{core => }/addons/subtensor_api/subnets.py (100%) rename bittensor/{core => }/addons/subtensor_api/utils.py (99%) rename bittensor/{core => }/addons/subtensor_api/wallets.py (100%) rename bittensor/{core => }/addons/timelock.py (100%) diff --git a/MIGRATION.md b/MIGRATION.md index bd0d064ee4..4143843435 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -41,19 +41,19 @@ This may seem like a harsh decision at first, but ultimately we will push the community to use Balance and there will be fewer errors in their calculations. Confusion with TAO and Alpha in calculations and display/printing/logging will be eliminated. ## Common things -1. ✅ Reduce the amount of logging.info or transfer part of logging.info to logging.debug `(in progress)` +1. ✅ Reduce the amount of logging.info or transfer part of logging.info to logging.debug 2. ✅ To be consistent across all SDK regarding local environment variables name: remove `BT_CHAIN_ENDPOINT` (settings.py :line 124) and use `BT_SUBTENSOR_CHAIN_ENDPOINT` instead of that. rename this variable in documentation. -3. ~~Move `bittensor.utils.get_transfer_fn_params` to `bittensor.core.extrinsics.utils`.~~ it's on the right place. +3. ✅ ~~Move `bittensor.utils.get_transfer_fn_params` to `bittensor.core.extrinsics.utils`.~~ it's on the right place. 4. ✅ Common refactoring (improve type annotations, etc) 5. ✅ ~~Rename `non-/fast-blocks` to `non-/fast-runtime` in related places to be consistent with subtensor repo. Related with testing, subtensor scripts, documentation.~~ done across many PRs. -6. ✅ To be consistent throughout the SDK `(in progress)`: +6. ✅ To be consistent throughout the SDK: `hotkey`, `coldkey`, `hotkeypub`, and `coldkeypub` are keypairs `hotkey_ss58`, `coldkey_ss58`, `hotkeypub_ss58`, and `coldkeypub_ss58` are SS58 addresses of keypair. @@ -235,7 +235,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - method `query_map` has updated parameters order. - method `add_stake_multiple` has updated parameters order. - method `get_stake_for_coldkey` removed, bc this is the same as `get_stake_info_for_coldkey` -- method `get_subnets` renamed to `get_all_subnets_netuid` (more obvious) +- method `get_subnets` renamed to `get_all_subnets_netuid` (obvious name, consistent with existing names) - method `get_owned_hotkeys` get rid `reuse_block` parameter to be consistent with other sync methods. - method `blocks_since_last_update` improved. Currently it can be used to get historical data from archive node. - methods (async) `get_subnet_validator_permits` and `get_subnet_owner_hotkey` got `block_hash` and `reuse_block` parameters. @@ -287,7 +287,7 @@ Links to subpackages removed: - `bittensor.extrinsics` (available in `bittensor.core.extrinsics`) -New subpackage `bittensor.core.addons` created to host optional extensions and experimental logic enhancing the core functionality. +New subpackage `bittensor.addons` created to host optional extensions and experimental logic enhancing the core functionality. Currently it contains: -- `bittensor.core.addons.subtensor_api` -- `bittensor.core.addons.timelock` \ No newline at end of file +- `bittensor.addons.subtensor_api` +- `bittensor.addons.timelock` \ No newline at end of file diff --git a/bittensor/core/addons/__init__.py b/bittensor/addons/__init__.py similarity index 86% rename from bittensor/core/addons/__init__.py rename to bittensor/addons/__init__.py index b84bb3ae90..0f7295f24a 100644 --- a/bittensor/core/addons/__init__.py +++ b/bittensor/addons/__init__.py @@ -9,8 +9,8 @@ discoverability and structure. """ -from bittensor.core.addons import timelock -from bittensor.core.addons.subtensor_api import SubtensorApi +from bittensor.addons import timelock +from bittensor.addons.subtensor_api import SubtensorApi __all__ = [ "timelock", diff --git a/bittensor/core/addons/subtensor_api/__init__.py b/bittensor/addons/subtensor_api/__init__.py similarity index 100% rename from bittensor/core/addons/subtensor_api/__init__.py rename to bittensor/addons/subtensor_api/__init__.py diff --git a/bittensor/core/addons/subtensor_api/chain.py b/bittensor/addons/subtensor_api/chain.py similarity index 100% rename from bittensor/core/addons/subtensor_api/chain.py rename to bittensor/addons/subtensor_api/chain.py diff --git a/bittensor/core/addons/subtensor_api/commitments.py b/bittensor/addons/subtensor_api/commitments.py similarity index 100% rename from bittensor/core/addons/subtensor_api/commitments.py rename to bittensor/addons/subtensor_api/commitments.py diff --git a/bittensor/core/addons/subtensor_api/delegates.py b/bittensor/addons/subtensor_api/delegates.py similarity index 100% rename from bittensor/core/addons/subtensor_api/delegates.py rename to bittensor/addons/subtensor_api/delegates.py diff --git a/bittensor/core/addons/subtensor_api/extrinsics.py b/bittensor/addons/subtensor_api/extrinsics.py similarity index 100% rename from bittensor/core/addons/subtensor_api/extrinsics.py rename to bittensor/addons/subtensor_api/extrinsics.py diff --git a/bittensor/core/addons/subtensor_api/metagraphs.py b/bittensor/addons/subtensor_api/metagraphs.py similarity index 100% rename from bittensor/core/addons/subtensor_api/metagraphs.py rename to bittensor/addons/subtensor_api/metagraphs.py diff --git a/bittensor/core/addons/subtensor_api/neurons.py b/bittensor/addons/subtensor_api/neurons.py similarity index 100% rename from bittensor/core/addons/subtensor_api/neurons.py rename to bittensor/addons/subtensor_api/neurons.py diff --git a/bittensor/core/addons/subtensor_api/queries.py b/bittensor/addons/subtensor_api/queries.py similarity index 100% rename from bittensor/core/addons/subtensor_api/queries.py rename to bittensor/addons/subtensor_api/queries.py diff --git a/bittensor/core/addons/subtensor_api/staking.py b/bittensor/addons/subtensor_api/staking.py similarity index 100% rename from bittensor/core/addons/subtensor_api/staking.py rename to bittensor/addons/subtensor_api/staking.py diff --git a/bittensor/core/addons/subtensor_api/subnets.py b/bittensor/addons/subtensor_api/subnets.py similarity index 100% rename from bittensor/core/addons/subtensor_api/subnets.py rename to bittensor/addons/subtensor_api/subnets.py diff --git a/bittensor/core/addons/subtensor_api/utils.py b/bittensor/addons/subtensor_api/utils.py similarity index 99% rename from bittensor/core/addons/subtensor_api/utils.py rename to bittensor/addons/subtensor_api/utils.py index 4b255e1b8c..86fa53eb95 100644 --- a/bittensor/core/addons/subtensor_api/utils.py +++ b/bittensor/addons/subtensor_api/utils.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from bittensor.core.addons import SubtensorApi + from bittensor.addons import SubtensorApi def add_legacy_methods(subtensor: "SubtensorApi"): diff --git a/bittensor/core/addons/subtensor_api/wallets.py b/bittensor/addons/subtensor_api/wallets.py similarity index 100% rename from bittensor/core/addons/subtensor_api/wallets.py rename to bittensor/addons/subtensor_api/wallets.py diff --git a/bittensor/core/addons/timelock.py b/bittensor/addons/timelock.py similarity index 100% rename from bittensor/core/addons/timelock.py rename to bittensor/addons/timelock.py diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index 5a84960152..37aafe1e32 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -1,4 +1,3 @@ -import warnings from typing import Union, TypedDict, Optional from scalecodec import ScaleType @@ -13,26 +12,23 @@ def _check_currencies(self, other): A warning is raised if the netuids differ. Example: - >>> balance1 = Balance.from_rao(1000).set_unit(12) - >>> balance2 = Balance.from_rao(500).set_unit(12) - >>> balance1 + balance2 # No warning + balance1 = Balance.from_rao(1000).set_unit(14) + balance2 = Balance.from_tao(500).set_unit(14) + balance1 + balance2 # No error. - >>> balance3 = Balance.from_rao(200).set_unit(15) - >>> balance1 + balance3 # Raises DeprecationWarning + balance3 = Balance.from_tao(200).set_unit(14) + balance1 + balance3 # Raises ValueError. In this example: - - `from_rao` creates a Balance instance from the amount in rao (smallest unit). - - `set_unit(12)` sets the unit to correspond to subnet 12 (i.e., Alpha from netuid 12). + - `from_rao` creates a Balance instance from the amount in rao. + - `set_unit(14)` sets the unit to correspond to subnet 14 (i.e., Alpha from netuid 14). """ if self.netuid != other.netuid: - warnings.simplefilter("default", DeprecationWarning) - warnings.warn( - "Balance objects must have the same netuid (Alpha currency) to perform arithmetic operations.\n" - f"First balance is `{self}`. Second balance is `{other}`.\n\n" - "To create a Balance instance with the correct netuid, use:\n" - "Balance.from_rao(1000).set_unit(12) # 1000 rao in subnet 12", - category=DeprecationWarning, - stacklevel=2, + raise TypeError( + f"Cannot perform arithmetic between balances of different currencies: {self} and {other}. " + "Both Balance objects must reference the same netuid (Alpha currency). " + "For example, to create a Balance instance for subnet 12 you can use: " + "Balance.from_tao(10).set_unit(14), which corresponds to 10 TAO in subnet 14." ) @@ -349,6 +345,7 @@ class FixedPoint(TypedDict): def fixed_to_float( fixed: Union[FixedPoint, ScaleType], frac_bits: int = 64, total_bits: int = 128 ) -> float: + """Converts a fixed-point value (e.g., U64F64) into a floating-point number.""" # By default, this is a U64F64 # which is 64 bits of integer and 64 bits of fractional data: int = fb.value if isinstance((fb := fixed["bits"]), ScaleType) else fb diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index b0f9578656..9eea34055a 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -30,8 +30,8 @@ from bittensor_wallet.keypair import Keypair from bittensor_wallet.wallet import Wallet +from bittensor.addons import timelock, SubtensorApi from bittensor.core import settings, extrinsics -from bittensor.core.addons import timelock, SubtensorApi from bittensor.core.async_subtensor import AsyncSubtensor, get_async_subtensor from bittensor.core.axon import Axon from bittensor.core.chain_data import ( @@ -122,7 +122,6 @@ from bittensor.utils.mock.subtensor_mock import MockSubtensor from bittensor.utils.subnets import SubnetsAPI - __all__ = [ "Keypair", "KeyFileError", diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 319b9d646a..f59dda1d9d 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -12,7 +12,7 @@ import pytest import pytest_asyncio -from bittensor.core.addons import SubtensorApi +from bittensor.addons import SubtensorApi from bittensor.utils.btlogging import logging from tests.e2e_tests.utils.e2e_test_utils import ( Templates, diff --git a/tests/e2e_tests/utils/chain_interactions.py b/tests/e2e_tests/utils/chain_interactions.py index 8fc0ecfe53..ee827b2083 100644 --- a/tests/e2e_tests/utils/chain_interactions.py +++ b/tests/e2e_tests/utils/chain_interactions.py @@ -15,7 +15,7 @@ # for typing purposes if TYPE_CHECKING: from bittensor import Wallet - from bittensor.core.addons import SubtensorApi + from bittensor.addons import SubtensorApi from async_substrate_interface import ( AsyncSubstrateInterface, AsyncExtrinsicReceipt, diff --git a/tests/e2e_tests/utils/e2e_test_utils.py b/tests/e2e_tests/utils/e2e_test_utils.py index e0a19ada7e..b561bbdd9f 100644 --- a/tests/e2e_tests/utils/e2e_test_utils.py +++ b/tests/e2e_tests/utils/e2e_test_utils.py @@ -6,7 +6,7 @@ from bittensor_wallet import Keypair, Wallet -from bittensor.core.addons import SubtensorApi +from bittensor.addons import SubtensorApi from bittensor.utils.btlogging import logging template_path = os.getcwd() + "/neurons/" diff --git a/tests/integration_tests/test_timelock.py b/tests/integration_tests/test_timelock.py index b0545e3517..2b7ddf9823 100644 --- a/tests/integration_tests/test_timelock.py +++ b/tests/integration_tests/test_timelock.py @@ -3,7 +3,7 @@ import pytest -from bittensor.core.addons import timelock +from bittensor.addons import timelock def test_encrypt_returns_valid_tuple(): diff --git a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py index 8b0032a79e..0721adfca2 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py +++ b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py @@ -7,23 +7,23 @@ @pytest.mark.asyncio async def test_unstake_extrinsic(fake_wallet, mocker): + # Preps fake_substrate = mocker.AsyncMock( **{"get_payment_info.return_value": {"partial_fee": 10}} ) - # Preps + fake_netuid = 14 fake_subtensor = mocker.AsyncMock( **{ "get_hotkey_owner.return_value": "hotkey_owner", - "get_stake_for_coldkey_and_hotkey.return_value": Balance(10.0), + "get_stake_for_coldkey_and_hotkey.return_value": Balance.from_tao(10.0, fake_netuid), "sign_and_send_extrinsic.return_value": ExtrinsicResponse(True, ""), - "get_stake.return_value": Balance(10.0), + "get_stake.return_value": Balance.from_tao(10.0, fake_netuid), "substrate": fake_substrate, } ) fake_wallet.coldkeypub.ss58_address = "hotkey_owner" hotkey_ss58 = "hotkey" - fake_netuid = 1 amount = Balance.from_tao(1.1) wait_for_inclusion = True wait_for_finalization = True @@ -48,7 +48,7 @@ async def test_unstake_extrinsic(fake_wallet, mocker): call_params={ "hotkey": "hotkey", "amount_unstaked": 1100000000, - "netuid": 1, + "netuid": fake_netuid, }, ) fake_subtensor.sign_and_send_extrinsic.assert_awaited_once_with( @@ -113,6 +113,9 @@ async def test_unstake_all_extrinsic(fake_wallet, mocker): async def test_unstake_multiple_extrinsic_some_unstake_is_happy(fake_wallet, mocker): """Verify that sync `unstake_multiple_extrinsic` method calls proper async method.""" # Preps + sn_5 = 5 + sn_14 = 14 + fake_netuids = [sn_5, sn_14] fake_substrate = mocker.AsyncMock( **{"get_payment_info.return_value": {"partial_fee": 10}} ) @@ -125,12 +128,11 @@ async def test_unstake_multiple_extrinsic_some_unstake_is_happy(fake_wallet, moc unstaking, "unstake_extrinsic", return_value=ExtrinsicResponse(True, "") ) mocker.patch.object( - unstaking, "get_old_stakes", return_value=[Balance(1.1), Balance(0.3)] + unstaking, "get_old_stakes", return_value=[Balance.from_tao(1.1, sn_5), Balance.from_tao(0.3, sn_14)] ) fake_wallet.coldkeypub.ss58_address = "hotkey_owner" hotkey_ss58s = ["hotkey1", "hotkey2"] - fake_netuids = [1, 2] - amounts = [Balance.from_tao(1.1), Balance.from_tao(1.2)] + amounts = [Balance.from_tao(1.1, sn_5), Balance.from_tao(1.2, sn_14)] wait_for_inclusion = True wait_for_finalization = True @@ -146,15 +148,13 @@ async def test_unstake_multiple_extrinsic_some_unstake_is_happy(fake_wallet, moc ) # Asserts - print(">>> result", response) - print(">>> result.success", response.success) assert response.success is False assert response.message == "Some unstake were successful." assert len(response.data) == 2 mocked_unstake_extrinsic.assert_awaited_once_with( subtensor=fake_subtensor, wallet=fake_wallet, - amount=Balance.from_tao(1.1), + amount=Balance.from_tao(1.1, sn_5), hotkey_ss58=hotkey_ss58s[0], netuid=fake_netuids[0], period=None, diff --git a/tests/unit_tests/extrinsics/test_unstaking.py b/tests/unit_tests/extrinsics/test_unstaking.py index 6c8320ff8d..4a88a5c8ba 100644 --- a/tests/unit_tests/extrinsics/test_unstaking.py +++ b/tests/unit_tests/extrinsics/test_unstaking.py @@ -4,22 +4,22 @@ def test_unstake_extrinsic(fake_wallet, mocker): + # Preps fake_substrate = mocker.Mock( **{"get_payment_info.return_value": {"partial_fee": 10}} ) - # Preps + fake_netuid = 14 fake_subtensor = mocker.Mock( **{ "get_hotkey_owner.return_value": "hotkey_owner", - "get_stake_for_coldkey_and_hotkey.return_value": Balance(10.0), + "get_stake_for_coldkey_and_hotkey.return_value": Balance.from_tao(10.0, fake_netuid), "sign_and_send_extrinsic.return_value": ExtrinsicResponse(True, ""), - "get_stake.return_value": Balance(10.0), + "get_stake.return_value": Balance.from_tao(10.0, fake_netuid), "substrate": fake_substrate, } ) fake_wallet.coldkeypub.ss58_address = "hotkey_owner" hotkey_ss58 = "hotkey" - fake_netuid = 1 amount = Balance.from_tao(1.1) wait_for_inclusion = True wait_for_finalization = True @@ -44,7 +44,7 @@ def test_unstake_extrinsic(fake_wallet, mocker): call_params={ "hotkey": "hotkey", "amount_unstaked": 1100000000, - "netuid": 1, + "netuid": fake_netuid, }, ) fake_subtensor.sign_and_send_extrinsic.assert_called_once_with( @@ -107,6 +107,9 @@ def test_unstake_all_extrinsic(fake_wallet, mocker): def test_unstake_multiple_extrinsic(subtensor, fake_wallet, mocker): """Tests when out of 2 unstakes 1 is completed and 1 is not.""" # Preps + sn_5 = 5 + sn_14 = 14 + fake_netuids = [sn_5, sn_14] mocked_balance = mocker.patch.object( subtensor, "get_balance", return_value=Balance.from_tao(1.0) ) @@ -119,12 +122,11 @@ def test_unstake_multiple_extrinsic(subtensor, fake_wallet, mocker): mocker.patch.object( unstaking, "get_old_stakes", - return_value=[Balance.from_tao(10), Balance.from_tao(0.3)], + return_value=[Balance.from_tao(10, sn_5), Balance.from_tao(0.3, sn_14)], ) fake_wallet.coldkeypub.ss58_address = "hotkey_owner" hotkey_ss58s = ["hotkey1", "hotkey2"] - fake_netuids = [1, 2] - amounts = [Balance.from_tao(1.1), Balance.from_tao(1.2)] + amounts = [Balance.from_tao(1.1, sn_5), Balance.from_tao(1.2, sn_14)] wait_for_inclusion = True wait_for_finalization = True @@ -151,9 +153,9 @@ def test_unstake_multiple_extrinsic(subtensor, fake_wallet, mocker): mocked_unstake_extrinsic.assert_called_once_with( subtensor=subtensor, wallet=fake_wallet, - netuid=1, + netuid=sn_5, hotkey_ss58="hotkey1", - amount=Balance.from_tao(1.1), + amount=Balance.from_tao(1.1, sn_5), period=None, raise_error=False, wait_for_inclusion=wait_for_inclusion, diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index caebce8a1c..6855eb72cd 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3801,7 +3801,7 @@ async def test_get_stake_operations_fee(subtensor, mocker): params=[netuid], block_hash=mocked_determine_block_hash.return_value, ) - assert result == Balance.from_rao(299076829).set_unit(netuid) + assert result == Balance.from_rao(299076829) @pytest.mark.asyncio diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index cf9a74f729..f45f71d51b 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3986,7 +3986,7 @@ def test_get_stake_operations_fee(subtensor, mocker): params=[netuid], block_hash=mocked_determine_block_hash.return_value, ) - assert result == Balance.from_rao(299076829).set_unit(netuid) + assert result == Balance.from_rao(299076829) def test_get_stake_add_fee(subtensor, mocker): diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index aead8c7ce1..39cc918439 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -1,6 +1,6 @@ import pytest -from bittensor.core.addons.subtensor_api import SubtensorApi +from bittensor.addons.subtensor_api import SubtensorApi from bittensor.core.subtensor import Subtensor From 4cadf5472ce29b14496529e0a95a0a5bf0c734f0 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 30 Sep 2025 09:46:30 -0700 Subject: [PATCH 29/41] Balance has strict check operations between instances regarding units. + fixed tests --- MIGRATION.md | 8 ++++---- bittensor/utils/balance.py | 21 ++++++++++++++++++++- tests/e2e_tests/test_delegate.py | 4 ++-- tests/e2e_tests/test_staking.py | 19 +++++++++++-------- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 4143843435..c1ced148a4 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -36,7 +36,7 @@ 2. Reconsider entire metagraph module logic. ## Balance -1. In `bittensor.utils.balance._check_currencies` raise the error instead of `warnings.warn`. +1. ✅ In `bittensor.utils.balance._check_currencies` raise the error instead of `warnings.warn`. 2. In `bittensor.utils.balance.check_and_convert_to_balance` raise the error instead of `warnings.warn`. This may seem like a harsh decision at first, but ultimately we will push the community to use Balance and there will be fewer errors in their calculations. Confusion with TAO and Alpha in calculations and display/printing/logging will be eliminated. @@ -65,7 +65,7 @@ rename this variable in documentation. - [x] CRv3 extrinsics - [x] CRv3 logic related subtensor's calls -10. Revise `bittensor/utils/easy_imports.py` module to remove deprecated backwards compatibility objects. Use this module as a functionality for exporting existing objects to the package root to keep __init__.py minimal and simple. +10. ✅ Revise `bittensor/utils/easy_imports.py` module to remove deprecated backwards compatibility objects. Use this module as a functionality for exporting existing objects to the package root to keep __init__.py minimal and simple. 11. ✅ Remove deprecated `bittensor.utils.version.version_checking` @@ -102,8 +102,8 @@ To implement the above changes and prepare for the v10 release, the following st All breaking changes and refactors should be targeted into this branch to isolate them from staging and maintain backward compatibility during development. - [ ] Add a `MIGRATION.md` document at the root of the repository and use it as a check list. This file will serve as a changelog and technical reference. It must include: - - [ ] All change categories (Extrinsics, Subtensor, Metagraph, etc.) - - [ ] Per-PR breakdown of what was added, removed, renamed, or refactored. + - [x] All change categories (Extrinsics, Subtensor, Metagraph, etc.) + - [x] Per-PR breakdown of what was added, removed, renamed, or refactored. - [ ] Justifications and migration notes for users (if API behavior changed). - [ ] Based on the final `MIGRATION.md`, develop migration documentation for the community. diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index 37aafe1e32..2210dce1f2 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -25,7 +25,7 @@ def _check_currencies(self, other): """ if self.netuid != other.netuid: raise TypeError( - f"Cannot perform arithmetic between balances of different currencies: {self} and {other}. " + f"Cannot perform any operations between balances of different currencies: {self} and {other}. " "Both Balance objects must reference the same netuid (Alpha currency). " "For example, to create a Balance instance for subnet 12 you can use: " "Balance.from_tao(10).set_unit(14), which corresponds to 10 TAO in subnet 14." @@ -43,6 +43,25 @@ class Balance: rao_unit (str): A string representing the symbol for the rao unit. rao (int): An integer that stores the balance in rao units. tao (float): A float property that gives the balance in tao units. + + Note: + To ensure arithmetic operations between `Balance` instances work correctly, they must set the same unit for each + using the `netuid`. + + Examples: + + balance_wallet_default = Balance.from_tao(10, netuid=14) + balance_wallet_secret = Balance.from_tao(2, netuid=14) + total_balance = balance_wallet_default + balance_wallet_secret + + # or + + balance_wallet_default = Balance.from_tao(10).set_unit(netuid=14) + balance_wallet_secret = Balance.from_tao(2).set_unit(netuid=14) + total_balance = balance_wallet_default + balance_wallet_secret + + The `from_tao()` and `from_rao()` methods accept the `netuid` parameter + to set the appropriate unit symbol. """ unit: str = settings.TAO_SYMBOL # This is the tao unit diff --git a/tests/e2e_tests/test_delegate.py b/tests/e2e_tests/test_delegate.py index 9659985413..3ebb405653 100644 --- a/tests/e2e_tests/test_delegate.py +++ b/tests/e2e_tests/test_delegate.py @@ -694,7 +694,7 @@ def test_nominator_min_required_stake(subtensor, alice_wallet, bob_wallet, dave_ hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, ) - assert stake == Balance(0) + assert stake == Balance.from_tao(0, alice_subnet_netuid) @pytest.mark.asyncio @@ -774,7 +774,7 @@ async def test_nominator_min_required_stake_async( hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, ) - assert stake == Balance(0) + assert stake == Balance.from_tao(0, alice_subnet_netuid) def test_get_vote_data(subtensor, alice_wallet): diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 270642cc6c..64b093ba8b 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -751,7 +751,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) assert partial_stake > Balance(0).set_unit(alice_subnet_netuid), ( "Partial stake should be added" ) - assert partial_stake < stake_amount, ( + assert partial_stake < Balance.from_tao(stake_amount.tao).set_unit(alice_subnet_netuid), ( "Partial stake should be less than requested amount" ) @@ -952,7 +952,7 @@ async def test_safe_staking_scenarios_async( assert partial_stake > Balance(0).set_unit(alice_subnet_netuid), ( "Partial stake should be added" ) - assert partial_stake < stake_amount, ( + assert partial_stake < Balance.from_tao(stake_amount.tao).set_unit(alice_subnet_netuid), ( "Partial stake should be less than requested amount" ) @@ -1333,9 +1333,9 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): netuid=alice_subnet_netuid if subtensor.chain.is_fast_blocks() else bob_subnet_netuid, - stake=get_dynamic_balance(stakes[0].stake.rao, bob_subnet_netuid), - locked=Balance(0).set_unit(bob_subnet_netuid), - emission=get_dynamic_balance(stakes[0].emission.rao, bob_subnet_netuid), + stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), + locked=Balance(0).set_unit(alice_subnet_netuid), + emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), drain=0, is_registered=True, ) @@ -1359,6 +1359,9 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): ) expected_stakes += fast_block_stake + logging.console.info(f"[orange]FS: {fast_block_stake}[/orange]") + logging.console.info(f"[orange]RS: {stakes}[/orange]") + logging.console.info(f"[orange]ES: {expected_stakes}[/orange]") assert stakes == expected_stakes # test move_stake with move_all_stake=True @@ -1502,9 +1505,9 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ netuid=alice_subnet_netuid if await async_subtensor.chain.is_fast_blocks() else bob_subnet_netuid, - stake=get_dynamic_balance(stakes[0].stake.rao, bob_subnet_netuid), - locked=Balance(0).set_unit(bob_subnet_netuid), - emission=get_dynamic_balance(stakes[0].emission.rao, bob_subnet_netuid), + stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), + locked=Balance(0).set_unit(alice_subnet_netuid), + emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), drain=0, is_registered=True, ) From fb997bc51d821044351cd97bc419e9edae6f596d Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 30 Sep 2025 09:52:54 -0700 Subject: [PATCH 30/41] add `hex_to_ss58` and `ss58_to_hex` to `bittensor.utils.__init__` --- MIGRATION.md | 6 +++--- bittensor/utils/__init__.py | 13 +++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index c1ced148a4..3f0015150f 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -75,9 +75,9 @@ rename this variable in documentation. 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`) -2. Implement Crowdloan logic. Issue: https://github.com/opentensor/bittensor/issues/3017 -3. ✅ Implement Sub-subnets logic. Subtensor PR https://github.com/opentensor/subtensor/pull/1984 +1. ✅ Add `bittensor.utils.hex_to_ss58` function. SDK still doesn't have it. (Probably inner import `from scalecodec import ss58_encode, ss58_decode`) +2. ✅ Implement Sub-subnets logic. Subtensor PR https://github.com/opentensor/subtensor/pull/1984 +3. Implement `Crowdloan` logic. Issue: https://github.com/opentensor/bittensor/issues/3017 ## Testing 1. ✅ When running tests via Docker, ensure no lingering processes occupy required ports before launch. diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index 0ffcdb37c1..7d58d94de3 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -1,18 +1,23 @@ import ast import decimal import hashlib +import inspect import warnings from collections import namedtuple from typing import Any, Literal, Union, Optional, TYPE_CHECKING from urllib.parse import urlparse -import inspect + import scalecodec from async_substrate_interface.utils import ( hex_to_bytes, ) from bittensor_wallet import Keypair from bittensor_wallet.errors import KeyFileError, PasswordError -from scalecodec import ss58_decode, is_valid_ss58_address as _is_valid_ss58_address +from scalecodec import ( + ss58_decode, + ss58_encode, + is_valid_ss58_address as _is_valid_ss58_address, +) from bittensor.core import settings from bittensor.core.settings import SS58_FORMAT @@ -24,6 +29,10 @@ from bittensor_wallet import Wallet from bittensor.utils.balance import Balance +# keep save from import analyzer as obvious aliases +hex_to_ss58 = ss58_encode +ss58_to_hex = ss58_decode + BT_DOCS_LINK = "https://docs.bittensor.com" RAOPERTAO = 1e9 U16_MAX = 65535 From 5ad650a9df017def8552ce265890e945d00ddaa3 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 30 Sep 2025 11:27:07 -0700 Subject: [PATCH 31/41] new custom errors for balance check --- bittensor/core/errors.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bittensor/core/errors.py b/bittensor/core/errors.py index 15eb9e0446..b1d2c510eb 100644 --- a/bittensor/core/errors.py +++ b/bittensor/core/errors.py @@ -212,3 +212,11 @@ def __init__( ): self.message = message super().__init__(self.message, synapse) + + +class BalanceUnitMismatchError(Exception): + """Raised when operations is attempted between Balance objects with different units (netuid).""" + + +class BalanceTypeError(Exception): + """Raised when an unsupported type is used instead of Balance amount.""" From c67491473fbeb8e42884c1c11e0c4aa4b9f2996d Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 30 Sep 2025 11:27:43 -0700 Subject: [PATCH 32/41] `check_and_convert_to_balance` renamed to `check_balance_amount` + new logic across the code --- MIGRATION.md | 13 +++++++-- bittensor/core/async_subtensor.py | 16 +++++------ bittensor/core/subtensor.py | 16 +++++------ bittensor/utils/balance.py | 46 ++++++++++++++++++------------- bittensor/utils/easy_imports.py | 4 +++ 5 files changed, 58 insertions(+), 37 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 3f0015150f..1521ac5fe0 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -37,7 +37,7 @@ ## Balance 1. ✅ In `bittensor.utils.balance._check_currencies` raise the error instead of `warnings.warn`. -2. In `bittensor.utils.balance.check_and_convert_to_balance` raise the error instead of `warnings.warn`. +2. ✅ In `bittensor.utils.balance.check_and_convert_to_balance` raise the error instead of `warnings.warn`. This may seem like a harsh decision at first, but ultimately we will push the community to use Balance and there will be fewer errors in their calculations. Confusion with TAO and Alpha in calculations and display/printing/logging will be eliminated. ## Common things @@ -290,4 +290,13 @@ Links to subpackages removed: New subpackage `bittensor.addons` created to host optional extensions and experimental logic enhancing the core functionality. Currently it contains: - `bittensor.addons.subtensor_api` -- `bittensor.addons.timelock` \ No newline at end of file +- `bittensor.addons.timelock` + +### Balance (bittensor/utils/balance.py) and related changes +- [x] Added 2 custom errors: + - `bittensor.core.errors.BalanceUnitMismatchError` + - `bittensor.core.errors.BalanceTypeError` +- [x] `check_balance` renamed to `check_balance_amount` +- [x] `check_and_convert_to_balance` renamed to `check_balance_amount` +- [x] `check_balance_amount` raised `BalanceTypeError` error instead of deprecated warning message. +- [x] private function `bittensor.utils.balance._check_currencies` raises `BalanceUnitMismatchError` error instead of deprecated warning message. This function is used inside the Balance class to check if units match during various mathematical and logical operations. \ No newline at end of file diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 9acc8c75b3..56fd0bf64e 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -109,7 +109,7 @@ from bittensor.utils.balance import ( Balance, fixed_to_float, - check_and_convert_to_balance, + check_balance_amount, ) from bittensor.utils.btlogging import logging from bittensor.utils.liquidity import ( @@ -3143,7 +3143,7 @@ async def get_transfer_fee( a crucial tool for managing financial operations within the Bittensor network. """ if value is not None: - value = check_and_convert_to_balance(value) + value = check_balance_amount(value) call_params: dict[str, Union[int, str, bool]] call_function, call_params = get_transfer_fn_params(value, dest, keep_alive) @@ -4330,7 +4330,7 @@ async def add_stake( 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) + amount = check_balance_amount(amount) return await add_stake_extrinsic( subtensor=self, wallet=wallet, @@ -4676,7 +4676,7 @@ async def move_stake( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - amount = check_and_convert_to_balance(amount) + amount = check_balance_amount(amount) return await move_stake_extrinsic( subtensor=self, wallet=wallet, @@ -5527,7 +5527,7 @@ async def swap_stake( - With allow_partial_stake=True: A partial amount will be swapped up to the point where the price ratio would increase by rate_tolerance. """ - amount = check_and_convert_to_balance(amount) + amount = check_balance_amount(amount) return await swap_stake_extrinsic( subtensor=self, wallet=wallet, @@ -5615,7 +5615,7 @@ async def transfer( ExtrinsicResponse: The result object of the extrinsic execution. """ if amount is not None: - amount = check_and_convert_to_balance(amount) + amount = check_balance_amount(amount) return await transfer_extrinsic( subtensor=self, wallet=wallet, @@ -5662,7 +5662,7 @@ async def transfer_stake( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - amount = check_and_convert_to_balance(amount) + amount = check_balance_amount(amount) return await transfer_stake_extrinsic( subtensor=self, wallet=wallet, @@ -5720,7 +5720,7 @@ async def unstake( This function supports flexible stake management, allowing neurons to adjust their network participation and potential reward accruals. """ - amount = check_and_convert_to_balance(amount) + amount = check_balance_amount(amount) return await unstake_extrinsic( subtensor=self, wallet=wallet, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 78ba592f7b..9e862241c6 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -113,7 +113,7 @@ Balance, fixed_to_float, FixedPoint, - check_and_convert_to_balance, + check_balance_amount, ) from bittensor.utils.btlogging import logging from bittensor.utils.liquidity import ( @@ -2275,7 +2275,7 @@ def get_transfer_fee( crucial tool for managing financial operations within the Bittensor network. """ if value is not None: - value = check_and_convert_to_balance(value) + value = check_balance_amount(value) call_params: dict[str, Union[int, str, bool]] call_function, call_params = get_transfer_fn_params(value, dest, keep_alive) @@ -3189,7 +3189,7 @@ def add_stake( 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) + amount = check_balance_amount(amount) return add_stake_extrinsic( subtensor=self, wallet=wallet, @@ -3532,7 +3532,7 @@ def move_stake( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - amount = check_and_convert_to_balance(amount) + amount = check_balance_amount(amount) return move_stake_extrinsic( subtensor=self, wallet=wallet, @@ -4364,7 +4364,7 @@ def swap_stake( - With allow_partial_stake=True: A partial amount will be swapped up to the point where the price ratio would increase by rate_tolerance """ - amount = check_and_convert_to_balance(amount) + amount = check_balance_amount(amount) return swap_stake_extrinsic( subtensor=self, wallet=wallet, @@ -4452,7 +4452,7 @@ def transfer( ExtrinsicResponse: The result object of the extrinsic execution. """ if amount is not None: - amount = check_and_convert_to_balance(amount) + amount = check_balance_amount(amount) return transfer_extrinsic( subtensor=self, wallet=wallet, @@ -4499,7 +4499,7 @@ def transfer_stake( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - amount = check_and_convert_to_balance(amount) + amount = check_balance_amount(amount) return transfer_stake_extrinsic( subtensor=self, wallet=wallet, @@ -4558,7 +4558,7 @@ def unstake( 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) + amount = check_balance_amount(amount) return unstake_extrinsic( subtensor=self, wallet=wallet, diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index 2210dce1f2..e68f3a0e1a 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -1,9 +1,9 @@ -from typing import Union, TypedDict, Optional +from typing import Any, TypedDict, Union from scalecodec import ScaleType from bittensor.core import settings -from bittensor.utils import deprecated_message +from bittensor.core.errors import BalanceTypeError, BalanceUnitMismatchError def _check_currencies(self, other): @@ -24,7 +24,7 @@ def _check_currencies(self, other): - `set_unit(14)` sets the unit to correspond to subnet 14 (i.e., Alpha from netuid 14). """ if self.netuid != other.netuid: - raise TypeError( + raise BalanceUnitMismatchError( f"Cannot perform any operations between balances of different currencies: {self} and {other}. " "Both Balance objects must reference the same netuid (Alpha currency). " "For example, to create a Balance instance for subnet 12 you can use: " @@ -35,8 +35,9 @@ def _check_currencies(self, other): class Balance: """ Represents the bittensor balance of the wallet, stored as rao (int). - This class provides a way to interact with balances in two different units: rao and tao. - It provides methods to convert between these units, as well as to perform arithmetic and comparison operations. + + This class provides a way to interact with balances in two different units: rao and tao. It provides methods to + convert between these units, as well as to perform arithmetic and comparison operations. Attributes: unit (str): A string representing the symbol for the tao unit. @@ -60,8 +61,19 @@ class Balance: balance_wallet_secret = Balance.from_tao(2).set_unit(netuid=14) total_balance = balance_wallet_default + balance_wallet_secret - The `from_tao()` and `from_rao()` methods accept the `netuid` parameter - to set the appropriate unit symbol. + The `from_tao()` and `from_rao()` methods accept the `netuid` parameter to set the appropriate unit symbol. + + Note: + When performing arithmetic or comparison operations where the first operand is a `Balance` instance and the + second operand is not, the second operand is implicitly interpreted as a raw amount in `rao`, using the same + unit (netuid) as the first operand. This allows interoperability with integer or float values, but may result in + unexpected behavior if the caller assumes the second operand is in `tao`. + + Example: + balance = Balance.from_tao(10, netuid=14) + result = balance + 5000 # 5 will be treated as 5000 rao, not 5 tao + print(result) + output: τ10.000005000 """ unit: str = settings.TAO_SYMBOL # This is the tao unit @@ -839,17 +851,13 @@ def rao(amount: int, netuid: int = 0) -> Balance: return Balance.from_rao(amount).set_unit(netuid) -def check_and_convert_to_balance( - amount: Union[float, int, Optional[Balance]], -) -> Balance: - """ - Helper function to check and convert the amount type to a Balance object. - This is used to support backwards compatibility while also providing a deprecation notice. - """ - if isinstance(amount, (float, int)): - deprecated_message( - "Detected a non-balance amount. Converting to Balance from Tao for backwards compatibility." - "Please update your code to use tao(amount) or Balance.from_tao(amount) for the main release 10.0.0." +def check_balance_amount(amount: Any) -> Balance: + """""" + if not isinstance(amount, Balance): + raise BalanceTypeError( + "Invalid type detected: expected a Balance instance. " + "Passing non-Balance types may lead to incorrect calculations. " + "Please update your code to explicitly construct Balance instances " + "(e.g., Balance.from_tao(value)) before using this function." ) - amount = tao(amount) return amount diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 9eea34055a..8d7689bf81 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -62,6 +62,8 @@ from bittensor.core.config import Config from bittensor.core.dendrite import Dendrite from bittensor.core.errors import ( + BalanceTypeError, + BalanceUnitMismatchError, BlacklistedException, ChainConnectionError, ChainError, @@ -169,6 +171,8 @@ "WeightCommitInfo", "Config", "Dendrite", + "BalanceTypeError", + "BalanceUnitMismatchError", "BlacklistedException", "ChainConnectionError", "ChainError", From b9d299bb3980eb9e8ce6789e5d69c4ae9d2a6fc7 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 30 Sep 2025 11:27:50 -0700 Subject: [PATCH 33/41] improve tests --- tests/unit_tests/test_async_subtensor.py | 14 +++++------ tests/unit_tests/test_subtensor.py | 20 +++++++-------- tests/unit_tests/utils/test_balance.py | 32 ++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 6855eb72cd..a96f863110 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -1,6 +1,6 @@ import datetime import unittest.mock as mock - +from bittensor.core.errors import BalanceTypeError import pytest from async_substrate_interface.types import ScaleObj from bittensor_wallet import Wallet @@ -710,13 +710,11 @@ async def test_get_transfer_with_exception(subtensor, mocker): subtensor.substrate.compose_call = mocked_compose_call subtensor.substrate.get_payment_info.side_effect = Exception - # Call - result = await subtensor.get_transfer_fee( - wallet=mocker.Mock(), dest=mocker.Mock(), value=fake_value - ) - - # Assertions - assert result == async_subtensor.Balance.from_rao(int(2e7)) + # Call + Assertions + with pytest.raises(BalanceTypeError): + await subtensor.get_transfer_fee( + wallet=mocker.Mock(), dest=mocker.Mock(), value=fake_value + ) @pytest.mark.asyncio diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index f45f71d51b..be96e33808 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1252,7 +1252,7 @@ def test_transfer(subtensor, fake_wallet, mocker): """Tests successful transfer call.""" # Prep fake_dest = "SS58PUBLICKEY" - fake_amount = 1.1 + fake_amount = Balance.from_tao(1.1) fake_wait_for_inclusion = True fake_wait_for_finalization = True mocked_transfer_extrinsic = mocker.patch.object( @@ -1273,7 +1273,7 @@ def test_transfer(subtensor, fake_wallet, mocker): subtensor=subtensor, wallet=fake_wallet, destination=fake_dest, - amount=Balance(fake_amount), + amount=fake_amount, transfer_all=False, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, @@ -2727,7 +2727,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): # Preps fake_hotkey_ss58 = "hotkey_1" fake_netuid = 1 - fake_amount = 10.0 + fake_amount = Balance.from_tao(10.0) mock_unstake_extrinsic = mocker.patch.object(subtensor_module, "unstake_extrinsic") @@ -2750,7 +2750,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): wallet=fake_wallet, netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, - amount=Balance.from_rao(fake_amount), + amount=fake_amount, safe_unstaking=False, allow_partial_stake=False, rate_tolerance=0.005, @@ -2765,7 +2765,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): 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_amount = Balance.from_tao(10.0) fake_netuid = 14 fake_rate_tolerance = 0.01 # 1% threshold @@ -2790,7 +2790,7 @@ def test_unstake_with_safe_unstaking(mocker, subtensor, fake_wallet): wallet=fake_wallet, netuid=fake_netuid, hotkey_ss58=fake_hotkey_ss58, - amount=Balance.from_rao(fake_amount), + amount=fake_amount, safe_unstaking=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, @@ -2808,7 +2808,7 @@ def test_swap_stake_success(mocker, subtensor, fake_wallet): fake_hotkey_ss58 = "hotkey_1" fake_origin_netuid = 1 fake_destination_netuid = 2 - fake_amount = 10.0 + fake_amount = Balance.from_tao(10.0) mock_swap_stake_extrinsic = mocker.patch.object( subtensor_module, "swap_stake_extrinsic" @@ -2835,7 +2835,7 @@ def test_swap_stake_success(mocker, subtensor, fake_wallet): hotkey_ss58=fake_hotkey_ss58, origin_netuid=fake_origin_netuid, destination_netuid=fake_destination_netuid, - amount=Balance.from_rao(fake_amount), + amount=fake_amount, wait_for_inclusion=True, wait_for_finalization=False, safe_swapping=False, @@ -2853,7 +2853,7 @@ def test_swap_stake_with_safe_staking(mocker, subtensor, fake_wallet): fake_hotkey_ss58 = "hotkey_1" fake_origin_netuid = 1 fake_destination_netuid = 2 - fake_amount = 10.0 + fake_amount = Balance.from_tao(10.0) fake_rate_tolerance = 0.01 # 1% threshold mock_swap_stake_extrinsic = mocker.patch.object( @@ -2881,7 +2881,7 @@ def test_swap_stake_with_safe_staking(mocker, subtensor, fake_wallet): hotkey_ss58=fake_hotkey_ss58, origin_netuid=fake_origin_netuid, destination_netuid=fake_destination_netuid, - amount=Balance.from_rao(fake_amount), + amount=fake_amount, wait_for_inclusion=True, wait_for_finalization=False, safe_swapping=True, diff --git a/tests/unit_tests/utils/test_balance.py b/tests/unit_tests/utils/test_balance.py index 4ff97bdb81..fab62002c3 100644 --- a/tests/unit_tests/utils/test_balance.py +++ b/tests/unit_tests/utils/test_balance.py @@ -5,8 +5,8 @@ import pytest from hypothesis import given from hypothesis import strategies as st - -from bittensor.utils.balance import Balance +from bittensor.core.errors import BalanceTypeError, BalanceUnitMismatchError +from bittensor.utils.balance import Balance, check_balance_amount from tests.helpers import CloseInValue @@ -518,3 +518,31 @@ def test_from_float(): def test_from_rao(): """Tests from_rao method call.""" assert Balance.from_tao(1) == Balance(1000000000) + + +@pytest.mark.parametrize( + "first, second", + [ + (Balance.from_tao(1), Balance.from_tao(1).set_unit(2)), + (Balance.from_tao(1).set_unit(2), Balance.from_tao(1)), + ], +) +def test_balance_raise_errors(first, second): + """Tests any cases with balance raise errors.""" + with pytest.raises(BalanceUnitMismatchError): + _ = first + second + + +@pytest.mark.parametrize( + "amount", + [ + 100, + 100.1, + "100", + "10.2" + ], +) +def test_check_balance_amount_raise_error(amount): + """Tests Balance.check_rao_value method.""" + with pytest.raises(BalanceTypeError): + check_balance_amount(amount) From 74b1886b91e8d00d63e7b9d3c769914c86aec33d Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 30 Sep 2025 12:18:36 -0700 Subject: [PATCH 34/41] add `check_balance_amount` across subtensor's and extrinsics codebase + fixed unit tests --- MIGRATION.md | 5 ++-- bittensor/core/async_subtensor.py | 26 +++++++++--------- bittensor/core/extrinsics/asyncex/staking.py | 6 +++-- .../core/extrinsics/asyncex/unstaking.py | 3 ++- bittensor/core/extrinsics/staking.py | 3 ++- bittensor/core/extrinsics/unstaking.py | 3 ++- bittensor/core/subtensor.py | 26 +++++++++--------- bittensor/utils/balance.py | 27 +++++++++++++++---- tests/unit_tests/test_async_subtensor.py | 14 +++++----- tests/unit_tests/test_subtensor.py | 8 +++--- 10 files changed, 74 insertions(+), 47 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index 1521ac5fe0..e7c3bc3f3f 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -100,11 +100,11 @@ To implement the above changes and prepare for the v10 release, the following st - [x] Create a new branch named SDKv10.~~ All breaking changes and refactors should be targeted into this branch to isolate them from staging and maintain backward compatibility during development. -- [ ] Add a `MIGRATION.md` document at the root of the repository and use it as a check list. This file will serve as a changelog and technical reference. +- [x] Add a `MIGRATION.md` document at the root of the repository and use it as a check list. This file will serve as a changelog and technical reference. It must include: - [x] All change categories (Extrinsics, Subtensor, Metagraph, etc.) - [x] Per-PR breakdown of what was added, removed, renamed, or refactored. - - [ ] Justifications and migration notes for users (if API behavior changed). + - [x] Justifications and migration notes for users (if API behavior changed). - [ ] Based on the final `MIGRATION.md`, develop migration documentation for the community. - [ ] Once complete, merge SDKv10 into staging and release version 10. @@ -241,6 +241,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - methods (async) `get_subnet_validator_permits` and `get_subnet_owner_hotkey` got `block_hash` and `reuse_block` parameters. - attribute `DelegateInfo/lite.total_daily_return` has been deleted (Vune confirmed that we shouldn't use it) - `Async/Subtensor` parameter `_mock` renamed to `mock`, also moved to last one in order. Community can use mocked `Async/Subtensor` in their tests in the same way as in we use it in the codebase. +- method `get_traansfer_fee` has renamed parameter `value` to `amount` Added sub-package `bittensor.core.addons` to host optional extensions and experimental logic enhancing the core functionality. - `bittensor.core.subtensor_api` moved to `bittensor.core.addons.subtensor_api` diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 56fd0bf64e..a89557f427 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -2545,6 +2545,7 @@ async def get_stake_add_fee( Returns: The calculated stake fee as a Balance object """ + check_balance_amount(amount) return await self.get_stake_operations_fee( amount=amount, netuid=netuid, block=block ) @@ -2773,6 +2774,7 @@ async def get_unstake_fee( Returns: The calculated stake fee as a Balance object """ + check_balance_amount(amount) return await self.get_stake_operations_fee( amount=amount, netuid=netuid, block=block ) @@ -2795,6 +2797,7 @@ async def get_stake_movement_fee( Returns: The calculated stake fee as a Balance object """ + check_balance_amount(amount) return await self.get_stake_operations_fee( amount=amount, netuid=origin_netuid, block=block ) @@ -2932,6 +2935,7 @@ async def get_stake_operations_fee( Returns: The calculated stake fee as a Balance object. """ + check_balance_amount(amount) block_hash = await self.determine_block_hash( block=block, block_hash=block_hash, reuse_block=reuse_block ) @@ -3119,7 +3123,7 @@ async def get_total_subnets( # TODO: update related with fee calculation async def get_transfer_fee( - self, wallet: "Wallet", dest: str, value: Balance, keep_alive: bool = True + self, wallet: "Wallet", dest: str, amount: Balance, keep_alive: bool = True ) -> Balance: """ Calculates the transaction fee for transferring tokens from a wallet to a specified destination address. This @@ -3129,7 +3133,7 @@ async def get_transfer_fee( Parameters: wallet: The wallet from which the transfer is initiated. dest: The ``SS58`` address of the destination account. - value: The amount of tokens to be transferred, specified as a Balance object, or in Tao (float) or Rao + amount: The amount of tokens to be transferred, specified as a Balance object, or in Tao (float) or Rao (int) units. keep_alive: Whether the transfer fee should be calculated based on keeping the wallet alive (existential deposit) or not. @@ -3142,10 +3146,9 @@ async def get_transfer_fee( wallet has sufficient funds to cover both the transfer amount and the associated costs. This function provides a crucial tool for managing financial operations within the Bittensor network. """ - if value is not None: - value = check_balance_amount(value) + check_balance_amount(amount) call_params: dict[str, Union[int, str, bool]] - call_function, call_params = get_transfer_fn_params(value, dest, keep_alive) + call_function, call_params = get_transfer_fn_params(amount, dest, keep_alive) call = await self.substrate.compose_call( call_module="Balances", @@ -4330,7 +4333,7 @@ async def add_stake( 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_balance_amount(amount) + check_balance_amount(amount) return await add_stake_extrinsic( subtensor=self, wallet=wallet, @@ -4676,7 +4679,7 @@ async def move_stake( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - amount = check_balance_amount(amount) + check_balance_amount(amount) return await move_stake_extrinsic( subtensor=self, wallet=wallet, @@ -5527,7 +5530,7 @@ async def swap_stake( - With allow_partial_stake=True: A partial amount will be swapped up to the point where the price ratio would increase by rate_tolerance. """ - amount = check_balance_amount(amount) + check_balance_amount(amount) return await swap_stake_extrinsic( subtensor=self, wallet=wallet, @@ -5614,8 +5617,7 @@ async def transfer( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - if amount is not None: - amount = check_balance_amount(amount) + check_balance_amount(amount) return await transfer_extrinsic( subtensor=self, wallet=wallet, @@ -5662,7 +5664,7 @@ async def transfer_stake( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - amount = check_balance_amount(amount) + check_balance_amount(amount) return await transfer_stake_extrinsic( subtensor=self, wallet=wallet, @@ -5720,7 +5722,7 @@ async def unstake( This function supports flexible stake management, allowing neurons to adjust their network participation and potential reward accruals. """ - amount = check_balance_amount(amount) + check_balance_amount(amount) return await unstake_extrinsic( subtensor=self, wallet=wallet, diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index a6dc7208eb..271ced2c99 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -2,9 +2,11 @@ from typing import Optional, Sequence, TYPE_CHECKING from async_substrate_interface.errors import SubstrateRequestException + +from bittensor.core.errors import BalanceTypeError from bittensor.core.extrinsics.utils import get_old_stakes -from bittensor.utils import format_error_message from bittensor.core.types import ExtrinsicResponse, UIDs +from bittensor.utils import format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -257,7 +259,7 @@ async def add_stake_multiple_extrinsic( raise TypeError("`hotkey_ss58s` must be a list of str.") if not all(isinstance(a, Balance) for a in amounts): - raise TypeError("Each `amount` must be an instance of Balance.") + raise BalanceTypeError("Each `amount` must be an instance of Balance.") new_amounts: Sequence[Optional[Balance]] = [ amount.set_unit(netuid) for amount, netuid in zip(amounts, netuids) diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index d3acf3f9bc..d666716b56 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -3,6 +3,7 @@ from async_substrate_interface.errors import SubstrateRequestException +from bittensor.core.errors import BalanceTypeError from bittensor.core.extrinsics.utils import get_old_stakes from bittensor.core.types import ExtrinsicResponse from bittensor.core.types import UIDs @@ -305,7 +306,7 @@ async def unstake_multiple_extrinsic( if amounts is not None and not all( isinstance(amount, Balance) for amount in amounts ): - raise TypeError("`amounts` must be a list of Balance or None.") + raise BalanceTypeError("`amounts` must be a list of Balance or None.") if amounts is None: amounts = [None] * len(hotkey_ss58s) diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 190a3a7ed5..0cd3f8481b 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -2,6 +2,7 @@ from async_substrate_interface.errors import SubstrateRequestException +from bittensor.core.errors import BalanceTypeError from bittensor.core.extrinsics.utils import get_old_stakes from bittensor.core.types import ExtrinsicResponse, UIDs from bittensor.utils import format_error_message @@ -251,7 +252,7 @@ def add_stake_multiple_extrinsic( raise TypeError("`hotkey_ss58s` must be a list of str.") if not all(isinstance(a, Balance) for a in amounts): - raise TypeError("Each `amount` must be an instance of Balance.") + raise BalanceTypeError("Each `amount` must be an instance of Balance.") new_amounts: Sequence[Optional[Balance]] = [ amount.set_unit(netuid) for amount, netuid in zip(amounts, netuids) diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index 45bda590d0..11c19f1423 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -2,6 +2,7 @@ from async_substrate_interface.errors import SubstrateRequestException +from bittensor.core.errors import BalanceTypeError from bittensor.core.extrinsics.utils import get_old_stakes from bittensor.core.types import ExtrinsicResponse, UIDs from bittensor.utils import format_error_message @@ -300,7 +301,7 @@ def unstake_multiple_extrinsic( if amounts is not None and not all( isinstance(amount, Balance) for amount in amounts ): - raise TypeError("`amounts` must be a list of Balance or None.") + raise BalanceTypeError("`amounts` must be a list of Balance or None.") if amounts is None: amounts = [None] * len(hotkey_ss58s) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 9e862241c6..3a7fc2b26f 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1793,6 +1793,7 @@ def get_stake_add_fee( Returns: The calculated stake fee as a Balance object """ + check_balance_amount(amount) return self.get_stake_operations_fee(amount=amount, netuid=netuid, block=block) def get_mechanism_emission_split( @@ -1983,6 +1984,7 @@ def get_unstake_fee( Returns: The calculated stake fee as a Balance object """ + check_balance_amount(amount) return self.get_stake_operations_fee(amount=amount, netuid=netuid, block=block) # TODO: update related with fee calculation @@ -2003,6 +2005,7 @@ def get_stake_movement_fee( Returns: The calculated stake fee as a Balance object """ + check_balance_amount(amount) return self.get_stake_operations_fee( amount=amount, netuid=origin_netuid, block=block ) @@ -2106,6 +2109,7 @@ def get_stake_operations_fee( Returns: The calculated stake fee as a Balance object. """ + check_balance_amount(amount) block_hash = self.determine_block_hash(block=block) result = self.substrate.query( module="Swap", @@ -2252,7 +2256,7 @@ def get_transfer_fee( self, wallet: "Wallet", dest: str, - value: Optional[Balance], + amount: Optional[Balance], keep_alive: bool = True, ) -> Balance: """ @@ -2263,7 +2267,7 @@ def get_transfer_fee( Parameters: wallet: The wallet from which the transfer is initiated. dest: The ``SS58`` address of the destination account. - value: The amount of tokens to be transferred, specified as a Balance object, or in Tao or Rao units. + amount: The amount of tokens to be transferred, specified as a Balance object, or in Tao or Rao units. keep_alive: Whether the transfer fee should be calculated based on keeping the wallet alive (existential deposit) or not. @@ -2274,10 +2278,9 @@ def get_transfer_fee( has sufficient funds to cover both the transfer amount and the associated costs. This function provides a crucial tool for managing financial operations within the Bittensor network. """ - if value is not None: - value = check_balance_amount(value) + check_balance_amount(amount) call_params: dict[str, Union[int, str, bool]] - call_function, call_params = get_transfer_fn_params(value, dest, keep_alive) + call_function, call_params = get_transfer_fn_params(amount, dest, keep_alive) call = self.substrate.compose_call( call_module="Balances", @@ -3189,7 +3192,7 @@ def add_stake( 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_balance_amount(amount) + check_balance_amount(amount) return add_stake_extrinsic( subtensor=self, wallet=wallet, @@ -3532,7 +3535,7 @@ def move_stake( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - amount = check_balance_amount(amount) + check_balance_amount(amount) return move_stake_extrinsic( subtensor=self, wallet=wallet, @@ -4364,7 +4367,7 @@ def swap_stake( - With allow_partial_stake=True: A partial amount will be swapped up to the point where the price ratio would increase by rate_tolerance """ - amount = check_balance_amount(amount) + check_balance_amount(amount) return swap_stake_extrinsic( subtensor=self, wallet=wallet, @@ -4451,8 +4454,7 @@ def transfer( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - if amount is not None: - amount = check_balance_amount(amount) + check_balance_amount(amount) return transfer_extrinsic( subtensor=self, wallet=wallet, @@ -4499,7 +4501,7 @@ def transfer_stake( Returns: ExtrinsicResponse: The result object of the extrinsic execution. """ - amount = check_balance_amount(amount) + check_balance_amount(amount) return transfer_stake_extrinsic( subtensor=self, wallet=wallet, @@ -4558,7 +4560,7 @@ def unstake( 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_balance_amount(amount) + check_balance_amount(amount) return unstake_extrinsic( subtensor=self, wallet=wallet, diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index e68f3a0e1a..996dc35262 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -1,4 +1,4 @@ -from typing import Any, TypedDict, Union +from typing import Optional, TypedDict, Union from scalecodec import ScaleType @@ -851,13 +851,30 @@ def rao(amount: int, netuid: int = 0) -> Balance: return Balance.from_rao(amount).set_unit(netuid) -def check_balance_amount(amount: Any) -> Balance: - """""" +def check_balance_amount(amount: Optional[Balance]) -> Optional[None]: + """ + Validate that the provided value is a Balance instance. + + This function ensures that the `amount` argument is a `Balance` object. If a non-Balance type is passed, it raises + a `BalanceTypeError` to enforce consistent usage of Balance objects across arithmetic operations. + + Args: + amount: The value to validate. + + Returns: + None if amount if None of a Balance instance. + + Raises: + BalanceTypeError: If `amount` is not an instance of Balance. + """ + if amount is None: + return None + if not isinstance(amount, Balance): raise BalanceTypeError( - "Invalid type detected: expected a Balance instance. " + f"Invalid type detected: amount type is {type(amount)}, but expected a Balance instance. " "Passing non-Balance types may lead to incorrect calculations. " "Please update your code to explicitly construct Balance instances " "(e.g., Balance.from_tao(value)) before using this function." ) - return amount + return None diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index a96f863110..50348838eb 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -662,14 +662,14 @@ async def test_get_balance(subtensor, mocker): assert result == mocked_balance.return_value -@pytest.mark.parametrize("balance", [100, 100.1]) +@pytest.mark.parametrize("balance", [Balance.from_tao(100), Balance.from_tao(100.1)]) @pytest.mark.asyncio async def test_get_transfer_fee(subtensor, fake_wallet, mocker, balance): """Tests get_transfer_fee method.""" # Preps fake_wallet.coldkeypub = "coldkeypub" fake_dest = "fake_dest" - fake_value = Balance(balance) + fake_value = balance mocked_compose_call = mocker.AsyncMock() subtensor.substrate.compose_call = mocked_compose_call @@ -679,7 +679,7 @@ async def test_get_transfer_fee(subtensor, fake_wallet, mocker, balance): # Call result = await subtensor.get_transfer_fee( - wallet=fake_wallet, dest=fake_dest, value=fake_value + wallet=fake_wallet, dest=fake_dest, amount=fake_value ) # Assertions @@ -713,7 +713,7 @@ async def test_get_transfer_with_exception(subtensor, mocker): # Call + Assertions with pytest.raises(BalanceTypeError): await subtensor.get_transfer_fee( - wallet=mocker.Mock(), dest=mocker.Mock(), value=fake_value + wallet=mocker.Mock(), dest=mocker.Mock(), amount=fake_value ) @@ -3807,7 +3807,7 @@ async def test_get_stake_add_fee(subtensor, mocker): """Verify that `get_stake_add_fee` calls proper methods and returns the correct value.""" # Preps netuid = mocker.Mock() - amount = mocker.Mock() + amount = mocker.Mock(spec=Balance) mocked_get_stake_operations_fee = mocker.patch.object( subtensor, "get_stake_operations_fee" ) @@ -3830,7 +3830,7 @@ async def test_get_unstake_fee(subtensor, mocker): """Verify that `get_unstake_fee` calls proper methods and returns the correct value.""" # Preps netuid = mocker.Mock() - amount = mocker.Mock() + amount = mocker.Mock(spec=Balance) mocked_get_stake_operations_fee = mocker.patch.object( subtensor, "get_stake_operations_fee" ) @@ -3853,7 +3853,7 @@ async def test_get_stake_movement_fee(subtensor, mocker): """Verify that `get_stake_movement_fee` calls proper methods and returns the correct value.""" # Preps netuid = mocker.Mock() - amount = mocker.Mock() + amount = mocker.Mock(spec=Balance) mocked_get_stake_operations_fee = mocker.patch.object( subtensor, "get_stake_operations_fee" ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index be96e33808..3f31bcf0a7 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1729,7 +1729,7 @@ def test_get_transfer_fee(subtensor, fake_wallet, mocker): subtensor.substrate.get_payment_info.return_value = fake_payment_info # Call - result = subtensor.get_transfer_fee(wallet=fake_wallet, dest=fake_dest, value=value) + result = subtensor.get_transfer_fee(wallet=fake_wallet, dest=fake_dest, amount=value) # Asserts subtensor.substrate.compose_call.assert_called_once_with( @@ -3993,7 +3993,7 @@ def test_get_stake_add_fee(subtensor, mocker): """Verify that `get_stake_add_fee` calls proper methods and returns the correct value.""" # Preps netuid = mocker.Mock() - amount = mocker.Mock() + amount = mocker.Mock(spec=Balance) mocked_get_stake_operations_fee = mocker.patch.object( subtensor, "get_stake_operations_fee" ) @@ -4015,7 +4015,7 @@ def test_get_unstake_fee(subtensor, mocker): """Verify that `get_unstake_fee` calls proper methods and returns the correct value.""" # Preps netuid = mocker.Mock() - amount = mocker.Mock() + amount = mocker.Mock(spec=Balance) mocked_get_stake_operations_fee = mocker.patch.object( subtensor, "get_stake_operations_fee" ) @@ -4037,7 +4037,7 @@ def test_get_stake_movement_fee(subtensor, mocker): """Verify that `get_stake_movement_fee` calls proper methods and returns the correct value.""" # Preps netuid = mocker.Mock() - amount = mocker.Mock() + amount = mocker.Mock(spec=Balance) mocked_get_stake_operations_fee = mocker.patch.object( subtensor, "get_stake_operations_fee" ) From 614e26d9d1e2ebf46ff4bcf1024a85ae78391e49 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 30 Sep 2025 12:40:55 -0700 Subject: [PATCH 35/41] fix `get_transfer_fee` --- bittensor/core/extrinsics/asyncex/transfer.py | 2 +- bittensor/core/extrinsics/transfer.py | 2 +- tests/e2e_tests/test_transfer.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/transfer.py b/bittensor/core/extrinsics/asyncex/transfer.py index f8dc6f8b49..3d4b718dfc 100644 --- a/bittensor/core/extrinsics/asyncex/transfer.py +++ b/bittensor/core/extrinsics/asyncex/transfer.py @@ -75,7 +75,7 @@ async def transfer_extrinsic( ) fee = await subtensor.get_transfer_fee( - wallet=wallet, dest=destination, value=amount, keep_alive=keep_alive + wallet=wallet, dest=destination, amount=amount, keep_alive=keep_alive ) if not keep_alive: diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index 888f9b1ade..56bd44f0e8 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -74,7 +74,7 @@ def transfer_extrinsic( existential_deposit = subtensor.get_existential_deposit(block=block) fee = subtensor.get_transfer_fee( - wallet=wallet, dest=destination, value=amount, keep_alive=keep_alive + wallet=wallet, dest=destination, amount=amount, keep_alive=keep_alive ) # Check if we have enough balance. diff --git a/tests/e2e_tests/test_transfer.py b/tests/e2e_tests/test_transfer.py index d372f4d33d..dc8e69deb3 100644 --- a/tests/e2e_tests/test_transfer.py +++ b/tests/e2e_tests/test_transfer.py @@ -27,7 +27,7 @@ def test_transfer(subtensor, alice_wallet): transfer_fee = subtensor.wallets.get_transfer_fee( wallet=alice_wallet, dest=dest_coldkey, - value=transfer_value, + amount=transfer_value, ) # Account details before transfer @@ -68,7 +68,7 @@ async def test_transfer_async(async_subtensor, alice_wallet): transfer_fee = await async_subtensor.wallets.get_transfer_fee( wallet=alice_wallet, dest=dest_coldkey, - value=transfer_value, + amount=transfer_value, ) # Account details before transfer From c4d1ded04d83000fe3dd5f1cdee3f6fbd4eb7b38 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Tue, 30 Sep 2025 14:50:26 -0700 Subject: [PATCH 36/41] set debug for the login when block is older 300 in metagraph --- MIGRATION.md | 15 +++++++++------ bittensor/core/metagraph.py | 4 ++-- tests/unit_tests/test_metagraph.py | 4 +++- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index e7c3bc3f3f..dec5e9d0c4 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -32,8 +32,8 @@ 6. ~~Add `hotkey_ss58` parameter to `get_liquidity_list` method. One wallet can have many HKs. Currently, the mentioned method uses default HK only.~~ wrong idea ## Metagraph -1. Remove verbose archival node warnings for blocks older than 300. Some users complained about many messages for them. -2. Reconsider entire metagraph module logic. +1. ✅ Remove verbose archival node warnings for blocks older than 300. Some users complained about many messages for them. +2. ✅ Reconsider entire metagraph module logic. ## Balance 1. ✅ In `bittensor.utils.balance._check_currencies` raise the error instead of `warnings.warn`. @@ -95,7 +95,6 @@ rename this variable in documentation. ## Implementation - To implement the above changes and prepare for the v10 release, the following steps must be taken: - [x] Create a new branch named SDKv10.~~ @@ -111,7 +110,6 @@ It must include: # Migration guide - - [x] `._do_commit_reveal_v3` logic is included in the main code `.commit_timelocked_weights_extrinsic` - [x] `commit_reveal_version` parameter with default value `4` added to `commit_timelocked_weights_extrinsic` - [x] `._do_commit_weights` logic is included in the main code `.commit_weights_extrinsic` @@ -126,8 +124,8 @@ It must include: - [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. -# Standardize parameter order is applied for (extrinsics and related calls): +# Standardize parameter order is applied for (extrinsics and related calls): These parameters will now exist in all extrinsics and related calls (default values could be different depends by extrinsic): ```py @@ -216,6 +214,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - `decrease_take_extrinsic` and `increase_take_extrinsic` have been merged into a single set_take_extrinsic. The API now has a new `action: Literal["increase_take", "decrease_take"]` parameter (DRY rule). + ### Extrinsics has extra data in response's `data` field: - `add_stake_extrinsic` - `add_stake_multiple_extrinsic` @@ -225,6 +224,7 @@ Removing deprecated extrinsics and replacing them with consistent ones: - `unstake_extrinsic` - `unstake_multiple_extrinsic` + ### Subtensor changes - method `all_subnets` has renamed parameter from `block_number` to `block` (consistency in the codebase). - The `hotkey` parameter, which meant ss58 key address, was renamed to `hotkey_ss58` in all methods and related extrinsics (consistency in the codebase). @@ -248,6 +248,7 @@ Added sub-package `bittensor.core.addons` to host optional extensions and experi - `bittensor.core.timelock` moved to `bittensor.core.addons.timelock` - local env variable `BT_CHAIN_ENDPOINT` replaced with `BT_SUBTENSOR_CHAIN_ENDPOINT`. + ### Mechid related changes: In the next subtensor methods got updated the parameters order: - `bonds` @@ -262,6 +263,7 @@ In the next subtensor methods got updated the parameters order: Additional: - `bittensor.core.chain_data.metagraph_info.MetagraphInfo` got required attribute `mechid: int`. + ### Renames parameters: - `get_metagraph_info`: `field_indices` -> `selected_indices` (to be consistent) @@ -293,6 +295,7 @@ Currently it contains: - `bittensor.addons.subtensor_api` - `bittensor.addons.timelock` + ### Balance (bittensor/utils/balance.py) and related changes - [x] Added 2 custom errors: - `bittensor.core.errors.BalanceUnitMismatchError` @@ -300,4 +303,4 @@ Currently it contains: - [x] `check_balance` renamed to `check_balance_amount` - [x] `check_and_convert_to_balance` renamed to `check_balance_amount` - [x] `check_balance_amount` raised `BalanceTypeError` error instead of deprecated warning message. -- [x] private function `bittensor.utils.balance._check_currencies` raises `BalanceUnitMismatchError` error instead of deprecated warning message. This function is used inside the Balance class to check if units match during various mathematical and logical operations. \ No newline at end of file +- [x] private function `bittensor.utils.balance._check_currencies` raises `BalanceUnitMismatchError` error instead of deprecated warning message. This function is used inside the Balance class to check if units match during various mathematical and logical operations. diff --git a/bittensor/core/metagraph.py b/bittensor/core/metagraph.py index 494ef24909..15f8080107 100644 --- a/bittensor/core/metagraph.py +++ b/bittensor/core/metagraph.py @@ -1412,7 +1412,7 @@ async def sync( ): cur_block = await subtensor.get_current_block() if block and block < (cur_block - 300): - logging.warning( + logging.debug( "Attempting to sync longer than 300 blocks ago on a non-archive node. Please use the 'archive' " "network for subtensor and retry." ) @@ -1730,7 +1730,7 @@ def sync( ): cur_block = subtensor.get_current_block() if block and block < (cur_block - 300): - logging.warning( + logging.debug( "Attempting to sync longer than 300 blocks ago on a non-archive node. Please use the 'archive' " "network for subtensor and retry." ) diff --git a/tests/unit_tests/test_metagraph.py b/tests/unit_tests/test_metagraph.py index cdb735f627..acf62e22d0 100644 --- a/tests/unit_tests/test_metagraph.py +++ b/tests/unit_tests/test_metagraph.py @@ -1,5 +1,6 @@ import asyncio import copy +from bittensor.utils.btlogging import logging from bittensor.utils.balance import Balance from unittest.mock import Mock @@ -159,10 +160,11 @@ def __contains__(self, item): ], ) def test_sync_warning_cases(block, test_id, metagraph_instance, mock_subtensor, caplog): + """Makes sure that the warning message is logged when the block is greater than 300 with debug level.""" + logging.set_debug() mock_subtensor.get_current_block.return_value = 601 mock_subtensor.get_metagraph_info.return_value = [] metagraph_instance.sync(block=block, lite=True, subtensor=mock_subtensor) - expected_message = "Attempting to sync longer than 300 blocks ago on a non-archive node. Please use the 'archive' network for subtensor and retry." assert expected_message in caplog.text, ( f"Test ID: {test_id} - Expected warning message not found in Loguru sink." From c9f71668aca5d929fa721715ba222b3244d3a6a9 Mon Sep 17 00:00:00 2001 From: Roman <167799377+basfroman@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:48:33 -0700 Subject: [PATCH 37/41] Update bittensor/utils/balance.py Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/utils/balance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index 996dc35262..6b006ab9d5 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -851,7 +851,7 @@ def rao(amount: int, netuid: int = 0) -> Balance: return Balance.from_rao(amount).set_unit(netuid) -def check_balance_amount(amount: Optional[Balance]) -> Optional[None]: +def check_balance_amount(amount: Optional[Balance]) -> None: """ Validate that the provided value is a Balance instance. From 50136197195d59cf0604255f708e3798070d9fdc Mon Sep 17 00:00:00 2001 From: Roman <167799377+basfroman@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:48:49 -0700 Subject: [PATCH 38/41] Update bittensor/utils/balance.py Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/utils/balance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index 6b006ab9d5..fca462f3e6 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -862,7 +862,7 @@ def check_balance_amount(amount: Optional[Balance]) -> None: amount: The value to validate. Returns: - None if amount if None of a Balance instance. + None Raises: BalanceTypeError: If `amount` is not an instance of Balance. From 3bdd9ea21e67b88440059d749e993d3c37662447 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 2 Oct 2025 10:50:41 -0700 Subject: [PATCH 39/41] review fixes --- bittensor/core/errors.py | 2 +- bittensor/utils/balance.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/core/errors.py b/bittensor/core/errors.py index b1d2c510eb..19e748103f 100644 --- a/bittensor/core/errors.py +++ b/bittensor/core/errors.py @@ -218,5 +218,5 @@ class BalanceUnitMismatchError(Exception): """Raised when operations is attempted between Balance objects with different units (netuid).""" -class BalanceTypeError(Exception): +class BalanceTypeError(TypeError): """Raised when an unsupported type is used instead of Balance amount.""" diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index fca462f3e6..7e1f9c0372 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -16,8 +16,8 @@ def _check_currencies(self, other): balance2 = Balance.from_tao(500).set_unit(14) balance1 + balance2 # No error. - balance3 = Balance.from_tao(200).set_unit(14) - balance1 + balance3 # Raises ValueError. + balance3 = Balance.from_tao(200).set_unit(5) + balance1 + balance3 # Raises BalanceUnitMismatchError. In this example: - `from_rao` creates a Balance instance from the amount in rao. @@ -862,7 +862,7 @@ def check_balance_amount(amount: Optional[Balance]) -> None: amount: The value to validate. Returns: - None + None if amount is Balance instance or None, otherwise raise error. Raises: BalanceTypeError: If `amount` is not an instance of Balance. From 44ffc05874a410c064d29a537333bcde8859acd8 Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 2 Oct 2025 11:02:46 -0700 Subject: [PATCH 40/41] `bittensor.addons` -> `bittensor.extras` --- MIGRATION.md | 6 +++--- bittensor/{addons => extras}/__init__.py | 4 ++-- bittensor/{addons => extras}/subtensor_api/__init__.py | 0 bittensor/{addons => extras}/subtensor_api/chain.py | 0 bittensor/{addons => extras}/subtensor_api/commitments.py | 0 bittensor/{addons => extras}/subtensor_api/delegates.py | 0 bittensor/{addons => extras}/subtensor_api/extrinsics.py | 0 bittensor/{addons => extras}/subtensor_api/metagraphs.py | 0 bittensor/{addons => extras}/subtensor_api/neurons.py | 0 bittensor/{addons => extras}/subtensor_api/queries.py | 0 bittensor/{addons => extras}/subtensor_api/staking.py | 0 bittensor/{addons => extras}/subtensor_api/subnets.py | 0 bittensor/{addons => extras}/subtensor_api/utils.py | 2 +- bittensor/{addons => extras}/subtensor_api/wallets.py | 0 bittensor/{addons => extras}/timelock.py | 0 bittensor/utils/easy_imports.py | 2 +- tests/e2e_tests/conftest.py | 2 +- tests/e2e_tests/utils/chain_interactions.py | 2 +- tests/e2e_tests/utils/e2e_test_utils.py | 2 +- tests/integration_tests/test_timelock.py | 2 +- tests/unit_tests/test_subtensor_api.py | 2 +- 21 files changed, 12 insertions(+), 12 deletions(-) rename bittensor/{addons => extras}/__init__.py (87%) rename bittensor/{addons => extras}/subtensor_api/__init__.py (100%) rename bittensor/{addons => extras}/subtensor_api/chain.py (100%) rename bittensor/{addons => extras}/subtensor_api/commitments.py (100%) rename bittensor/{addons => extras}/subtensor_api/delegates.py (100%) rename bittensor/{addons => extras}/subtensor_api/extrinsics.py (100%) rename bittensor/{addons => extras}/subtensor_api/metagraphs.py (100%) rename bittensor/{addons => extras}/subtensor_api/neurons.py (100%) rename bittensor/{addons => extras}/subtensor_api/queries.py (100%) rename bittensor/{addons => extras}/subtensor_api/staking.py (100%) rename bittensor/{addons => extras}/subtensor_api/subnets.py (100%) rename bittensor/{addons => extras}/subtensor_api/utils.py (99%) rename bittensor/{addons => extras}/subtensor_api/wallets.py (100%) rename bittensor/{addons => extras}/timelock.py (100%) diff --git a/MIGRATION.md b/MIGRATION.md index dec5e9d0c4..b2228798c5 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -290,10 +290,10 @@ Links to subpackages removed: - `bittensor.extrinsics` (available in `bittensor.core.extrinsics`) -New subpackage `bittensor.addons` created to host optional extensions and experimental logic enhancing the core functionality. +New subpackage `bittensor.extras` created to host optional extensions and experimental logic enhancing the core functionality. Currently it contains: -- `bittensor.addons.subtensor_api` -- `bittensor.addons.timelock` +- `bittensor.extras.subtensor_api` +- `bittensor.extras.timelock` ### Balance (bittensor/utils/balance.py) and related changes diff --git a/bittensor/addons/__init__.py b/bittensor/extras/__init__.py similarity index 87% rename from bittensor/addons/__init__.py rename to bittensor/extras/__init__.py index 0f7295f24a..27ccdbaeaf 100644 --- a/bittensor/addons/__init__.py +++ b/bittensor/extras/__init__.py @@ -9,8 +9,8 @@ discoverability and structure. """ -from bittensor.addons import timelock -from bittensor.addons.subtensor_api import SubtensorApi +from bittensor.extras import timelock +from bittensor.extras.subtensor_api import SubtensorApi __all__ = [ "timelock", diff --git a/bittensor/addons/subtensor_api/__init__.py b/bittensor/extras/subtensor_api/__init__.py similarity index 100% rename from bittensor/addons/subtensor_api/__init__.py rename to bittensor/extras/subtensor_api/__init__.py diff --git a/bittensor/addons/subtensor_api/chain.py b/bittensor/extras/subtensor_api/chain.py similarity index 100% rename from bittensor/addons/subtensor_api/chain.py rename to bittensor/extras/subtensor_api/chain.py diff --git a/bittensor/addons/subtensor_api/commitments.py b/bittensor/extras/subtensor_api/commitments.py similarity index 100% rename from bittensor/addons/subtensor_api/commitments.py rename to bittensor/extras/subtensor_api/commitments.py diff --git a/bittensor/addons/subtensor_api/delegates.py b/bittensor/extras/subtensor_api/delegates.py similarity index 100% rename from bittensor/addons/subtensor_api/delegates.py rename to bittensor/extras/subtensor_api/delegates.py diff --git a/bittensor/addons/subtensor_api/extrinsics.py b/bittensor/extras/subtensor_api/extrinsics.py similarity index 100% rename from bittensor/addons/subtensor_api/extrinsics.py rename to bittensor/extras/subtensor_api/extrinsics.py diff --git a/bittensor/addons/subtensor_api/metagraphs.py b/bittensor/extras/subtensor_api/metagraphs.py similarity index 100% rename from bittensor/addons/subtensor_api/metagraphs.py rename to bittensor/extras/subtensor_api/metagraphs.py diff --git a/bittensor/addons/subtensor_api/neurons.py b/bittensor/extras/subtensor_api/neurons.py similarity index 100% rename from bittensor/addons/subtensor_api/neurons.py rename to bittensor/extras/subtensor_api/neurons.py diff --git a/bittensor/addons/subtensor_api/queries.py b/bittensor/extras/subtensor_api/queries.py similarity index 100% rename from bittensor/addons/subtensor_api/queries.py rename to bittensor/extras/subtensor_api/queries.py diff --git a/bittensor/addons/subtensor_api/staking.py b/bittensor/extras/subtensor_api/staking.py similarity index 100% rename from bittensor/addons/subtensor_api/staking.py rename to bittensor/extras/subtensor_api/staking.py diff --git a/bittensor/addons/subtensor_api/subnets.py b/bittensor/extras/subtensor_api/subnets.py similarity index 100% rename from bittensor/addons/subtensor_api/subnets.py rename to bittensor/extras/subtensor_api/subnets.py diff --git a/bittensor/addons/subtensor_api/utils.py b/bittensor/extras/subtensor_api/utils.py similarity index 99% rename from bittensor/addons/subtensor_api/utils.py rename to bittensor/extras/subtensor_api/utils.py index 86fa53eb95..b12e18667f 100644 --- a/bittensor/addons/subtensor_api/utils.py +++ b/bittensor/extras/subtensor_api/utils.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from bittensor.addons import SubtensorApi + from bittensor.extras import SubtensorApi def add_legacy_methods(subtensor: "SubtensorApi"): diff --git a/bittensor/addons/subtensor_api/wallets.py b/bittensor/extras/subtensor_api/wallets.py similarity index 100% rename from bittensor/addons/subtensor_api/wallets.py rename to bittensor/extras/subtensor_api/wallets.py diff --git a/bittensor/addons/timelock.py b/bittensor/extras/timelock.py similarity index 100% rename from bittensor/addons/timelock.py rename to bittensor/extras/timelock.py diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 8d7689bf81..f4dd89902d 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -30,7 +30,6 @@ from bittensor_wallet.keypair import Keypair from bittensor_wallet.wallet import Wallet -from bittensor.addons import timelock, SubtensorApi from bittensor.core import settings, extrinsics from bittensor.core.async_subtensor import AsyncSubtensor, get_async_subtensor from bittensor.core.axon import Axon @@ -108,6 +107,7 @@ from bittensor.core.synapse import TerminalInfo, Synapse from bittensor.core.tensor import Tensor from bittensor.core.threadpool import PriorityThreadPoolExecutor +from bittensor.extras import timelock, SubtensorApi from bittensor.utils import ( mock, ss58_to_vec_u8, diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index f59dda1d9d..8eea338091 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -12,7 +12,7 @@ import pytest import pytest_asyncio -from bittensor.addons import SubtensorApi +from bittensor.extras import SubtensorApi from bittensor.utils.btlogging import logging from tests.e2e_tests.utils.e2e_test_utils import ( Templates, diff --git a/tests/e2e_tests/utils/chain_interactions.py b/tests/e2e_tests/utils/chain_interactions.py index ee827b2083..87ee70da9d 100644 --- a/tests/e2e_tests/utils/chain_interactions.py +++ b/tests/e2e_tests/utils/chain_interactions.py @@ -15,7 +15,7 @@ # for typing purposes if TYPE_CHECKING: from bittensor import Wallet - from bittensor.addons import SubtensorApi + from bittensor.extras import SubtensorApi from async_substrate_interface import ( AsyncSubstrateInterface, AsyncExtrinsicReceipt, diff --git a/tests/e2e_tests/utils/e2e_test_utils.py b/tests/e2e_tests/utils/e2e_test_utils.py index b561bbdd9f..3e97bf2ad5 100644 --- a/tests/e2e_tests/utils/e2e_test_utils.py +++ b/tests/e2e_tests/utils/e2e_test_utils.py @@ -6,7 +6,7 @@ from bittensor_wallet import Keypair, Wallet -from bittensor.addons import SubtensorApi +from bittensor.extras import SubtensorApi from bittensor.utils.btlogging import logging template_path = os.getcwd() + "/neurons/" diff --git a/tests/integration_tests/test_timelock.py b/tests/integration_tests/test_timelock.py index 2b7ddf9823..a1faf44cf6 100644 --- a/tests/integration_tests/test_timelock.py +++ b/tests/integration_tests/test_timelock.py @@ -3,7 +3,7 @@ import pytest -from bittensor.addons import timelock +from bittensor.extras import timelock def test_encrypt_returns_valid_tuple(): diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index 39cc918439..7eaac5fc3a 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -1,6 +1,6 @@ import pytest -from bittensor.addons.subtensor_api import SubtensorApi +from bittensor.extras import SubtensorApi from bittensor.core.subtensor import Subtensor From 69617ed42304d1c2060c8d5736cd318a6f2d7d1f Mon Sep 17 00:00:00 2001 From: Roman Chkhaidze Date: Thu, 2 Oct 2025 12:19:00 -0700 Subject: [PATCH 41/41] important wording --- bittensor/utils/balance.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index 7e1f9c0372..f585dd0b11 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -862,10 +862,10 @@ def check_balance_amount(amount: Optional[Balance]) -> None: amount: The value to validate. Returns: - None if amount is Balance instance or None, otherwise raise error. + None: Always returns None if validation passes. Raises: - BalanceTypeError: If `amount` is not an instance of Balance. + BalanceTypeError: If amount is not a Balance instance and not None. """ if amount is None: return None