bittensor-cli 8.2.0__py3-none-any.whl → 8.3.1__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.
@@ -4,16 +4,18 @@ from typing import Any, Optional
4
4
 
5
5
 
6
6
  class Constants:
7
- networks = ["local", "finney", "test", "archive"]
7
+ networks = ["local", "finney", "test", "archive", "subvortex"]
8
8
  finney_entrypoint = "wss://entrypoint-finney.opentensor.ai:443"
9
9
  finney_test_entrypoint = "wss://test.finney.opentensor.ai:443"
10
10
  archive_entrypoint = "wss://archive.chain.opentensor.ai:443"
11
- local_entrypoint = "ws://127.0.0.1:9444"
11
+ subvortex_entrypoint = "ws://subvortex.info:9944"
12
+ local_entrypoint = "ws://127.0.0.1:9944"
12
13
  network_map = {
13
14
  "finney": finney_entrypoint,
14
15
  "test": finney_test_entrypoint,
15
16
  "archive": archive_entrypoint,
16
17
  "local": local_entrypoint,
18
+ "subvortex": subvortex_entrypoint,
17
19
  }
18
20
  delegates_detail_url = "https://raw.githubusercontent.com/opentensor/bittensor-delegates/main/public/delegates.json"
19
21
 
@@ -6,13 +6,14 @@ from dataclasses import dataclass
6
6
  from hashlib import blake2b
7
7
  from typing import Optional, Any, Union, Callable, Awaitable, cast
8
8
 
9
- from bt_decode import PortableRegistry, decode as decode_by_type_string, MetadataV15
10
9
  from async_property import async_property
10
+ from bt_decode import PortableRegistry, decode as decode_by_type_string, MetadataV15
11
+ from bittensor_wallet import Keypair
12
+ from packaging import version
11
13
  from scalecodec import GenericExtrinsic
12
14
  from scalecodec.base import ScaleBytes, ScaleType, RuntimeConfigurationObject
13
15
  from scalecodec.type_registry import load_type_registry_preset
14
16
  from scalecodec.types import GenericCall
15
- from bittensor_wallet import Keypair
16
17
  from substrateinterface.exceptions import (
17
18
  SubstrateRequestException,
18
19
  ExtrinsicNotFound,
@@ -460,6 +461,9 @@ class Runtime:
460
461
  self.runtime_config = runtime_config
461
462
  self.metadata = metadata
462
463
 
464
+ def __str__(self):
465
+ return f"Runtime: {self.chain} | {self.config}"
466
+
463
467
  @property
464
468
  def implements_scaleinfo(self) -> bool:
465
469
  """
@@ -682,9 +686,9 @@ class Websocket:
682
686
  try:
683
687
  self._receiving_task.cancel()
684
688
  await self._receiving_task
689
+ await self.ws.close()
685
690
  except (AttributeError, asyncio.CancelledError):
686
691
  pass
687
- await self.ws.close()
688
692
  self.ws = None
689
693
  self._initialized = False
690
694
  self._receiving_task = None
@@ -768,14 +772,13 @@ class AsyncSubstrateInterface:
768
772
  """
769
773
  self.chain_endpoint = chain_endpoint
770
774
  self.__chain = chain_name
771
- self.ws = Websocket(
772
- chain_endpoint,
773
- options={
774
- "max_size": 2**32,
775
- "read_limit": 2**16,
776
- "write_limit": 2**16,
777
- },
778
- )
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)
779
782
  self._lock = asyncio.Lock()
780
783
  self.last_block_hash: Optional[str] = None
