bittensor-cli 8.3.1__py3-none-any.whl → 8.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bittensor_cli/__init__.py CHANGED
@@ -18,6 +18,6 @@
18
18
  from .cli import CLIManager
19
19
 
20
20
 
21
- __version__ = "8.3.1"
21
+ __version__ = "8.4.0"
22
22
 
23
23
  __all__ = ["CLIManager", "__version__"]
bittensor_cli/cli.py CHANGED
@@ -58,7 +58,7 @@ except ImportError:
58
58
  pass
59
59
 
60
60
 
61
- __version__ = "8.3.1"
61
+ __version__ = "8.4.0"
62
62
 
63
63
 
64
64
  _core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0)
@@ -127,8 +127,6 @@ class Options:
127
127
  use_password = typer.Option(
128
128
  True,
129
129
  help="Set this to `True` to protect the generated Bittensor key with a password.",
130
- is_flag=True,
131
- flag_value=False,
132
130
  )
133
131
  public_hex_key = typer.Option(None, help="The public key in hex format.")
134
132
  ss58_address = typer.Option(
@@ -1843,8 +1841,6 @@ class CLIManager:
1843
1841
  use_password: bool = typer.Option(
1844
1842
  False, # Overriden to False
1845
1843
  help="Set to 'True' to protect the generated Bittensor key with a password.",
1846
- is_flag=True,
1847
- flag_value=True,
1848
1844
  ),
1849
1845
  quiet: bool = Options.quiet,
1850
1846
  verbose: bool = Options.verbose,
@@ -1901,8 +1897,6 @@ class CLIManager:
1901
1897
  use_password: bool = typer.Option(
1902
1898
  False, # Overriden to False
1903
1899
  help="Set to 'True' to protect the generated Bittensor key with a password.",
1904
- is_flag=True,
1905
- flag_value=True,
1906
1900
  ),
1907
1901
  quiet: bool = Options.quiet,
1908
1902
  verbose: bool = Options.verbose,
@@ -3910,6 +3904,16 @@ class CLIManager:
3910
3904
  )
3911
3905
  param_name = hyperparam_list[choice - 1]
3912
3906
 
3907
+ if param_name in ["alpha_high", "alpha_low"]:
3908
+ param_name = "alpha_values"
3909
+ low_val = FloatPrompt.ask(
3910
+ "Enter the new value for [dark_orange]alpha_low[/dark_orange]"
3911
+ )
3912
+ high_val = FloatPrompt.ask(
3913
+ "Enter the new value for [dark_orange]alpha_high[/dark_orange]"
3914
+ )
3915
+ param_value = f"{low_val},{high_val}"
3916
+
3913
3917
  if not param_value:
