diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index a306f9a4bf..c78f28f165 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 @@ -768,7 +769,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, ) @@ -2288,29 +2290,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( @@ -2321,36 +2329,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 4afe9d95e4..b28c49533f 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -9,9 +9,10 @@ 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 -from bittensor.utils import deprecated_message + from bittensor.core.async_subtensor import ProposalVoteData from bittensor.core.axon import Axon from bittensor.core.chain_data import ( @@ -105,6 +106,7 @@ torch, u16_normalized_float, u64_normalized_float, + deprecated_message, ) from bittensor.utils.balance import ( Balance, @@ -428,8 +430,7 @@ 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], + method="state_call", params=[method, data], block_hash=block_hash ) # Common subtensor calls =========================================================================================== @@ -1530,31 +1531,41 @@ 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", + 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", + 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", + 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 @@ -1564,26 +1575,38 @@ 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, ) + 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/bittensor/utils/balance.py b/bittensor/utils/balance.py index 07a8431e66..99c1a85831 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -350,8 +350,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) diff --git a/pyproject.toml b/pyproject.toml index de109b014b..50aee3d831 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.2" ] [project.optional-dependencies] 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_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 93573438d2..05dbc6f760 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 217caeb9a9..aa5121408e 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 @@ -1497,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): @@ -3845,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,), @@ -3879,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():