781
784
  self.config = {
@@ -897,9 +900,10 @@ class AsyncSubstrateInterface:
897
900
 
898
901
  async def get_runtime(block_hash, block_id) -> Runtime:
899
902
  # Check if runtime state already set to current block
900
- if (block_hash and block_hash == self.last_block_hash) or (
901
- block_id and block_id == self.block_id
902
- ):
903
+ if (
904
+ (block_hash and block_hash == self.last_block_hash)
905
+ or (block_id and block_id == self.block_id)
906
+ ) and self.metadata is not None:
903
907
  return Runtime(
904
908
  self.chain,
905
909
  self.runtime_config,
@@ -945,9 +949,11 @@ class AsyncSubstrateInterface:
945
949
  raise SubstrateRequestException(
946
950
  f"No runtime information for block '{block_hash}'"
947
951
  )
948
-
949
952
  # Check if runtime state already set to current block
950
- if runtime_info.get("specVersion") == self.runtime_version:
953
+ if (
954
+ runtime_info.get("specVersion") == self.runtime_version
955
+ and self.metadata is not None
956
+ ):
951
957
  return Runtime(
952
958
  self.chain,
953
959
  self.runtime_config,
@@ -962,16 +968,19 @@ class AsyncSubstrateInterface:
962
968
  if self.runtime_version in self.__metadata_cache:
963
969
  # Get metadata from cache
964
970
  # self.debug_message('Retrieved metadata for {} from memory'.format(self.runtime_version))
965
- self.metadata = self.__metadata_cache[self.runtime_version]
971
+ metadata = self.metadata = self.__metadata_cache[
972
+ self.runtime_version
973
+ ]
966
974
  else:
967
- self.metadata = await self.get_block_metadata(
975
+ metadata = self.metadata = await self.get_block_metadata(
968
976
  block_hash=runtime_block_hash, decode=True
969
977
  )
970
978
  # self.debug_message('Retrieved metadata for {} from Substrate node'.format(self.runtime_version))
971
979
 
972
980
  # Update metadata cache
973
981
  self.__metadata_cache[self.runtime_version] = self.metadata
974
-
982
+ else:
983
+ metadata = self.metadata
975
984
  # Update type registry
976
985
  self.reload_type_registry(use_remote_preset=False, auto_discover=True)
977
986
 
@@ -1012,7 +1021,10 @@ class AsyncSubstrateInterface:
1012
1021
  if block_id and block_hash:
1013
1022
  raise ValueError("Cannot provide block_hash and block_id at the same time")
1014
1023
 
1015
- if not (runtime := self.runtime_cache.retrieve(block_id, block_hash)):
1024
+ if (
1025
+ not (runtime := self.runtime_cache.retrieve(block_id, block_hash))
1026
+ or runtime.metadata is None
1027
+ ):
1016
1028
  runtime = await get_runtime(block_hash, block_id)
1017
1029
  self.runtime_cache.add_item(block_id, block_hash, runtime)
1018
1030
  return runtime
@@ -1123,7 +1135,7 @@ class AsyncSubstrateInterface:
1123
1135
  -------
1124
1136
  StorageKey
1125
1137
  """
1126
- await self.init_runtime(block_hash=block_hash)
1138
+ runtime = await self.init_runtime(block_hash=block_hash)
1127
1139
 
1128
1140
  return StorageKey.create_from_storage_function(
1129
1141
  pallet,
@@ -1707,9 +1719,7 @@ class AsyncSubstrateInterface:
1707
1719
  )
1708
1720
  result = await self._make_rpc_request(payloads, runtime=runtime)
1709
1721
  if "error" in result[payload_id][0]:
1710
- raise SubstrateRequestException(
1711
- result[payload_id][0]["error"]["message"]
1712
- )
1722
+ raise SubstrateRequestException(result[payload_id][0]["error"]["message"])
1713
1723
  if "result" in result[payload_id][0]:
1714
1724
  return result[payload_id][0]
1715
1725
  else:
@@ -2274,7 +2284,7 @@ class AsyncSubstrateInterface:
2274
2284
  MetadataModuleConstants
2275
2285
  """
2276
2286
 
2277
- # await self.init_runtime(block_hash=block_hash)
2287
+ await self.init_runtime(block_hash=block_hash)
2278
2288
 
2279
2289
  for module in self.metadata.pallets:
2280
2290
  if module_name == module.name and module.constants:
@@ -16,10 +16,10 @@ import random
16
16
  import time
17
17
  import typing
18
18
  from typing import Optional
19
+ import subprocess
19
20
 
20
21
  import backoff
21
22
  from bittensor_wallet import Wallet
22
- from bittensor_wallet.errors import KeyFileError
23
23
  from Crypto.Hash import keccak
24
24
  import numpy as np
25
25
  from rich.prompt import Confirm
@@ -35,6 +35,8 @@ from bittensor_cli.src.bittensor.utils import (
35
35
  millify,
36
36
  get_human_readable,
37
37
  print_verbose,
38
+ print_error,
39
+ unlock_key,
38
40
  )
39
41
 
40
42
  if typing.TYPE_CHECKING:
@@ -512,12 +514,13 @@ async def register_extrinsic(
512
514
  with console.status(
513
515
  f":satellite: Checking Account on [bold]subnet:{netuid}[/bold]...",
514
516
  spinner="aesthetic",
515
- ):
517
+ ) as status:
516
518
  neuron = await get_neuron_for_pubkey_and_subnet()
517
519
  if not neuron.is_null:
518
- # bittensor.logging.debug(
519
- # f"Wallet {wallet} is already registered on {neuron.netuid} with {neuron.uid}"
520
- # )
520
+ print_error(
521
+ f"Wallet {wallet} is already registered on subnet {neuron.netuid} with uid {neuron.uid}",
522
+ status,
523
+ )
521
524
  return True
522
525
 
523
526
  if prompt:
@@ -611,7 +614,8 @@ async def register_extrinsic(
611
614
  success, err_msg = True, ""
612
615
  else:
613
616
  await response.process_events()
614
- if not await response.is_success:
617
+ success = await response.is_success
618
+ if not success:
615
619
  success, err_msg = (
616
620
  False,
617
621
  format_error_message(
@@ -619,23 +623,23 @@ async def register_extrinsic(
619
623
  substrate=subtensor.substrate,
620
624
  ),
621
625
  )
622
-
623
- if not success:
624
- # Look error here
625
- # https://github.com/opentensor/subtensor/blob/development/pallets/subtensor/src/errors.rs
626
- if "HotKeyAlreadyRegisteredInSubNet" in err_msg:
627
- console.print(
628
- f":white_heavy_check_mark: [green]Already Registered on "
629
- f"[bold]subnet:{netuid}[/bold][/green]"
626
+ # Look error here
627
+ # https://github.com/opentensor/subtensor/blob/development/pallets/subtensor/src/errors.rs
628
+
629
+ if "HotKeyAlreadyRegisteredInSubNet" in err_msg:
630
+ console.print(
631
+ f":white_heavy_check_mark: [green]Already Registered on "
632
+ f"[bold]subnet:{netuid}[/bold][/green]"
633
+ )
634
+ return True
635
+ err_console.print(
636
+ f":cross_mark: [red]Failed[/red]: {err_msg}"
630
637
  )
631
- return True
632
-
633
- err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
634
- await asyncio.sleep(0.5)
638
+ await asyncio.sleep(0.5)
635
639
 
636
640
  # Successful registration, final check for neuron and pubkey
637
- else:
638
- console.print(":satellite: Checking Balance...")
641
+ if success:
642
+ console.print(":satellite: Checking Registration status...")
639
643
  is_registered = await is_hotkey_registered(
640
644
  subtensor,
641
645
  netuid=netuid,
@@ -722,10 +726,8 @@ async def run_faucet_extrinsic(
722
726
  return False, "Requires torch"
723
727
 
724
728
  # Unlock coldkey
725
- try:
726
- wallet.unlock_coldkey()
727
- except KeyFileError:
728
- return False, "There was an error unlocking your coldkey"
729
+ if not (unlock_status := unlock_key(wallet, print_out=False)).success:
730
+ return False, unlock_status.message
729
731
 
730
732
  # Get previous balance.
731
733
  old_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
@@ -914,6 +916,9 @@ async def _block_solver(
914
916
  stop_event.clear()
915
917
 
916
918
  solution_queue = Queue()
919
+ if cuda:
920
+ num_processes = len(dev_id)
921
+
917
922
  finished_queues = [Queue() for _ in range(num_processes)]
918
923
  check_block = Lock()
919
924
 
@@ -923,7 +928,6 @@ async def _block_solver(
923
928
 
924
929
  if cuda:
925
930
  ## Create a worker per CUDA device
926
- num_processes = len(dev_id)
927
931
  solvers = [
928
932
  _CUDASolver(
929
933
  i,
@@ -1231,8 +1235,14 @@ def _terminate_workers_and_wait_for_exit(
1231
1235
  if isinstance(worker, Queue_Type):
1232
1236
  worker.join_thread()
1233
1237
  else:
1234
- worker.join()
1235
- worker.close()
1238
+ try:
1239
+ worker.join(3.0)
1240
+ except subprocess.TimeoutExpired:
1241
+ worker.terminate()
1242
+ try:
1243
+ worker.close()
1244
+ except ValueError:
1245
+ worker.terminate()
1236
1246
 
1237
1247
 
1238
1248
  # TODO verify this works with async
@@ -1627,9 +1637,7 @@ async def swap_hotkey_extrinsic(
1627
1637
  )
1628
1638
  return False
1629
1639
 
1630
- try:
1631
- wallet.unlock_coldkey()
1632
- except KeyFileError:
1640
+ if not unlock_key(wallet).success:
1633
1641
  return False
1634
1642
 
1635
1643
  if prompt:
@@ -21,7 +21,6 @@ import time
21
21
  from typing import Union, List, TYPE_CHECKING
22
22
 
23
23
  from bittensor_wallet import Wallet, Keypair
24
- from bittensor_wallet.errors import KeyFileError
25
24
  import numpy as np
26
25
  from numpy.typing import NDArray
27
26
  from rich.prompt import Confirm
@@ -37,6 +36,7 @@ from bittensor_cli.src.bittensor.utils import (
37
36
  u16_normalized_float,
38
37
  print_verbose,
39
38
  format_error_message,
39
+ unlock_key,
40
40
  )
41
41
 
42
42
  if TYPE_CHECKING:
@@ -306,9 +306,7 @@ async def root_register_extrinsic(
306
306
  the response is `True`.
307
307
  """
308
308
 
309
- try:
310
- wallet.unlock_coldkey()
311
- except KeyFileError:
309
+ if not unlock_key(wallet).success:
312
310
  return False
313
311
 
314
312
  print_verbose(f"Checking if hotkey ({wallet.hotkey_str}) is registered on root")
@@ -426,9 +424,7 @@ async def set_root_weights_extrinsic(
426
424
  err_console.print("Your hotkey is not registered to the root network")
427
425
  return False
428
426
 
429
- try:
430
- wallet.unlock_coldkey()
431
- except KeyFileError:
427
+ if not unlock_key(wallet).success:
432
428
  return False
433
429
 
434
430
  # First convert types.
@@ -1,7 +1,6 @@
1
1
  import asyncio
2
2
 
3
3
  from bittensor_wallet import Wallet
4
- from bittensor_wallet.errors import KeyFileError
5
4
  from rich.prompt import Confirm
6
5
  from substrateinterface.exceptions import SubstrateRequestException
7
6
 
@@ -15,6 +14,8 @@ from bittensor_cli.src.bittensor.utils import (
15
14
  format_error_message,
16
15
  get_explorer_url_for_network,
17
16
  is_valid_bittensor_address_or_public_key,
17
+ print_error,
18
+ unlock_key,
18
19
  )
19
20
 
20
21
 
@@ -23,6 +24,7 @@ async def transfer_extrinsic(
23
24
  wallet: Wallet,
24
25
  destination: str,
25
26
  amount: Balance,
27
+ transfer_all: bool = False,
26
28
  wait_for_inclusion: bool = True,
27
29
  wait_for_finalization: bool = False,
28
30
  keep_alive: bool = True,
@@ -34,6 +36,7 @@ async def transfer_extrinsic(
34
36
  :param wallet: Bittensor wallet object to make transfer from.
35
37
  :param destination: Destination public key address (ss58_address or ed25519) of recipient.
36
38
  :param amount: Amount to stake as Bittensor balance.
39
+ :param transfer_all: Whether to transfer all funds from this wallet to the destination address.
37
40
  :param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`,
38
41
  or returns `False` if the extrinsic fails to enter the block within the timeout.
39
42
  :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning
@@ -98,7 +101,11 @@ async def transfer_extrinsic(
98
101
  block_hash_ = response.block_hash
99
102
  return True, block_hash_, ""
100
103
  else:
101
- return False, "", format_error_message(await response.error_message)
104
+ return (
105
+ False,
106
+ "",
107
+ format_error_message(await response.error_message, subtensor.substrate),
108
+ )
102
109
 
103
110
  # Validate destination address.
104
111
  if not is_valid_bittensor_address_or_public_key(destination):
@@ -108,9 +115,7 @@ async def transfer_extrinsic(
108
115
  return False
109
116
  console.print(f"[dark_orange]Initiating transfer on network: {subtensor.network}")
110
117
  # Unlock wallet coldkey.
111
- try:
112
- wallet.unlock_coldkey()
113
- except KeyFileError:
118
+ if not unlock_key(wallet).success:
114
119
  return False
115
120
 
116
121
  # Check balance.
@@ -135,6 +140,12 @@ async def transfer_extrinsic(
135
140
  existential_deposit = Balance(0)
136
141
 
137
142
  # Check if we have enough balance.
143
+ if transfer_all is True:
144
+ amount = account_balance - fee - existential_deposit
145
+ if amount < Balance(0):
146
+ print_error("Not enough balance to transfer")
147
+ return False
148
+
138
149
  if account_balance < (amount + fee + existential_deposit):
139
150
  err_console.print(
140
151
  ":cross_mark: [bold red]Not enough balance[/bold red]:\n\n"
@@ -183,7 +194,7 @@ async def transfer_extrinsic(
183
194
  )
184
195
  console.print(
185
196
  f"Balance:\n"
186
- f" [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance[wallet.coldkey.ss58_address]}[/green]"
197
+ f" [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance[wallet.coldkeypub.ss58_address]}[/green]"
187
198
  )
188
199
  return True
189
200
 
@@ -111,18 +111,18 @@ class SubtensorInterface:
111
111
  return f"Network: {self.network}, Chain: {self.chain_endpoint}"
112
112
 
113
113
  async def __aenter__(self):
114
- with console.status(
115
- f"[yellow]Connecting to Substrate:[/yellow][bold white] {self}..."
116
- ):
117
- try:
114
+ try:
115
+ with console.status(
116
+ f"[yellow]Connecting to Substrate:[/yellow][bold white] {self}..."
117
+ ):
118
118
  async with self.substrate:
119
119
  return self
120
- except TimeoutException:
121
- err_console.print(
122
- "\n[red]Error[/red]: Timeout occurred connecting to substrate. "
123
- f"Verify your chain and network settings: {self}"
124
- )
125
- raise typer.Exit(code=1)
120
+ except TimeoutException:
121
+ err_console.print(
122
+ "\n[red]Error[/red]: Timeout occurred connecting to substrate. "
123
+ f"Verify your chain and network settings: {self}"
124
+ )
125
+ raise typer.Exit(code=1)
126
126
 
127
127
  async def __aexit__(self, exc_type, exc_val, exc_tb):
128
128
  await self.substrate.close()
@@ -283,7 +283,7 @@ class SubtensorInterface:
283
283
  self,
284
284
  runtime_api: str,
285
285
  method: str,
286
- params: Optional[Union[list[list[int]], dict[str, int]]],
286
+ params: Optional[Union[list[list[int]], list[int], dict[str, int]]],
287
287
  block_hash: Optional[str] = None,
288
288
  reuse_block: Optional[bool] = False,
289
289
  ) -> Optional[str]:
@@ -1,4 +1,5 @@
1
1
  import ast
2
+ from collections import namedtuple
2
3
  import math
3
4
  import os
4
5
  import sqlite3
@@ -9,7 +10,7 @@ from urllib.parse import urlparse
9
10
 
10
11
  from bittensor_wallet import Wallet, Keypair
11
12
  from bittensor_wallet.utils import SS58_FORMAT
12
- from bittensor_wallet.errors import KeyFileError
13
+ from bittensor_wallet.errors import KeyFileError, PasswordError
13
14
  from bittensor_wallet import utils
14
15
  from jinja2 import Template
15
16
  from markupsafe import Markup
@@ -35,6 +36,8 @@ console = Console()
35
36
  err_console = Console(stderr=True)
36
37
  verbose_console = Console(quiet=True)
37
38
 
39
+ UnlockStatus = namedtuple("UnlockStatus", ["success", "message"])
40
+
38
41
 
39
42
  def print_console(message: str, colour: str, title: str, console: Console):
40
43
  console.print(
@@ -238,11 +241,14 @@ def get_hotkey_wallets_for_wallet(
238
241
  def get_coldkey_wallets_for_path(path: str) -> list[Wallet]:
239
242
  """Gets all wallets with coldkeys from a given path"""
240
243
  wallet_path = Path(path).expanduser()
241
- wallets = [
242
- Wallet(name=directory.name, path=path)
243
- for directory in wallet_path.iterdir()
244
- if directory.is_dir()
245
- ]
244
+ try:
245
+ wallets = [
246
+ Wallet(name=directory.name, path=path)
247
+ for directory in wallet_path.iterdir()
248
+ if directory.is_dir()
249
+ ]
250
+ except FileNotFoundError:
251
+ wallets = []
246
252
  return wallets
247
253
 
248
254
 
@@ -443,7 +449,7 @@ def get_explorer_url_for_network(
443
449
  explorer_opentensor_url = "{root_url}/query/{block_hash}".format(
444
450
  root_url=explorer_root_urls.get("opentensor"), block_hash=block_hash
445
451
  )
446
- explorer_taostats_url = "{root_url}/extrinsic/{block_hash}".format(
452
+ explorer_taostats_url = "{root_url}/hash/{block_hash}".format(
447
453
  root_url=explorer_root_urls.get("taostats"), block_hash=block_hash
448
454
  )
449
455
  explorer_urls["opentensor"] = explorer_opentensor_url
@@ -532,7 +538,7 @@ def format_error_message(
532
538
  err_docs = error_message.get("docs", [err_description])
533
539
  err_description = err_docs[0] if err_docs else err_description
534
540
 
535
- return f"Subtensor returned `{err_name}({err_type})` error. This means: `{err_description}`."
541
+ return f"Subtensor returned `{err_name}({err_type})` error. This means: '{err_description}'."
536
542
 
537
543
 
538
544
  def convert_blocks_to_time(blocks: int, block_time: int = 12) -> tuple[int, int, int]:
@@ -974,3 +980,39 @@ def retry_prompt(
974
980
  return var
975
981
  else:
976
982
  err_console.print(rejection_text)
983
+
984
+
985
+ def unlock_key(
986
+ wallet: Wallet, unlock_type="cold", print_out: bool = True
987
+ ) -> "UnlockStatus":
988
+ """
989
+ Attempts to decrypt a wallet's coldkey or hotkey
990
+ Args:
991
+ wallet: a Wallet object
992
+ unlock_type: the key type, 'cold' or 'hot'
993
+ print_out: whether to print out the error message to the err_console
994
+
995
+ Returns: UnlockStatus for success status of unlock, with error message if unsuccessful
996
+
997
+ """
998
+ if unlock_type == "cold":
999
+ unlocker = "unlock_coldkey"
1000
+ elif unlock_type == "hot":
1001
+ unlocker = "unlock_hotkey"
1002
+ else:
1003
+ raise ValueError(
1004
+ f"Invalid unlock type provided: {unlock_type}. Must be 'cold' or 'hot'."
1005
+ )
1006
+ try:
1007
+ getattr(wallet, unlocker)()
1008
+ return UnlockStatus(True, "")
1009
+ except PasswordError:
1010
+ err_msg = f"The password used to decrypt your {unlock_type.capitalize()}key Keyfile is invalid."
1011
+ if print_out:
1012
+ err_console.print(f":cross_mark: [red]{err_msg}[/red]")
1013
+ return UnlockStatus(False, err_msg)
1014
+ except KeyFileError:
1015
+ err_msg = f"{unlock_type.capitalize()}key Keyfile is corrupt, non-writable, or non-readable, or non-existent."
1016
+ if print_out:
1017
+ err_console.print(f":cross_mark: [red]{err_msg}[/red]")
1018
+ return UnlockStatus(False, err_msg)
@@ -3,7 +3,6 @@ import json
3
3
  from typing import Optional, TYPE_CHECKING
4
4
 
5
5
  from bittensor_wallet import Wallet
6
- from bittensor_wallet.errors import KeyFileError
7
6
  import numpy as np
8
7
  from numpy.typing import NDArray
9
8
  from rich import box
@@ -42,6 +41,7 @@ from bittensor_cli.src.bittensor.utils import (
42
41
  ss58_to_vec_u8,
43
42
  update_metadata_table,
44
43
  group_subnets,
44
+ unlock_key,
45
45
  )
46
46
 
47
47
  if TYPE_CHECKING:
@@ -280,9 +280,7 @@ async def burned_register_extrinsic(
280
280
  finalization/inclusion, the response is `True`.
281
281
  """
282
282
 
283
- try:
284
- wallet.unlock_coldkey()
285
- except KeyFileError:
283
+ if not unlock_key(wallet).success:
286
284
  return False
287
285
 
288
286
  with console.status(
@@ -536,9 +534,7 @@ async def delegate_extrinsic(
536
534
  delegate_string = "delegate" if delegate else "undelegate"
537
535
 
538
536
  # Decrypt key
539
- try:
540
- wallet.unlock_coldkey()
541
- except KeyFileError:
537
+ if not unlock_key(wallet).success:
542
538
  return False
543
539
 
544
540
  print_verbose("Checking if hotkey is a delegate")
@@ -1096,10 +1092,7 @@ async def senate_vote(
1096
1092
  return False
1097
1093
 
1098
1094
  # Unlock the wallet.
1099
- try:
1100
- wallet.unlock_hotkey()
1101
- wallet.unlock_coldkey()
1102
- except KeyFileError:
1095
+ if not unlock_key(wallet).success and unlock_key(wallet, "hot").success:
1103
1096
  return False
1104
1097
 
1105
1098
  console.print(f"Fetching proposals in [dark_orange]network: {subtensor.network}")
@@ -1319,10 +1312,7 @@ async def set_take(wallet: Wallet, subtensor: SubtensorInterface, take: float) -
1319
1312
 
1320
1313
  console.print(f"Setting take on [dark_orange]network: {subtensor.network}")
1321
1314
  # Unlock the wallet.
1322
- try:
1323
- wallet.unlock_hotkey()
1324
- wallet.unlock_coldkey()
1325
- except KeyFileError:
1315
+ if not unlock_key(wallet).success and unlock_key(wallet, "hot").success:
1326
1316
  return False
1327
1317
 
1328
1318
  result_ = await _do_set_take()
@@ -1720,10 +1710,7 @@ async def nominate(wallet: Wallet, subtensor: SubtensorInterface, prompt: bool):
1720
1710
 
1721
1711
  console.print(f"Nominating on [dark_orange]network: {subtensor.network}")
1722
1712
  # Unlock the wallet.
1723
- try:
1724
- wallet.unlock_hotkey()
1725
- wallet.unlock_coldkey()
1726
- except KeyFileError:
1713
+ if not unlock_key(wallet).success and unlock_key(wallet, "hot").success:
1727
1714
  return False
1728
1715
 
1729
1716
  print_verbose(f"Checking hotkey ({wallet.hotkey_str}) is a delegate")