3914
3918
  param_value = Prompt.ask(
3915
3919
  f"Enter the new value for [dark_orange]{param_name}[/dark_orange] in the VALUE column format"
@@ -318,28 +318,32 @@ NETWORK_EXPLORER_MAP = {
318
318
 
319
319
 
320
320
  HYPERPARAMS = {
321
- "serving_rate_limit": "sudo_set_serving_rate_limit",
321
+ "rho": "sudo_set_rho",
322
+ "kappa": "sudo_set_kappa",
323
+ "immunity_period": "sudo_set_immunity_period",
324
+ "min_allowed_weights": "sudo_set_min_allowed_weights",
325
+ "max_weights_limit": "sudo_set_max_weight_limit",
326
+ "tempo": "sudo_set_tempo",
322
327
  "min_difficulty": "sudo_set_min_difficulty",
323
328
  "max_difficulty": "sudo_set_max_difficulty",
324
329
  "weights_version": "sudo_set_weights_version_key",
325
330
  "weights_rate_limit": "sudo_set_weights_set_rate_limit",
326
- "max_weight_limit": "sudo_set_max_weight_limit",
327
- "immunity_period": "sudo_set_immunity_period",
328
- "min_allowed_weights": "sudo_set_min_allowed_weights",
331
+ "adjustment_interval": "sudo_set_adjustment_interval",
329
332
  "activity_cutoff": "sudo_set_activity_cutoff",
330
- "network_registration_allowed": "sudo_set_network_registration_allowed",
331
- "network_pow_registration_allowed": "sudo_set_network_pow_registration_allowed",
333
+ "target_regs_per_interval": "sudo_set_target_registrations_per_interval",
332
334
  "min_burn": "sudo_set_min_burn",
333
335
  "max_burn": "sudo_set_max_burn",
336
+ "bonds_moving_avg": "sudo_set_bonds_moving_average",
337
+ "max_regs_per_block": "sudo_set_max_registrations_per_block",
338
+ "serving_rate_limit": "sudo_set_serving_rate_limit",
339
+ "max_validators": "sudo_set_max_allowed_validators",
334
340
  "adjustment_alpha": "sudo_set_adjustment_alpha",
335
- "rho": "sudo_set_rho",
336
- "kappa": "sudo_set_kappa",
337
341
  "difficulty": "sudo_set_difficulty",
338
- "bonds_moving_avg": "sudo_set_bonds_moving_average",
339
342
  "commit_reveal_weights_interval": "sudo_set_commit_reveal_weights_interval",
340
343
  "commit_reveal_weights_enabled": "sudo_set_commit_reveal_weights_enabled",
341
344
  "alpha_values": "sudo_set_alpha_values",
342
345
  "liquid_alpha_enabled": "sudo_set_liquid_alpha_enabled",
346
+ "registration_allowed": "sudo_set_network_registration_allowed",
343
347
  }
344
348
 
345
349
  # Help Panels for cli help
@@ -4,12 +4,11 @@ import random
4
4
  from collections import defaultdict
5
5
  from dataclasses import dataclass
6
6
  from hashlib import blake2b
7
- from typing import Optional, Any, Union, Callable, Awaitable, cast
7
+ from typing import Optional, Any, Union, Callable, Awaitable, cast, TYPE_CHECKING
8
8
 
9
9
  from async_property import async_property
10
10
  from bt_decode import PortableRegistry, decode as decode_by_type_string, MetadataV15
11
11
  from bittensor_wallet import Keypair
12
- from packaging import version
13
12
  from scalecodec import GenericExtrinsic
14
13
  from scalecodec.base import ScaleBytes, ScaleType, RuntimeConfigurationObject
15
14
  from scalecodec.type_registry import load_type_registry_preset
@@ -20,7 +19,13 @@ from substrateinterface.exceptions import (
20
19
  BlockNotFound,
21
20
  )
22
21
  from substrateinterface.storage import StorageKey
23
- import websockets
22
+ from websockets.asyncio.client import connect
23
+ from websockets.exceptions import ConnectionClosed
24
+
25
+ from bittensor_cli.src.bittensor.utils import hex_to_bytes
26
+
27
+ if TYPE_CHECKING:
28
+ from websockets.asyncio.client import ClientConnection
24
29
 
25
30
  ResultHandler = Callable[[dict, Any], Awaitable[tuple[dict, bool]]]
26
31
 
@@ -434,7 +439,7 @@ class RuntimeCache:
434
439
  self.block_hashes[block_hash] = runtime
435
440
 
436
441
  def retrieve(
437
- self, block: Optional[int], block_hash: Optional[str]
442
+ self, block: Optional[int] = None, block_hash: Optional[str] = None
438
443
  ) -> Optional["Runtime"]:
439
444
  if block is not None:
440
445
  return self.blocks.get(block)
@@ -625,7 +630,7 @@ class Websocket:
625
630
  # TODO allow setting max concurrent connections and rpc subscriptions per connection
626
631
  # TODO reconnection logic
627
632
  self.ws_url = ws_url
628
- self.ws: Optional[websockets.WebSocketClientProtocol] = None
633
+ self.ws: Optional["ClientConnection"] = None
629
634
  self.id = 0
630
635
  self.max_subscriptions = max_subscriptions
631
636
  self.max_connections = max_connections
@@ -647,15 +652,12 @@ class Websocket:
647
652
  self._exit_task.cancel()
648
653
  if not self._initialized:
649
654
  self._initialized = True
650
- await self._connect()
655
+ self.ws = await asyncio.wait_for(
656
+ connect(self.ws_url, **self._options), timeout=10
657
+ )
651
658
  self._receiving_task = asyncio.create_task(self._start_receiving())
652
659
  return self
653
660
 
654
- async def _connect(self):
655
- self.ws = await asyncio.wait_for(
656
- websockets.connect(self.ws_url, **self._options), timeout=10
657
- )
658
-
659
661
  async def __aexit__(self, exc_type, exc_val, exc_tb):
660
662
  async with self._lock:
661
663
  self._in_use -= 1
@@ -696,9 +698,7 @@ class Websocket:
696
698
 
697
699
  async def _recv(self) -> None:
698
700
  try:
699
- response = json.loads(
700
- await cast(websockets.WebSocketClientProtocol, self.ws).recv()
701
- )
701
+ response = json.loads(await self.ws.recv())
702
702
  async with self._lock:
703
703
  self._open_subscriptions -= 1
704
704
  if "id" in response:
@@ -707,7 +707,7 @@ class Websocket:
707
707
  self._received[response["params"]["subscription"]] = response
708
708
  else:
709
709
  raise KeyError(response)
710
- except websockets.ConnectionClosed:
710
+ except ConnectionClosed:
711
711
  raise
712
712
  except KeyError as e:
713
713
  raise e
@@ -718,7 +718,7 @@ class Websocket:
718
718
  await self._recv()
719
719
  except asyncio.CancelledError:
720
720
  pass
721
- except websockets.ConnectionClosed:
721
+ except ConnectionClosed:
722
722
  # TODO try reconnect, but only if it's needed
723
723
  raise
724
724
 
@@ -735,7 +735,7 @@ class Websocket:
735
735
  try:
736
736
  await self.ws.send(json.dumps({**payload, **{"id": original_id}}))
737
737
  return original_id
738
- except websockets.ConnectionClosed:
738
+ except ConnectionClosed:
739
739
  raise
740
740
 
741
741
  async def retrieve(self, item_id: int) -> Optional[dict]:
@@ -772,13 +772,13 @@ class AsyncSubstrateInterface:
772
772
  """
773
773
  self.chain_endpoint = chain_endpoint
774
774
  self.__chain = chain_name
775
- options = {
776
- "max_size": 2**32,
777
- "write_limit": 2**16,
778
- }
779
- if version.parse(websockets.__version__) < version.parse("14.0"):
780
- options.update({"read_limit": 2**16})
781
- self.ws = Websocket(chain_endpoint, options=options)
775
+ self.ws = Websocket(
776
+ chain_endpoint,
777
+ options={
778
+ "max_size": 2**32,
779
+ "write_limit": 2**16,
780
+ },
781
+ )
782
782
  self._lock = asyncio.Lock()
783
783
  self.last_block_hash: Optional[str] = None
784
784
  self.config = {
@@ -1135,7 +1135,7 @@ class AsyncSubstrateInterface:
1135
1135
  -------
1136
1136
  StorageKey
1137
1137
  """
1138
- runtime = await self.init_runtime(block_hash=block_hash)
1138
+ await self.init_runtime(block_hash=block_hash)
1139
1139
 
1140
1140
  return StorageKey.create_from_storage_function(
1141
1141
  pallet,
@@ -1555,7 +1555,7 @@ class AsyncSubstrateInterface:
1555
1555
  self,
1556
1556
  response: dict,
1557
1557
  subscription_id: Union[int, str],
1558
- value_scale_type: Optional[str],
1558
+ value_scale_type: Optional[str] = None,
1559
1559
  storage_item: Optional[ScaleType] = None,
1560
1560
  runtime: Optional[Runtime] = None,
1561
1561
  result_handler: Optional[ResultHandler] = None,
@@ -1769,7 +1769,6 @@ class AsyncSubstrateInterface:
1769
1769
  call_params = {}
1770
1770
 
1771
1771
  await self.init_runtime(block_hash=block_hash)
1772
-
1773
1772
  call = self.runtime_config.create_scale_object(
1774
1773
  type_string="Call", metadata=self.metadata
1775
1774
  )
@@ -2087,7 +2086,8 @@ class AsyncSubstrateInterface:
2087
2086
 
2088
2087
  :return: The signed Extrinsic
2089
2088
  """
2090
- await self.init_runtime()
2089
+ if not self.metadata:
2090
+ await self.init_runtime()
2091
2091
 
2092
2092
  # Check requirements
2093
2093
  if not isinstance(call, GenericCall):
@@ -2140,7 +2140,6 @@ class AsyncSubstrateInterface:
2140
2140
  extrinsic = self.runtime_config.create_scale_object(
2141
2141
  type_string="Extrinsic", metadata=self.metadata
2142
2142
  )
2143
-
2144
2143
  value = {
2145
2144
  "account_id": f"0x{keypair.public_key.hex()}",
2146
2145
  "signature": f"0x{signature.hex()}",
@@ -2158,9 +2157,7 @@ class AsyncSubstrateInterface:
2158
2157
  signature_cls = self.runtime_config.get_decoder_class("ExtrinsicSignature")
2159
2158
  if issubclass(signature_cls, self.runtime_config.get_decoder_class("Enum")):
2160
2159
  value["signature_version"] = signature_version
2161
-
2162
2160
  extrinsic.encode(value)
2163
-
2164
2161
  return extrinsic
2165
2162
 
2166
2163
  async def get_chain_finalised_head(self):
@@ -2564,10 +2561,7 @@ class AsyncSubstrateInterface:
2564
2561
  item_key = None
2565
2562
 
2566
2563
  try:
2567
- try:
2568
- item_bytes = bytes.fromhex(item[1][2:])
2569
- except ValueError:
2570
- item_bytes = bytes.fromhex(item[1])
2564
+ item_bytes = hex_to_bytes(item[1])
2571
2565
 
2572
2566
  item_value = await self.decode_scale(
2573
2567
  type_string=value_type,
@@ -18,7 +18,6 @@ import typing
18
18
  from typing import Optional
19
19
  import subprocess
20
20
 
21
- import backoff
22
21
  from bittensor_wallet import Wallet
23
22
  from Crypto.Hash import keccak
24
23
  import numpy as np
@@ -37,6 +36,7 @@ from bittensor_cli.src.bittensor.utils import (
37
36
  print_verbose,
38
37
  print_error,
39
38
  unlock_key,
39
+ hex_to_bytes,
40
40
  )
41
41
 
42
42
  if typing.TYPE_CHECKING:
@@ -687,7 +687,7 @@ async def run_faucet_extrinsic(
687
687
  tpb: int = 256,
688
688
  num_processes: Optional[int] = None,
689
689
  update_interval: Optional[int] = None,
690
- log_verbose: bool = False,
690
+ log_verbose: bool = True,
691
691
  max_successes: int = 3,
692
692
  ) -> tuple[bool, str]:
693
693
  r"""Runs a continual POW to get a faucet of TAO on the test net.
@@ -735,8 +735,12 @@ async def run_faucet_extrinsic(
735
735
  # Attempt rolling registration.
736
736
  attempts = 1
737
737
  successes = 1
738
+ pow_result: Optional[POWSolution]
738
739
  while True:
739
740
  try:
741
+ account_nonce = await subtensor.substrate.get_account_nonce(
742
+ wallet.coldkey.ss58_address
743
+ )
740
744
  pow_result = None
741
745
  while pow_result is None or await pow_result.is_stale(subtensor=subtensor):
742
746
  # Solve latest POW.
@@ -745,7 +749,7 @@ async def run_faucet_extrinsic(
745
749
  if prompt:
746
750
  err_console.print("CUDA is not available.")
747
751
  return False, "CUDA is not available."
748
- pow_result: Optional[POWSolution] = await create_pow(
752
+ pow_result = await create_pow(
749
753
  subtensor,
750
754
  wallet,
751
755
  -1,
@@ -758,7 +762,7 @@ async def run_faucet_extrinsic(
758
762
  log_verbose=log_verbose,
759
763
  )
760
764
  else:
761
- pow_result: Optional[POWSolution] = await create_pow(
765
+ pow_result = await create_pow(
762
766
  subtensor,
763
767
  wallet,
764
768
  -1,
@@ -778,7 +782,7 @@ async def run_faucet_extrinsic(
778
782
  },
779
783
  )
780
784
  extrinsic = await subtensor.substrate.create_signed_extrinsic(
781
- call=call, keypair=wallet.coldkey
785
+ call=call, keypair=wallet.coldkey, nonce=account_nonce
782
786
  )
783
787
  response = await subtensor.substrate.submit_extrinsic(
784
788
  extrinsic,
@@ -863,7 +867,7 @@ async def _check_for_newest_block_and_update(
863
867
  block_number, difficulty, block_hash = await _get_block_with_retry(
864
868
  subtensor=subtensor, netuid=netuid
865
869
  )
866
- block_bytes = bytes.fromhex(block_hash[2:])
870
+ block_bytes = hex_to_bytes(block_hash)
867
871
 
868
872
  update_curr_block(
869
873
  curr_diff,
@@ -970,7 +974,7 @@ async def _block_solver(
970
974
  subtensor=subtensor, netuid=netuid
971
975
  )
972
976
 
973
- block_bytes = bytes.fromhex(block_hash[2:])
977
+ block_bytes = hex_to_bytes(block_hash)
974
978
  old_block_number = block_number
975
979
  # Set to current block
976
980
  _update_curr_block(
@@ -1245,11 +1249,9 @@ def _terminate_workers_and_wait_for_exit(
1245
1249
  worker.terminate()
1246
1250
 
1247
1251
 
1248
- # TODO verify this works with async
1249
- @backoff.on_exception(backoff.constant, Exception, interval=1, max_tries=3)
1250
1252
  async def _get_block_with_retry(
1251
1253
  subtensor: "SubtensorInterface", netuid: int
1252
- ) -> tuple[int, int, bytes]:
1254
+ ) -> tuple[int, int, str]:
1253
1255
  """
1254
1256
  Gets the current block number, difficulty, and block hash from the substrate node.
1255
1257
 
@@ -1261,10 +1263,9 @@ async def _get_block_with_retry(
1261
1263
  :raises Exception: If the block hash is None.
1262
1264
  :raises ValueError: If the difficulty is None.
1263
1265
  """
1264
- block_number = await subtensor.substrate.get_block_number(None)
1265
- block_hash = await subtensor.substrate.get_block_hash(
1266
- block_number
1267
- ) # TODO check if I need to do all this
1266
+ block = await subtensor.substrate.get_block()
1267
+ block_hash = block["header"]["hash"]
1268
+ block_number = block["header"]["number"]
1268
1269
  try:
1269
1270
  difficulty = (
1270
1271
  1_000_000
@@ -35,6 +35,7 @@ from bittensor_cli.src.bittensor.utils import (
35
35
  err_console,
36
36
  decode_hex_identity_dict,
37
37
  validate_chain_endpoint,
38
+ hex_to_bytes,
38
39
  )
39
40
 
40
41
 
@@ -213,12 +214,7 @@ class SubtensorInterface:
213
214
  block_hash=block_hash,
214
215
  )
215
216
  if hex_bytes_result is not None:
216
- try:
217
- bytes_result = bytes.fromhex(hex_bytes_result[2:])
218
- except ValueError:
219
- bytes_result = bytes.fromhex(hex_bytes_result)
220
-
221
- return DelegateInfo.list_from_vec_u8(bytes_result)
217
+ return DelegateInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
222
218
  else:
223
219
  return []
224
220
 
@@ -254,12 +250,7 @@ class SubtensorInterface:
254
250
  if hex_bytes_result is None:
255
251
  return []
256
252
 
257
- try:
258
- bytes_result = bytes.fromhex(hex_bytes_result[2:])
259
- except ValueError:
260
- bytes_result = bytes.fromhex(hex_bytes_result)
261
-
262
- return StakeInfo.list_from_vec_u8(bytes_result)
253
+ return StakeInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
263
254
 
264
255
  async def get_stake_for_coldkey_and_hotkey(
265
256
  self, hotkey_ss58: str, coldkey_ss58: str, block_hash: Optional[str]
@@ -654,12 +645,7 @@ class SubtensorInterface:
654
645
  if hex_bytes_result is None:
655
646
  return []
656
647
 
657
- try:
658
- bytes_result = bytes.fromhex(hex_bytes_result[2:])
659
- except ValueError:
660
- bytes_result = bytes.fromhex(hex_bytes_result)
661
-
662
- return NeuronInfoLite.list_from_vec_u8(bytes_result)
648
+ return NeuronInfoLite.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
663
649
 
664
650
  async def neuron_for_uid(
665
651
  self, uid: Optional[int], netuid: int, block_hash: Optional[str] = None
@@ -988,12 +974,7 @@ class SubtensorInterface:
988
974
  if hex_bytes_result is None:
989
975
  return []
990
976
 
991
- if hex_bytes_result.startswith("0x"):
992
- bytes_result = bytes.fromhex(hex_bytes_result[2:])
993
- else:
994
- bytes_result = bytes.fromhex(hex_bytes_result)
995
-
996
- return SubnetHyperparameters.from_vec_u8(bytes_result)
977
+ return SubnetHyperparameters.from_vec_u8(hex_to_bytes(hex_bytes_result))
997
978
 
998
979
  async def get_vote_data(
999
980
  self,
@@ -581,7 +581,7 @@ def decode_hex_identity_dict(info_dictionary) -> dict[str, Any]:
581
581
  def get_decoded(data: str) -> str:
582
582
  """Decodes a hex-encoded string."""
583
583
  try:
584
- return bytes.fromhex(data[2:]).decode()
584
+ return hex_to_bytes(data).decode()
585
585
  except UnicodeDecodeError:
586
586
  print(f"Could not decode: {key}: {item}")
587
587
 
@@ -1016,3 +1016,14 @@ def unlock_key(
1016
1016
  if print_out:
1017
1017
  err_console.print(f":cross_mark: [red]{err_msg}[/red]")
1018
1018
  return UnlockStatus(False, err_msg)
1019
+
1020
+
1021
+ def hex_to_bytes(hex_str: str) -> bytes:
1022
+ """
1023
+ Converts a hex-encoded string into bytes. Handles 0x-prefixed and non-prefixed hex-encoded strings.
1024
+ """
1025
+ if hex_str.startswith("0x"):
1026
+ bytes_result = bytes.fromhex(hex_str[2:])
1027
+ else:
1028
+ bytes_result = bytes.fromhex(hex_str)
1029
+ return bytes_result
@@ -28,6 +28,7 @@ from bittensor_cli.src.bittensor.utils import (
28
28
  render_table,
29
29
  update_metadata_table,
30
30
  unlock_key,
31
+ hex_to_bytes,
31
32
  )
32
33
 
33
34
  if TYPE_CHECKING:
@@ -155,12 +156,8 @@ async def subnets_list(
155
156
  hex_bytes_result = await subtensor.query_runtime_api(
156
157
  runtime_api="SubnetInfoRuntimeApi", method="get_subnets_info", params=[]
157
158
  )
158
- try:
159
- bytes_result = bytes.fromhex(hex_bytes_result[2:])
160
- except ValueError:
161
- bytes_result = bytes.fromhex(hex_bytes_result)
162
159
 
163
- return SubnetInfo.list_from_vec_u8(bytes_result)
160
+ return SubnetInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
164
161
 
165
162
  if not reuse_last:
166
163
  subnets: list[SubnetInfo]
@@ -178,19 +178,20 @@ async def sudo_set_hyperparameter(
178
178
 
179
179
  normalized_value: Union[str, bool]
180
180
  if param_name in [
181
- "network_registration_allowed",
181
+ "registration_allowed",
182
182
  "network_pow_registration_allowed",
183
183
  "commit_reveal_weights_enabled",
184
184
  "liquid_alpha_enabled",
185
185
  ]:
186
- normalized_value = param_value.lower() in ["true", "1"]
186
+ normalized_value = param_value.lower() in ["true", "True", "1"]
187
187
  else:
188
188
  normalized_value = param_value
189
189
 
190
190
  is_allowed_value, value = allowed_value(param_name, normalized_value)
191
191
  if not is_allowed_value:
192
192
  err_console.print(
193
- f"Hyperparameter {param_name} value is not within bounds. Value is {normalized_value} but must be {value}"
193
+ f"Hyperparameter [dark_orange]{param_name}[/dark_orange] value is not within bounds. "
194
+ f"Value is {normalized_value} but must be {value}"
194
195
  )
195
196
  return
196
197
 
@@ -15,23 +15,19 @@ from bittensor_wallet.keyfile import Keyfile
15
15
  from fuzzywuzzy import fuzz
16
16
  from rich import box
17
17
  from rich.align import Align
18
- from rich.prompt import Confirm, Prompt
18
+ from rich.prompt import Confirm
19
19
  from rich.table import Column, Table
20
20
  from rich.tree import Tree
21
21
  from rich.padding import Padding
22
22
  from rich.prompt import IntPrompt
23
- from scalecodec import ScaleBytes
24
- import scalecodec
25
23
  import typer
26
24
 
27
- from bittensor_cli.src import TYPE_REGISTRY
28
25
  from bittensor_cli.src.bittensor import utils
29
26
  from bittensor_cli.src.bittensor.balances import Balance
30
27
  from bittensor_cli.src.bittensor.chain_data import (
31
28
  DelegateInfo,
32
29
  NeuronInfoLite,
33
30
  StakeInfo,
34
- custom_rpc_type_registry,
35
31
  decode_account_id,
36
32
  )
37
33
  from bittensor_cli.src.bittensor.extrinsics.registration import (
@@ -46,7 +42,6 @@ from bittensor_cli.src.bittensor.utils import (
46
42
  RAO_PER_TAO,
47
43
  console,
48
44
  convert_blocks_to_time,
49
- decode_scale_bytes,
50
45
  err_console,
51
46
  print_error,
52
47
  print_verbose,
@@ -56,6 +51,7 @@ from bittensor_cli.src.bittensor.utils import (
56
51
  validate_coldkey_presence,
57
52
  retry_prompt,
58
53
  unlock_key,
54
+ hex_to_bytes,
59
55
  )
60
56
 
61
57
 
@@ -660,8 +656,13 @@ async def overview(
660
656
  neurons
661
657
  )
662
658
 
659
+ has_alerts = False
663
660
  alerts_table = Table(show_header=True, header_style="bold magenta")
664
661
  alerts_table.add_column("🥩 alert!")
662
+ alerts_table.add_row(
663
+ "[bold]Detected the following stake(s) associated with coldkey(s) that are not linked to any local hotkeys:[/bold]"
664
+ )
665
+ alerts_table.add_row("")
665
666
 
666
667
  coldkeys_to_check = []
667
668
  ck_stakes = await subtensor.get_total_stake_for_coldkey(
@@ -684,12 +685,23 @@ async def overview(
684
685
  if difference == 0:
685
686
  continue # We have all our stake registered.
686
687
 
688
+ has_alerts = True
687
689
  coldkeys_to_check.append(coldkey_wallet)
688
690
  alerts_table.add_row(
689
- "Found [light_goldenrod2]{}[/light_goldenrod2] stake with coldkey [bright_magenta]{}[/bright_magenta] that is not registered.".format(
690
- abs(difference), coldkey_wallet.coldkeypub.ss58_address
691
+ "[light_goldenrod2]{}[/light_goldenrod2] stake associated with coldkey [bright_magenta]{}[/bright_magenta] (ss58: [bright_magenta]{}[/bright_magenta])".format(
692
+ abs(difference),
693
+ coldkey_wallet.name,
694
+ coldkey_wallet.coldkeypub.ss58_address,
691
695
  )
692
696
  )
697
+ if has_alerts:
698
+ alerts_table.add_row("")
699
+ alerts_table.add_row(
700
+ "[bold yellow]Note:[/bold yellow] This stake might be delegated, staked to another user's hotkey, or associated with a hotkey not present in your wallet."
701
+ )
702
+ alerts_table.add_row(
703
+ "You can find out more by executing `[bold]btcli wallet inspect[/bold]` command."
704
+ )
693
705
 
694
706
  if coldkeys_to_check:
695
707
  # We have some stake that is not with a registered hotkey.
@@ -743,7 +755,7 @@ async def overview(
743
755
  grid = Table.grid(pad_edge=True)
744
756
 
745
757
  # If there are any alerts, add them to the grid
746
- if len(alerts_table.rows) > 0:
758
+ if has_alerts:
747
759
  grid.add_row(alerts_table)
748
760
 
749
761
  # Add title
@@ -1176,7 +1188,7 @@ def _process_neurons_for_netuids(
1176
1188
  :return: netuids mapped to decoded neurons
1177
1189
  """
1178
1190
  all_results = [
1179
- (netuid, NeuronInfoLite.list_from_vec_u8(bytes.fromhex(result[2:])))
1191
+ (netuid, NeuronInfoLite.list_from_vec_u8(hex_to_bytes(result)))
1180
1192
  if result
1181
1193
  else (netuid, [])
1182
1194
  for netuid, result in netuids_with_all_neurons_hex_bytes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bittensor-cli
3
- Version: 8.3.1
3
+ Version: 8.4.0
4
4
  Summary: Bittensor CLI
5
5
  Home-page: https://github.com/opentensor/btcli
6
6
  Author: bittensor.com
@@ -32,7 +32,6 @@ Requires-Dist: fuzzywuzzy~=0.18.0
32
32
  Requires-Dist: netaddr~=1.3.0
33
33
  Requires-Dist: numpy>=2.0.1
34
34
  Requires-Dist: Jinja2
35
- Requires-Dist: packaging
36
35
  Requires-Dist: pycryptodome
37
36
  Requires-Dist: PyYAML~=6.0.1
38
37
  Requires-Dist: pytest
@@ -41,8 +40,8 @@ Requires-Dist: rich~=13.7
41
40
  Requires-Dist: scalecodec==1.2.11
42
41
  Requires-Dist: substrate-interface~=1.7.9
43
42
  Requires-Dist: typer~=0.12
44
- Requires-Dist: websockets>=12.0
45
- Requires-Dist: bittensor-wallet>=2.1.0
43
+ Requires-Dist: websockets>=14.1
44
+ Requires-Dist: bittensor-wallet>=2.1.3
46
45
  Requires-Dist: bt-decode==0.2.0a0
47
46
  Provides-Extra: cuda
48
47
  Requires-Dist: cubit>=1.1.0; extra == "cuda"
@@ -1,31 +1,31 @@
1
- bittensor_cli/__init__.py,sha256=yocN6uqpsPRC1tYESgUAxLcB-JDVYswhljK3KdC2DUA,1217
2
- bittensor_cli/cli.py,sha256=ew32_K2VZ4-l6DsETJV6mjLpGBAa-VTUhx_8mBTG6vM,169772
1
+ bittensor_cli/__init__.py,sha256=0dDE_gb7wXMgoxy5mCs0B4IjiojCNSEiBmyJTyKwCTg,1217
2
+ bittensor_cli/cli.py,sha256=1C9xRsNq3CZHEsYcvw50IemjLxQ7olSljX6wK1WwK6g,170025
3
3
  bittensor_cli/doc_generation_helper.py,sha256=GexqjEIKulWg84hpNBEchJ840oOgOi7DWpt447nsdNI,91
4
- bittensor_cli/src/__init__.py,sha256=9hFSqapIynZbrjc9YHl2wARkPdk4pV5P5zLSvn-EbqM,11856
4
+ bittensor_cli/src/__init__.py,sha256=1_hTPLBqA6gV8HTL-v0_RVTPt7TQJEJEv-TIw9f79OE,12055
5
5
  bittensor_cli/src/bittensor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- bittensor_cli/src/bittensor/async_substrate_interface.py,sha256=eXF5IxHMKJNNPzw_JdfhDiijghBQzOhEnk2i5F2L1OQ,103367
6
+ bittensor_cli/src/bittensor/async_substrate_interface.py,sha256=o7Pe_Nxsys8aPm_xsih2xR8ArhZrUu9yYobf8qBvT5s,103185
7
7
  bittensor_cli/src/bittensor/balances.py,sha256=102mg1iiliLSLx-EocQseWGHiWzRqcBBM-MRoObOdKo,10075
8
8
  bittensor_cli/src/bittensor/chain_data.py,sha256=IsHWjmhJbTvud2i3IM1sfmBHP82VxNHC1iFDAtlLgXc,26490
9
9
  bittensor_cli/src/bittensor/minigraph.py,sha256=17AjEA75MO7ez_NekOJSaAz6ARjPt99QG_7E1RJ_u_k,8891
10
10
  bittensor_cli/src/bittensor/networking.py,sha256=pZLMs8YXpZzDMLXWMBb_Bj6TVkm_q9khyY-lnbwVMuE,462
11
- bittensor_cli/src/bittensor/subtensor_interface.py,sha256=cwbRhS5qV0Jmref5Qe7rsuDy8ssrRqdObKHvaeGeoOc,43373
12
- bittensor_cli/src/bittensor/utils.py,sha256=_uJ7aiYJL6r-Cgyc0NDWBHVkdeyaGbFRYH7PABeM-iM,35184
11
+ bittensor_cli/src/bittensor/subtensor_interface.py,sha256=_99AjukDm7ijEwQUA6TXctZFrj9IRuOzJdheKNmbZ5s,42775
12
+ bittensor_cli/src/bittensor/utils.py,sha256=60h45hRVTaLhhOOTHmNurY3wHb0Jpt9pIhS3joVM7d4,35505
13
13
  bittensor_cli/src/bittensor/extrinsics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- bittensor_cli/src/bittensor/extrinsics/registration.py,sha256=O3ikqyEbosvp3e9vCpMQ7cfjVdd6R4uiPYkXkiawxI4,59045
14
+ bittensor_cli/src/bittensor/extrinsics/registration.py,sha256=87L7xs64beu0htRaiykaJcYLyRmxoFzF5PFNumNuhcw,59010
15
15
  bittensor_cli/src/bittensor/extrinsics/root.py,sha256=huycqHbSTFBNIhQ846MuBqwBEtQCSVDW3y3yPAqPzeU,19121
16
16
  bittensor_cli/src/bittensor/extrinsics/transfer.py,sha256=yC5oKgoKAXszMzfrof3GbeGppESUMgSqJFFGOMMALuc,8686
17
17
  bittensor_cli/src/bittensor/templates/table.j2,sha256=P2EFiksnO1cQsB8zjK6hVJwUryHTsLslzRE0YtobAV8,10601
18
18
  bittensor_cli/src/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  bittensor_cli/src/commands/root.py,sha256=hohGfh7w9Lu7z264rbmFkwRCStyZrtdBZVMt20RwiqA,62186
20
- bittensor_cli/src/commands/subnets.py,sha256=zlnWU-wmJ2GvhM1T3EN9nO_uzjHBai73i48NXGp9P94,33301
21
- bittensor_cli/src/commands/sudo.py,sha256=tIkrqUpn-xpbrRLr3jBmjNauP8NKbLloRg37p65llNM,8166
22
- bittensor_cli/src/commands/wallets.py,sha256=CCY9rz623ob-JcGU9pJxYwstMvlH8jdMc-sa7J-5XsU,60057
20
+ bittensor_cli/src/commands/subnets.py,sha256=_M2EJLdHaLBN04C22A-Xn6weN-3j5dU5frLe6W5Q2rs,33175
21
+ bittensor_cli/src/commands/sudo.py,sha256=sr4hW27EwW_O7lz0fwPLN3xRpVKNsdBasETIbGDhkb0,8209
22
+ bittensor_cli/src/commands/wallets.py,sha256=BuPJiEDOpHuGRx3zekU2QkFQjFpCUP-Kdk4opS5YfWo,60678
23
23
  bittensor_cli/src/commands/weights.py,sha256=UHakAMTNqRkg_fkiyebbZAT9T3UEf93MmdbFgPSbqUw,16520
24
24
  bittensor_cli/src/commands/stake/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  bittensor_cli/src/commands/stake/children_hotkeys.py,sha256=j239HdawvHp9rTbIx9MI9v9hkaKbA5OaN_cIQISkHdw,28606
26
26
  bittensor_cli/src/commands/stake/stake.py,sha256=7TyFtL5wnXvGvD-GckNrNTRdcu3RewAfx_JfEekugFU,56147
27
- bittensor_cli-8.3.1.dist-info/METADATA,sha256=KBU7oULNqbxQySkMNj3aWwUkVJPsZwgT_JqoIOcg_cY,6813
28
- bittensor_cli-8.3.1.dist-info/WHEEL,sha256=bFJAMchF8aTQGUgMZzHJyDDMPTO3ToJ7x23SLJa1SVo,92
29
- bittensor_cli-8.3.1.dist-info/entry_points.txt,sha256=hBTLGLbVxmAKy69XSKaUZvjTCmyEzDGZKq4S8UOto8I,49
30
- bittensor_cli-8.3.1.dist-info/top_level.txt,sha256=DvgvXpmTtI_Q1BbDZMlK90LFcGFCreN1daViEPV2iFw,14
31
- bittensor_cli-8.3.1.dist-info/RECORD,,
27
+ bittensor_cli-8.4.0.dist-info/METADATA,sha256=XJctL-W1oJCDVKEvsDOrEG_oh6t8ycRPeXudbVaQ174,6788
28
+ bittensor_cli-8.4.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
29
+ bittensor_cli-8.4.0.dist-info/entry_points.txt,sha256=hBTLGLbVxmAKy69XSKaUZvjTCmyEzDGZKq4S8UOto8I,49
30
+ bittensor_cli-8.4.0.dist-info/top_level.txt,sha256=DvgvXpmTtI_Q1BbDZMlK90LFcGFCreN1daViEPV2iFw,14
31
+ bittensor_cli-8.4.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.0)
2
+ Generator: bdist_wheel (0.45.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5