From 9801676d83969c620aa16783b0a4e1050754a2d2 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Jul 2025 15:44:03 +0200 Subject: [PATCH 01/10] [WIP] Optimisations --- bittensor/core/subtensor.py | 72 +++++++++++++++++++++---------------- tests/e2e_tests/conftest.py | 5 ++- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 94e9b7dd91..ca7f578626 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -9,6 +9,7 @@ from async_substrate_interface.substrate_addons import RetrySyncSubstrate from async_substrate_interface.sync_substrate import SubstrateInterface from async_substrate_interface.types import ScaleObj +from async_substrate_interface.utils.storage import StorageKey from bittensor_drand import get_encrypted_commitment from numpy.typing import NDArray @@ -429,7 +430,8 @@ def state_call( block_hash = self.determine_block_hash(block) return self.substrate.rpc_request( method="state_call", - params=[method, data, block_hash] if block_hash else [method, data], + params=[method, data], + block_hash=block_hash ) # Common subtensor calls =========================================================================================== @@ -484,20 +486,26 @@ 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. Arguments: netuid (int): The unique identifier of the subnetwork. uid (int): The unique identifier of the neuron. + block (int): The block number for this query. Returns: Optional[int]: 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])) + call = self.get_hyperparameter(param_name="LastUpdate", netuid=netuid, block=block) + if not call: + return None + elif block is not None: + return block - int(call[uid]) + else: + return self.get_current_block() - int(call[uid]) def bonds( self, netuid: int, block: Optional[int] = None @@ -1492,31 +1500,25 @@ def get_liquidity_list( logging.debug(f"Subnet {netuid} is not active.") return None - query = self.substrate.query block_hash = self.determine_block_hash(block) # Fetch global fees and current price - fee_global_tao_query = query( - module="Swap", - storage_function="FeeGlobalTao", - params=[netuid], - block_hash=block_hash, + fee_global_tao_query_sk = self.substrate.create_storage_key( + pallet="Swap", storage_function="FeeGlobalTao", params=[netuid], block_hash=block_hash ) - fee_global_alpha_query = query( - module="Swap", - storage_function="FeeGlobalAlpha", - params=[netuid], - block_hash=block_hash, + fee_global_alpha_query_sk = self.substrate.create_storage_key( + pallet="Swap", storage_function="FeeGlobalAlpha", params=[netuid], block_hash=block_hash ) - sqrt_price_query = query( - module="Swap", - storage_function="AlphaSqrtPrice", - params=[netuid], - block_hash=block_hash, + sqrt_price_query_sk = self.substrate.create_storage_key( + pallet="Swap", storage_function="AlphaSqrtPrice", params=[netuid], block_hash=block_hash ) - fee_global_tao = fixed_to_float(fee_global_tao_query) - fee_global_alpha = fixed_to_float(fee_global_alpha_query) - sqrt_price = fixed_to_float(sqrt_price_query) + fee_global_tao_query, fee_global_alpha_query, sqrt_price_query = self.substrate.query_multi( + [fee_global_tao_query_sk, fee_global_alpha_query_sk, sqrt_price_query_sk], + block_hash=block_hash) + + fee_global_tao = fixed_to_float(fee_global_tao_query[1]) + fee_global_alpha = fixed_to_float(fee_global_alpha_query[1]) + sqrt_price = fixed_to_float(sqrt_price_query[1]) current_tick = price_to_tick(sqrt_price**2) # Fetch positions @@ -1526,26 +1528,36 @@ def get_liquidity_list( block=block, params=[netuid, wallet.coldkeypub.ss58_address], ) - - positions = [] + positions_values: list[tuple[dict, int, int]] = [] + positions_storage_keys: list[StorageKey] = [] for _, p in positions_response: position = p.value tick_low_idx = position["tick_low"][0] tick_high_idx = position["tick_high"][0] - tick_low = query( - module="Swap", + tick_low_sk = self.substrate.create_storage_key( + pallet="Swap", storage_function="Ticks", params=[netuid, tick_low_idx], block_hash=block_hash, ) - tick_high = query( - module="Swap", + tick_high_sk = self.substrate.create_storage_key( + pallet="Swap", storage_function="Ticks", params=[netuid, tick_high_idx], - block_hash=block_hash, + block_hash=block_hash ) + positions_values.append((position, tick_low_idx, tick_high_idx)) + positions_storage_keys.extend([tick_low_sk, tick_high_sk]) + # query all our ticks at once + ticks_query = self.substrate.query_multi(positions_storage_keys, block_hash=block_hash) + # iterator with just the values + ticks = iter([x[1] for x in ticks_query]) + positions = [] + for position, tick_low_idx, tick_high_idx in positions_values: + tick_low = next(ticks) + tick_high = next(ticks) # Calculate fees above/below range for both tokens tao_below = get_fees( diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index c79446be26..c9ed635971 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -18,7 +18,10 @@ setup_wallet, ) -LOCALNET_IMAGE_NAME = os.getenv("LOCALNET_IMAGE_NAME") or "ghcr.io/opentensor/subtensor-localnet:devnet-ready" +LOCALNET_IMAGE_NAME = ( + os.getenv("LOCALNET_IMAGE_NAME") + or "ghcr.io/opentensor/subtensor-localnet:devnet-ready" +) CONTAINER_NAME_PREFIX = "test_local_chain_" From 0761f0da29399084fbe85321f4cf6145b410ea30 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Jul 2025 16:30:44 +0200 Subject: [PATCH 02/10] Fixes liquidity_list abysmal performance --- bittensor/core/async_subtensor.py | 96 ++++++++++++++++++------------- bittensor/core/subtensor.py | 52 ++++++++++------- pyproject.toml | 2 +- 3 files changed, 90 insertions(+), 60 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index bb46e4ddc9..ef2fbf4c71 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -10,6 +10,7 @@ import scalecodec from async_substrate_interface import AsyncSubstrateInterface from async_substrate_interface.substrate_addons import RetryAsyncSubstrate +from async_substrate_interface.utils.storage import StorageKey from bittensor_drand import get_encrypted_commitment from bittensor_wallet.utils import SS58_FORMAT from numpy.typing import NDArray @@ -767,7 +768,8 @@ async def state_call( block_hash = await self.determine_block_hash(block, block_hash, reuse_block) return await self.substrate.rpc_request( method="state_call", - params=[method, data, block_hash] if block_hash else [method, data], + params=[method, data], + block_hash=block_hash, reuse_block_hash=reuse_block, ) @@ -2242,29 +2244,35 @@ async def get_liquidity_list( block=block, block_hash=block_hash, reuse_block=reuse_block ) - query = self.substrate.query + # Fetch global fees and current price + fee_global_tao_query_sk = await self.substrate.create_storage_key( + pallet="Swap", + storage_function="FeeGlobalTao", + params=[netuid], + block_hash=block_hash, + ) + fee_global_alpha_query_sk = await self.substrate.create_storage_key( + pallet="Swap", + storage_function="FeeGlobalAlpha", + params=[netuid], + block_hash=block_hash, + ) + sqrt_price_query_sk = await self.substrate.create_storage_key( + pallet="Swap", + storage_function="AlphaSqrtPrice", + params=[netuid], + block_hash=block_hash, + ) ( - fee_global_tao, - fee_global_alpha, - sqrt_price, + (fee_global_tao_query, fee_global_alpha_query, sqrt_price_query), positions_response, ) = await asyncio.gather( - query( - module="Swap", - storage_function="FeeGlobalTao", - params=[netuid], - block_hash=block_hash, - ), - query( - module="Swap", - storage_function="FeeGlobalAlpha", - params=[netuid], - block_hash=block_hash, - ), - query( - module="Swap", - storage_function="AlphaSqrtPrice", - params=[netuid], + self.substrate.query_multi( + [ + fee_global_tao_query_sk, + fee_global_alpha_query_sk, + sqrt_price_query_sk, + ], block_hash=block_hash, ), self.query_map( @@ -2275,36 +2283,46 @@ async def get_liquidity_list( ), ) # convert to floats - fee_global_tao = fixed_to_float(fee_global_tao) - fee_global_alpha = fixed_to_float(fee_global_alpha) - sqrt_price = fixed_to_float(sqrt_price) + fee_global_tao = fixed_to_float(fee_global_tao_query[1]) + fee_global_alpha = fixed_to_float(fee_global_alpha_query[1]) + sqrt_price = fixed_to_float(sqrt_price_query[1]) # Fetch global fees and current price current_tick = price_to_tick(sqrt_price**2) # Fetch positions - positions = [] + positions_values: list[tuple[dict, int, int]] = [] + positions_storage_keys: list[StorageKey] = [] async for _, p in positions_response: position = p.value tick_low_idx = position.get("tick_low")[0] tick_high_idx = position.get("tick_high")[0] - - tick_low, tick_high = await asyncio.gather( - query( - module="Swap", - storage_function="Ticks", - params=[netuid, tick_low_idx], - block_hash=block_hash, - ), - query( - module="Swap", - storage_function="Ticks", - params=[netuid, tick_high_idx], - block_hash=block_hash, - ), + positions_values.append((position, tick_low_idx, tick_high_idx)) + tick_low_sk = await self.substrate.create_storage_key( + pallet="Swap", + storage_function="Ticks", + params=[netuid, tick_low_idx], + block_hash=block_hash, ) + tick_high_sk = await self.substrate.create_storage_key( + pallet="Swap", + storage_function="Ticks", + params=[netuid, tick_high_idx], + block_hash=block_hash, + ) + positions_storage_keys.extend([tick_low_sk, tick_high_sk]) + # query all our ticks at once + ticks_query = await self.substrate.query_multi( + positions_storage_keys, block_hash=block_hash + ) + # iterator with just the values + ticks = iter([x[1] for x in ticks_query]) + positions = [] + for position, tick_low_idx, tick_high_idx in positions_values: + tick_low = next(ticks) + tick_high = next(ticks) # Calculate fees above/below range for both tokens tao_below = get_fees( current_tick=current_tick, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index ca7f578626..2e92a2be22 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -429,9 +429,7 @@ def state_call( """ block_hash = self.determine_block_hash(block) return self.substrate.rpc_request( - method="state_call", - params=[method, data], - block_hash=block_hash + method="state_call", params=[method, data], block_hash=block_hash ) # Common subtensor calls =========================================================================================== @@ -486,26 +484,22 @@ 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, block: Optional[int] = None) -> Optional[int]: + def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int]: """ Returns the number of blocks since the last update for a specific UID in the subnetwork. Arguments: netuid (int): The unique identifier of the subnetwork. uid (int): The unique identifier of the neuron. - block (int): The block number for this query. Returns: Optional[int]: 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, block=block) - if not call: - return None - elif block is not None: - return block - int(call[uid]) - else: - return self.get_current_block() - int(call[uid]) + call = self.get_hyperparameter( + param_name="LastUpdate", netuid=netuid, block=block + ) + return None if call is None else self.get_current_block() - int(call[uid]) def bonds( self, netuid: int, block: Optional[int] = None @@ -1504,17 +1498,33 @@ def get_liquidity_list( # Fetch global fees and current price fee_global_tao_query_sk = self.substrate.create_storage_key( - pallet="Swap", storage_function="FeeGlobalTao", params=[netuid], block_hash=block_hash + pallet="Swap", + storage_function="FeeGlobalTao", + params=[netuid], + block_hash=block_hash, ) fee_global_alpha_query_sk = self.substrate.create_storage_key( - pallet="Swap", storage_function="FeeGlobalAlpha", params=[netuid], block_hash=block_hash + pallet="Swap", + storage_function="FeeGlobalAlpha", + params=[netuid], + block_hash=block_hash, ) sqrt_price_query_sk = self.substrate.create_storage_key( - pallet="Swap", storage_function="AlphaSqrtPrice", params=[netuid], block_hash=block_hash + pallet="Swap", + storage_function="AlphaSqrtPrice", + params=[netuid], + block_hash=block_hash, + ) + fee_global_tao_query, fee_global_alpha_query, sqrt_price_query = ( + self.substrate.query_multi( + [ + fee_global_tao_query_sk, + fee_global_alpha_query_sk, + sqrt_price_query_sk, + ], + block_hash=block_hash, + ) ) - fee_global_tao_query, fee_global_alpha_query, sqrt_price_query = self.substrate.query_multi( - [fee_global_tao_query_sk, fee_global_alpha_query_sk, sqrt_price_query_sk], - block_hash=block_hash) fee_global_tao = fixed_to_float(fee_global_tao_query[1]) fee_global_alpha = fixed_to_float(fee_global_alpha_query[1]) @@ -1546,12 +1556,14 @@ def get_liquidity_list( pallet="Swap", storage_function="Ticks", params=[netuid, tick_high_idx], - block_hash=block_hash + block_hash=block_hash, ) positions_values.append((position, tick_low_idx, tick_high_idx)) positions_storage_keys.extend([tick_low_sk, tick_high_sk]) # query all our ticks at once - ticks_query = self.substrate.query_multi(positions_storage_keys, block_hash=block_hash) + ticks_query = self.substrate.query_multi( + positions_storage_keys, block_hash=block_hash + ) # iterator with just the values ticks = iter([x[1] for x in ticks_query]) positions = [] diff --git a/pyproject.toml b/pyproject.toml index 9c32e6ee7a..8b4b70f248 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ "uvicorn", "bittensor-drand>=0.5.0", "bittensor-wallet>=3.1.0", - "async-substrate-interface>=1.3.1" + "async-substrate-interface>=1.4.1" ] [project.optional-dependencies] From 295bdde093e353a135540a4fca9e8821480c5aaf Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Jul 2025 16:35:26 +0200 Subject: [PATCH 03/10] Debug --- bittensor/core/subtensor.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 2e92a2be22..8cab6df590 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -496,9 +496,7 @@ def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int]: Optional[int]: 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, block=block - ) + call = self.get_hyperparameter(param_name="LastUpdate", netuid=netuid) return None if call is None else self.get_current_block() - int(call[uid]) def bonds( From 8c558d1dfc9a175a9a6c29f89024627682b056ce Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Jul 2025 16:38:09 +0200 Subject: [PATCH 04/10] Flake --- bittensor/core/subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 8cab6df590..bd89046e2a 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -655,7 +655,7 @@ def get_all_subnets_info(self, block: Optional[int] = None) -> list["SubnetInfo" for subnet in result: subnet.update({"price": subnets_prices.get(subnet["netuid"], 0)}) - except SubstrateRequestException: + except SubstrateRequestException as e: logging.warning(f"Unable to fetch subnet prices for block {block}: {e}") return SubnetInfo.list_from_dicts(result) From b5ccbef42325a6a68686fb11713562cbcab2c757 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Jul 2025 17:00:48 +0200 Subject: [PATCH 05/10] Fixed unit tests --- tests/unit_tests/test_async_subtensor.py | 109 ++++++++++++++--------- tests/unit_tests/test_subtensor.py | 3 +- 2 files changed, 67 insertions(+), 45 deletions(-) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 24dee30ef7..165905f443 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3570,52 +3570,75 @@ async def test_get_liquidity_list_happy_path(subtensor, fake_wallet, mocker): return_value=(Balance.from_tao(0.0), Balance.from_tao(0.0, netuid)), ) - mocked_substrate_query = mocker.AsyncMock( + mocked_substrate_query_multi = mocker.AsyncMock( side_effect=[ - # for gather - {"bits": 0}, - {"bits": 0}, - {"bits": 18446744073709551616}, - # for loop - { - "liquidity_net": 1000000000000, - "liquidity_gross": 1000000000000, - "fees_out_tao": {"bits": 0}, - "fees_out_alpha": {"bits": 0}, - }, - { - "liquidity_net": -1000000000000, - "liquidity_gross": 1000000000000, - "fees_out_tao": {"bits": 0}, - "fees_out_alpha": {"bits": 0}, - }, - { - "liquidity_net": 1000000000000, - "liquidity_gross": 1000000000000, - "fees_out_tao": {"bits": 0}, - "fees_out_alpha": {"bits": 0}, - }, - { - "liquidity_net": -1000000000000, - "liquidity_gross": 1000000000000, - "fees_out_tao": {"bits": 0}, - "fees_out_alpha": {"bits": 0}, - }, - { - "liquidity_net": 1000000000000, - "liquidity_gross": 1000000000000, - "fees_out_tao": {"bits": 0}, - "fees_out_alpha": {"bits": 0}, - }, - { - "liquidity_net": -1000000000000, - "liquidity_gross": 1000000000000, - "fees_out_tao": {"bits": 0}, - "fees_out_alpha": {"bits": 0}, - }, + [ + (None, {"bits": 0}), + (None, {"bits": 0}), + (None, {"bits": 18446744073709551616}), + ], + [ + ( + None, + { + "liquidity_net": 1000000000000, + "liquidity_gross": 1000000000000, + "fees_out_tao": {"bits": 0}, + "fees_out_alpha": {"bits": 0}, + }, + ), + ( + None, + { + "liquidity_net": -1000000000000, + "liquidity_gross": 1000000000000, + "fees_out_tao": {"bits": 0}, + "fees_out_alpha": {"bits": 0}, + }, + ), + ( + None, + { + "liquidity_net": 1000000000000, + "liquidity_gross": 1000000000000, + "fees_out_tao": {"bits": 0}, + "fees_out_alpha": {"bits": 0}, + }, + ), + ( + None, + { + "liquidity_net": -1000000000000, + "liquidity_gross": 1000000000000, + "fees_out_tao": {"bits": 0}, + "fees_out_alpha": {"bits": 0}, + }, + ), + ( + None, + { + "liquidity_net": 1000000000000, + "liquidity_gross": 1000000000000, + "fees_out_tao": {"bits": 0}, + "fees_out_alpha": {"bits": 0}, + }, + ), + ( + None, + { + "liquidity_net": -1000000000000, + "liquidity_gross": 1000000000000, + "fees_out_tao": {"bits": 0}, + "fees_out_alpha": {"bits": 0}, + }, + ), + ], ] ) - mocker.patch.object(subtensor.substrate, "query", mocked_substrate_query) + + mocker.patch.object( + subtensor.substrate, "query_multi", mocked_substrate_query_multi + ) fake_positions = [ [ diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 6ee02cea5a..0d22398683 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -954,8 +954,7 @@ def test_state_call(subtensor, mocker): # Asserts subtensor.substrate.rpc_request.assert_called_once_with( - method="state_call", - params=[fake_method, fake_data], + method="state_call", params=[fake_method, fake_data], block_hash=None ) assert result == subtensor.substrate.rpc_request.return_value From b16a56ad9e3b840bbddebf12ecdc8220e94fc27d Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Jul 2025 17:51:09 +0200 Subject: [PATCH 06/10] Fixes (relies on https://github.com/opentensor/async-substrate-interface/pull/152) --- bittensor/core/subtensor.py | 4 ++-- bittensor/utils/balance.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index bd89046e2a..8c586f4635 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1566,8 +1566,8 @@ def get_liquidity_list( ticks = iter([x[1] for x in ticks_query]) positions = [] for position, tick_low_idx, tick_high_idx in positions_values: - tick_low = next(ticks) - tick_high = next(ticks) + tick_low = next(ticks).value + tick_high = next(ticks).value # Calculate fees above/below range for both tokens tao_below = get_fees( diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index b65fdaec82..34d2e5c787 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -349,8 +349,7 @@ def fixed_to_float( ) -> float: # By default, this is a U64F64 # which is 64 bits of integer and 64 bits of fractional - - data: int = fixed["bits"] + data: int = fb.value if isinstance((fb := fixed["bits"]), ScaleType) else fb # Logical and to get the fractional part; remaining is the integer part fractional_part = data & (2**frac_bits - 1) From 5cb6623a8a2a274b6ca2a9951434d9c1ca00c1cb Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Jul 2025 17:59:10 +0200 Subject: [PATCH 07/10] Apply to async aswell --- 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 ef2fbf4c71..e096ffd7b3 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -2321,8 +2321,8 @@ async def get_liquidity_list( ticks = iter([x[1] for x in ticks_query]) positions = [] for position, tick_low_idx, tick_high_idx in positions_values: - tick_low = next(ticks) - tick_high = next(ticks) + tick_low = next(ticks).value + tick_high = next(ticks).value # Calculate fees above/below range for both tokens tao_below = get_fees( current_tick=current_tick, From a3502baa87e28233dcc7e45fdae0b943877b98c4 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Jul 2025 18:08:33 +0200 Subject: [PATCH 08/10] Remove unnecessary .value call --- bittensor/core/async_subtensor.py | 4 ++-- bittensor/core/subtensor.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index e096ffd7b3..ef2fbf4c71 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -2321,8 +2321,8 @@ async def get_liquidity_list( ticks = iter([x[1] for x in ticks_query]) positions = [] for position, tick_low_idx, tick_high_idx in positions_values: - tick_low = next(ticks).value - tick_high = next(ticks).value + tick_low = next(ticks) + tick_high = next(ticks) # Calculate fees above/below range for both tokens tao_below = get_fees( current_tick=current_tick, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 8c586f4635..bd89046e2a 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1566,8 +1566,8 @@ def get_liquidity_list( ticks = iter([x[1] for x in ticks_query]) positions = [] for position, tick_low_idx, tick_high_idx in positions_values: - tick_low = next(ticks).value - tick_high = next(ticks).value + tick_low = next(ticks) + tick_high = next(ticks) # Calculate fees above/below range for both tokens tao_below = get_fees( From 8f687b854d5e126f20ac3757a895475b14a61ac8 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Jul 2025 18:14:42 +0200 Subject: [PATCH 09/10] Will rely on changes in 1.4.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8b4b70f248..ebf263ba57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ "uvicorn", "bittensor-drand>=0.5.0", "bittensor-wallet>=3.1.0", - "async-substrate-interface>=1.4.1" + "async-substrate-interface>=1.4.2" ] [project.optional-dependencies] From 4974adff374644d669c967d3769652be7ef3cfb6 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 23 Jul 2025 19:11:03 +0200 Subject: [PATCH 10/10] Fix test --- bittensor/core/subtensor.py | 1 + .../extrinsics/asyncex/test_registration.py | 1 - .../unit_tests/extrinsics/test_set_weights.py | 3 +- tests/unit_tests/test_subtensor.py | 82 +++++++++---------- tests/unit_tests/utils/test_weight_utils.py | 2 - 5 files changed, 43 insertions(+), 46 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index c5962d50f1..b28c49533f 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -106,6 +106,7 @@ torch, u16_normalized_float, u64_normalized_float, + deprecated_message, ) from bittensor.utils.balance import ( Balance, diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index d10c032cd7..4cecae6616 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -1,6 +1,5 @@ import pytest -from bittensor.core import async_subtensor from bittensor.core.extrinsics.asyncex import registration as async_registration diff --git a/tests/unit_tests/extrinsics/test_set_weights.py b/tests/unit_tests/extrinsics/test_set_weights.py index 1046385291..6a47809b43 100644 --- a/tests/unit_tests/extrinsics/test_set_weights.py +++ b/tests/unit_tests/extrinsics/test_set_weights.py @@ -1,9 +1,8 @@ from unittest.mock import MagicMock, patch import pytest -import torch +# import torch -from bittensor.core import subtensor as subtensor_module from bittensor.core.extrinsics.set_weights import ( _do_set_weights, set_weights_extrinsic, diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index cfaeb831ba..aa5121408e 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1496,7 +1496,7 @@ def test_do_serve_axon_is_success( ) assert result[0] is True - assert result[1] is "" + assert result[1] == "" def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_params): @@ -3844,25 +3844,22 @@ def test_get_liquidity_list_subnet_is_not_active(subtensor, mocker): def test_get_liquidity_list_happy_path(subtensor, fake_wallet, mocker): """Tests `get_liquidity_list` returns the correct value.""" - # Preps netuid = 2 + # Mock network state mocker.patch.object(subtensor, "subnet_exists", return_value=True) mocker.patch.object(subtensor, "is_subnet_active", return_value=True) - mocker.patch.object(subtensor, "determine_block_hash") + mocker.patch.object(subtensor, "determine_block_hash", return_value="0x1234") - mocker.patch.object( - subtensor_module, "price_to_tick", return_value=Balance.from_tao(1.0, netuid) - ) + # Mock price and fee calculation + mocker.patch.object(subtensor_module, "price_to_tick", return_value=100) mocker.patch.object( subtensor_module, "calculate_fees", return_value=(Balance.from_tao(0.0), Balance.from_tao(0.0, netuid)), ) - mocked_substrate_query = mocker.MagicMock() - mocker.patch.object(subtensor.substrate, "query", mocked_substrate_query) - + # Fake positions to return from query_map fake_positions = [ [ (2,), @@ -3878,55 +3875,58 @@ def test_get_liquidity_list_happy_path(subtensor, fake_wallet, mocker): } ), ], - [ - (2,), - mocker.Mock( - value={ - "id": (2,), - "netuid": 2, - "tick_low": (216189,), - "tick_high": (198196,), - "liquidity": 2000000000000, - "fees_tao": {"bits": 0}, - "fees_alpha": {"bits": 0}, - } - ), - ], - [ - (2,), - mocker.Mock( - value={ - "id": (2,), - "netuid": 2, - "tick_low": (226189,), - "tick_high": (188196,), - "liquidity": 3000000000000, - "fees_tao": {"bits": 0}, - "fees_alpha": {"bits": 0}, - } - ), - ], ] mocked_query_map = mocker.MagicMock(return_value=fake_positions) mocker.patch.object(subtensor, "query_map", new=mocked_query_map) - # Call + # Mock storage key creation + mocker.patch.object( + subtensor.substrate, + "create_storage_key", + side_effect=lambda pallet, + storage_function, + params, + block_hash=None: f"{pallet}:{storage_function}:{params}", + ) + + # Mock query_multi for fee + sqrt_price + tick data + mock_query_multi = mocker.MagicMock( + side_effect=[ + [ + ("key1", {"bits": 0}), # fee_global_tao + ("key2", {"bits": 0}), # fee_global_alpha + ("key3", {"bits": 1072693248}), + ], + [ + ( + "tick_low", + {"fees_out_tao": {"bits": 0}, "fees_out_alpha": {"bits": 0}}, + ), + ( + "tick_high", + {"fees_out_tao": {"bits": 0}, "fees_out_alpha": {"bits": 0}}, + ), + ], + ] + ) + mocker.patch.object(subtensor.substrate, "query_multi", new=mock_query_multi) + # Call result = subtensor.get_liquidity_list(wallet=fake_wallet, netuid=netuid) # Asserts - subtensor.determine_block_hash.assert_called_once_with(None) + assert subtensor.determine_block_hash.call_count == 1 assert subtensor_module.price_to_tick.call_count == 1 assert subtensor_module.calculate_fees.call_count == len(fake_positions) - mocked_query_map.assert_called_once_with( module="Swap", name="Positions", block=None, params=[netuid, fake_wallet.coldkeypub.ss58_address], ) + assert mock_query_multi.call_count == 2 # one for fees, one for ticks assert len(result) == len(fake_positions) - assert all([isinstance(p, subtensor_module.LiquidityPosition) for p in result]) + assert all(isinstance(p, subtensor_module.LiquidityPosition) for p in result) def test_add_liquidity(subtensor, fake_wallet, mocker): diff --git a/tests/unit_tests/utils/test_weight_utils.py b/tests/unit_tests/utils/test_weight_utils.py index e49a814e00..c378173bfa 100644 --- a/tests/unit_tests/utils/test_weight_utils.py +++ b/tests/unit_tests/utils/test_weight_utils.py @@ -1,12 +1,10 @@ import logging import numpy as np -from hypothesis import settings import bittensor.utils.weight_utils as weight_utils import pytest from bittensor.utils import torch -from bittensor.core.settings import version_as_int def test_convert_weight_and_uids():