bittensor-cli 9.0.0rc4__py3-none-any.whl → 9.0.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.
@@ -35,12 +35,13 @@ from bittensor_cli.src.bittensor.utils import (
35
35
  update_metadata_table,
36
36
  prompt_for_identity,
37
37
  get_subnet_name,
38
+ unlock_key,
38
39
  )
39
40
 
40
41
  if TYPE_CHECKING:
41
42
  from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
42
43
 
43
- TAO_WEIGHT = 0.018
44
+ TAO_WEIGHT = 0.18
44
45
 
45
46
  # helpers and extrinsics
46
47
 
@@ -183,7 +184,7 @@ async def register_subnetwork_extrinsic(
183
184
  await response.process_events()
184
185
  if not await response.is_success:
185
186
  err_console.print(
186
- f":cross_mark: [red]Failed[/red]: {format_error_message(await response.error_message, substrate)}"
187
+ f":cross_mark: [red]Failed[/red]: {format_error_message(await response.error_message)}"
187
188
  )
188
189
  await asyncio.sleep(0.5)
189
190
  return False
@@ -889,7 +890,7 @@ async def show(
889
890
  for netuid_ in range(len(all_subnets)):
890
891
  subnet = all_subnets[netuid_]
891
892
  emission_on_subnet = (
892
- root_state.emission_history[netuid_][idx] / subnet.tempo
893
+ root_state.emission_history[netuid_][idx] / (subnet.tempo or 1)
893
894
  )
894
895
  total_emission_per_block += subnet.alpha_to_tao(
895
896
  Balance.from_rao(emission_on_subnet)
@@ -1322,7 +1323,7 @@ async def show(
1322
1323
  return hotkey
1323
1324
  else:
1324
1325
  console.print(
1325
- f"[red]Invalid UID. Please enter a valid UID from the table above[/red]"
1326
+ "[red]Invalid UID. Please enter a valid UID from the table above[/red]"
1326
1327
  )
1327
1328
  except ValueError:
1328
1329
  console.print("[red]Please enter a valid number[/red]")
@@ -2026,3 +2027,146 @@ async def metagraph_cmd(
2026
2027
  table.add_row(*row)
2027
2028
 
2028
2029
  console.print(table)
2030
+
2031
+
2032
+ def create_identity_table(title: str = None):
2033
+ if not title:
2034
+ title = "Subnet Identity"
2035
+
2036
+ table = Table(
2037
+ Column(
2038
+ "Item",
2039
+ justify="right",
2040
+ style=COLOR_PALETTE["GENERAL"]["SUBHEADING_MAIN"],
2041
+ no_wrap=True,
2042
+ ),
2043
+ Column("Value", style=COLOR_PALETTE["GENERAL"]["SUBHEADING"]),
2044
+ title=f"\n[{COLOR_PALETTE['GENERAL']['HEADER']}]{title}\n",
2045
+ show_footer=True,
2046
+ show_edge=False,
2047
+ header_style="bold white",
2048
+ border_style="bright_black",
2049
+ style="bold",
2050
+ title_justify="center",
2051
+ show_lines=False,
2052
+ pad_edge=True,
2053
+ )
2054
+ return table
2055
+
2056
+
2057
+ async def set_identity(
2058
+ wallet: "Wallet",
2059
+ subtensor: "SubtensorInterface",
2060
+ netuid: int,
2061
+ subnet_identity: dict,
2062
+ prompt: bool = False,
2063
+ ) -> bool:
2064
+ """Set identity information for a subnet"""
2065
+
2066
+ if not await subtensor.subnet_exists(netuid):
2067
+ err_console.print(f"Subnet {netuid} does not exist")
2068
+ return False
2069
+
2070
+ identity_data = {
2071
+ "netuid": netuid,
2072
+ "subnet_name": subnet_identity.get("subnet_name", ""),
2073
+ "github_repo": subnet_identity.get("github_repo", ""),
2074
+ "subnet_contact": subnet_identity.get("subnet_contact", ""),
2075
+ "subnet_url": subnet_identity.get("subnet_url", ""),
2076
+ "discord": subnet_identity.get("discord", ""),
2077
+ "description": subnet_identity.get("description", ""),
2078
+ "additional": subnet_identity.get("additional", ""),
2079
+ }
2080
+
2081
+ if not unlock_key(wallet).success:
2082
+ return False
2083
+
2084
+ if prompt:
2085
+ if not Confirm.ask(
2086
+ "Are you sure you want to set subnet's identity? This is subject to a fee."
2087
+ ):
2088
+ return False
2089
+
2090
+ call = await subtensor.substrate.compose_call(
2091
+ call_module="SubtensorModule",
2092
+ call_function="set_subnet_identity",
2093
+ call_params=identity_data,
2094
+ )
2095
+
2096
+ with console.status(
2097
+ " :satellite: [dark_sea_green3]Setting subnet identity on-chain...",
2098
+ spinner="earth",
2099
+ ):
2100
+ success, err_msg = await subtensor.sign_and_send_extrinsic(call, wallet)
2101
+
2102
+ if not success:
2103
+ err_console.print(f"[red]:cross_mark: Failed![/red] {err_msg}")
2104
+ return False
2105
+
2106
+ console.print(
2107
+ ":white_heavy_check_mark: [dark_sea_green3]Successfully set subnet identity\n"
2108
+ )
2109
+
2110
+ subnet = await subtensor.subnet(netuid)
2111
+ identity = subnet.subnet_identity if subnet else None
2112
+
2113
+ if identity:
2114
+ table = create_identity_table(title=f"New Subnet {netuid} Identity")
2115
+ table.add_row("Netuid", str(netuid))
2116
+ for key in [
2117
+ "subnet_name",
2118
+ "github_repo",
2119
+ "subnet_contact",
2120
+ "subnet_url",
2121
+ "discord",
2122
+ "description",
2123
+ "additional",
2124
+ ]:
2125
+ value = getattr(identity, key, None)
2126
+ table.add_row(key, str(value) if value else "~")
2127
+ console.print(table)
2128
+
2129
+ return True
2130
+
2131
+
2132
+ async def get_identity(subtensor: "SubtensorInterface", netuid: int, title: str = None):
2133
+ """Fetch and display existing subnet identity information."""
2134
+ if not title:
2135
+ title = "Subnet Identity"
2136
+
2137
+ if not await subtensor.subnet_exists(netuid):
2138
+ print_error(
2139
+ f"Subnet {netuid} does not exist."
2140
+ )
2141
+ raise typer.Exit()
2142
+
2143
+ with console.status(
2144
+ ":satellite: [bold green]Querying subnet identity...", spinner="earth"
2145
+ ):
2146
+ subnet = await subtensor.subnet(netuid)
2147
+ identity = subnet.subnet_identity if subnet else None
2148
+
2149
+ if not identity:
2150
+ err_console.print(
2151
+ f"Existing subnet identity not found"
2152
+ f" for subnet [blue]{netuid}[/blue]"
2153
+ f" on {subtensor}"
2154
+ )
2155
+ return {}
2156
+
2157
+ if identity:
2158
+ table = create_identity_table(title=f"Current Subnet {netuid} Identity")
2159
+ table.add_row("Netuid", str(netuid))
2160
+ for key in [
2161
+ "subnet_name",
2162
+ "github_repo",
2163
+ "subnet_contact",
2164
+ "subnet_url",
2165
+ "discord",
2166
+ "description",
2167
+ "additional",
2168
+ ]:
2169
+ value = getattr(identity, key, None)
2170
+ table.add_row(key, str(value) if value else "~")
2171
+ console.print(table)
2172
+ return identity
@@ -3,7 +3,6 @@ from typing import TYPE_CHECKING, Union, Optional
3
3
 
4
4
  import typer
5
5
  from bittensor_wallet import Wallet
6
- from bittensor_wallet.errors import KeyFileError
7
6
  from rich import box
8
7
  from rich.table import Column, Table
9
8
  from rich.prompt import Confirm
@@ -17,6 +16,8 @@ from bittensor_cli.src.bittensor.utils import (
17
16
  print_error,
18
17
  print_verbose,
19
18
  normalize_hyperparameters,
19
+ unlock_key,
20
+ blocks_to_duration,
20
21
  )
21
22
 
22
23
  if TYPE_CHECKING:
@@ -106,13 +107,10 @@ async def set_hyperparameter_extrinsic(
106
107
  )
107
108
  return False
108
109
 
109
- try:
110
- wallet.unlock_coldkey()
111
- except KeyFileError:
112
- err_console.print("Error decrypting coldkey (possibly incorrect password)")
110
+ if not unlock_key(wallet).success:
113
111
  return False
114
112
 
115
- extrinsic = HYPERPARAMS.get(parameter)
113
+ extrinsic, sudo_ = HYPERPARAMS.get(parameter, ("", False))
116
114
  if extrinsic is None:
117
115
  err_console.print(":cross_mark: [red]Invalid hyperparameter specified.[/red]")
118
116
  return False
@@ -152,11 +150,17 @@ async def set_hyperparameter_extrinsic(
152
150
  call_params[str(value_argument["name"])] = value
153
151
 
154
152
  # create extrinsic call
155
- call = await substrate.compose_call(
153
+ call_ = await substrate.compose_call(
156
154
  call_module="AdminUtils",
157
155
  call_function=extrinsic,
158
156
  call_params=call_params,
159
157
  )
158
+ if sudo_:
159
+ call = await substrate.compose_call(
160
+ call_module="Sudo", call_function="sudo", call_params={"call": call_}
161
+ )
162
+ else:
163
+ call = call_
160
164
  success, err_msg = await subtensor.sign_and_send_extrinsic(
161
165
  call, wallet, wait_for_inclusion, wait_for_finalization
162
166
  )
@@ -266,14 +270,22 @@ def format_call_data(call_data: dict) -> str:
266
270
  call_info = call_details[0]
267
271
  call_function, call_args = next(iter(call_info.items()))
268
272
 
269
- # Extract the argument, handling tuple values
270
- formatted_args = ", ".join(
271
- str(arg[0]) if isinstance(arg, tuple) else str(arg)
272
- for arg in call_args.values()
273
- )
273
+ # Format arguments, handle nested/large payloads
274
+ formatted_args = []
275
+ for arg_name, arg_value in call_args.items():
276
+ if isinstance(arg_value, (tuple, list, dict)):
277
+ # For large nested, show abbreviated version
278
+ content_str = str(arg_value)
279
+ if len(content_str) > 20:
280
+ formatted_args.append(f"{arg_name}: ... [{len(content_str)}] ...")
281
+ else:
282
+ formatted_args.append(f"{arg_name}: {arg_value}")
283
+ else:
284
+ formatted_args.append(f"{arg_name}: {arg_value}")
274
285
 
275
286
  # Format the final output string
276
- return f"{call_function}({formatted_args})"
287
+ args_str = ", ".join(formatted_args)
288
+ return f"{module}.{call_function}({args_str})"
277
289
 
278
290
 
279
291
  def _validate_proposal_hash(proposal_hash: str) -> bool:
@@ -464,19 +476,20 @@ async def sudo_set_hyperparameter(
464
476
 
465
477
  normalized_value: Union[str, bool]
466
478
  if param_name in [
467
- "network_registration_allowed",
479
+ "registration_allowed",
468
480
  "network_pow_registration_allowed",
469
481
  "commit_reveal_weights_enabled",
470
482
  "liquid_alpha_enabled",
471
483
  ]:
472
- normalized_value = param_value.lower() in ["true", "1"]
484
+ normalized_value = param_value.lower() in ["true", "True", "1"]
473
485
  else:
474
486
  normalized_value = param_value
475
487
 
476
488
  is_allowed_value, value = allowed_value(param_name, normalized_value)
477
489
  if not is_allowed_value:
478
490
  err_console.print(
479
- f"Hyperparameter {param_name} value is not within bounds. Value is {normalized_value} but must be {value}"
491
+ f"Hyperparameter [dark_orange]{param_name}[/dark_orange] value is not within bounds. "
492
+ f"Value is {normalized_value} but must be {value}"
480
493
  )
481
494
  return
482
495
 
@@ -572,24 +585,30 @@ async def get_senate(subtensor: "SubtensorInterface"):
572
585
  return console.print(table)
573
586
 
574
587
 
575
- async def proposals(subtensor: "SubtensorInterface"):
588
+ async def proposals(subtensor: "SubtensorInterface", verbose: bool):
576
589
  console.print(
577
590
  ":satellite: Syncing with chain: [white]{}[/white] ...".format(
578
591
  subtensor.network
579
592
  )
580
593
  )
581
- print_verbose("Fetching senate members & proposals")
582
594
  block_hash = await subtensor.substrate.get_chain_head()
583
- senate_members, all_proposals = await asyncio.gather(
595
+ senate_members, all_proposals, current_block = await asyncio.gather(
584
596
  _get_senate_members(subtensor, block_hash),
585
597
  _get_proposals(subtensor, block_hash),
598
+ subtensor.substrate.get_block_number(block_hash),
586
599
  )
587
600
 
588
- print_verbose("Fetching member information from Chain")
589
601
  registered_delegate_info: dict[
590
602
  str, DelegatesDetails
591
603
  ] = await subtensor.get_delegate_identities()
592
604
 
605
+ title = (
606
+ f"[bold #4196D6]Bittensor Governance Proposals[/bold #4196D6]\n"
607
+ f"[steel_blue3]Current Block:[/steel_blue3] {current_block}\t"
608
+ f"[steel_blue3]Network:[/steel_blue3] {subtensor.network}\n\n"
609
+ f"[steel_blue3]Active Proposals:[/steel_blue3] {len(all_proposals)}\t"
610
+ f"[steel_blue3]Senate Size:[/steel_blue3] {len(senate_members)}\n"
611
+ )
593
612
  table = Table(
594
613
  Column(
595
614
  "[white]HASH",
@@ -604,8 +623,8 @@ async def proposals(subtensor: "SubtensorInterface"):
604
623
  style="rgb(50,163,219)",
605
624
  ),
606
625
  Column("[white]END", style="bright_cyan"),
607
- Column("[white]CALLDATA", style="dark_sea_green"),
608
- title=f"\n[dark_orange]Proposals\t\t\nActive Proposals: {len(all_proposals)}\t\tSenate Size: {len(senate_members)}\nNetwork: {subtensor.network}",
626
+ Column("[white]CALLDATA", style="dark_sea_green", width=30),
627
+ title=title,
609
628
  show_footer=True,
610
629
  box=box.SIMPLE_HEAVY,
611
630
  pad_edge=False,
@@ -613,16 +632,36 @@ async def proposals(subtensor: "SubtensorInterface"):
613
632
  border_style="bright_black",
614
633
  )
615
634
  for hash_, (call_data, vote_data) in all_proposals.items():
635
+ blocks_remaining = vote_data.end - current_block
636
+ if blocks_remaining > 0:
637
+ duration_str = blocks_to_duration(blocks_remaining)
638
+ vote_end_cell = f"{vote_data.end} [dim](in {duration_str})[/dim]"
639
+ else:
640
+ vote_end_cell = f"{vote_data.end} [red](expired)[/red]"
641
+
642
+ ayes_threshold = (
643
+ (len(vote_data.ayes) / vote_data.threshold * 100)
644
+ if vote_data.threshold > 0
645
+ else 0
646
+ )
647
+ nays_threshold = (
648
+ (len(vote_data.nays) / vote_data.threshold * 100)
649
+ if vote_data.threshold > 0
650
+ else 0
651
+ )
616
652
  table.add_row(
617
- hash_,
653
+ hash_ if verbose else f"{hash_[:4]}...{hash_[-4:]}",
618
654
  str(vote_data.threshold),
619
- str(len(vote_data.ayes)),
620
- str(len(vote_data.nays)),
655
+ f"{len(vote_data.ayes)} ({ayes_threshold:.2f}%)",
656
+ f"{len(vote_data.nays)} ({nays_threshold:.2f}%)",
621
657
  display_votes(vote_data, registered_delegate_info),
622
- str(vote_data.end),
658
+ vote_end_cell,
623
659
  format_call_data(call_data),
624
660
  )
625
- return console.print(table)
661
+ console.print(table)
662
+ console.print(
663
+ "\n[dim]* Both Ayes and Nays percentages are calculated relative to the proposal's threshold.[/dim]"
664
+ )
626
665
 
627
666
 
628
667
  async def senate_vote(
@@ -653,10 +692,7 @@ async def senate_vote(
653
692
  return False
654
693
 
655
694
  # Unlock the wallet.
656
- try:
657
- wallet.unlock_hotkey()
658
- wallet.unlock_coldkey()
659
- except KeyFileError:
695
+ if not unlock_key(wallet, "hot").success and unlock_key(wallet, "cold").success:
660
696
  return False
661
697
 
662
698
  console.print(f"Fetching proposals in [dark_orange]network: {subtensor.network}")
@@ -732,10 +768,7 @@ async def set_take(
732
768
  f"Setting take on [{COLOR_PALETTE['GENERAL']['LINKS']}]network: {subtensor.network}"
733
769
  )
734
770
 
735
- try:
736
- wallet.unlock_hotkey()
737
- wallet.unlock_coldkey()
738
- except KeyFileError:
771
+ if not unlock_key(wallet, "hot").success and unlock_key(wallet, "cold").success:
739
772
  return False
740
773
 
741
774
  result_ = await _do_set_take()
@@ -6,7 +6,7 @@ from typing import Generator, Optional
6
6
 
7
7
  import aiohttp
8
8
  from bittensor_wallet import Wallet, Keypair
9
- from bittensor_wallet.errors import KeyFileError, PasswordError
9
+ from bittensor_wallet.errors import KeyFileError
10
10
  from bittensor_wallet.keyfile import Keyfile
11
11
  from fuzzywuzzy import fuzz
12
12
  from rich import box
@@ -43,16 +43,11 @@ from bittensor_cli.src.bittensor.utils import (
43
43
  validate_coldkey_presence,
44
44
  get_subnet_name,
45
45
  millify_tao,
46
+ unlock_key,
47
+ WalletLike,
46
48
  )
47
49
 
48
50
 
49
- class WalletLike:
50
- def __init__(self, name=None, hotkey_ss58=None, hotkey_str=None):
51
- self.name = name
52
- self.hotkey_ss58 = hotkey_ss58
53
- self.hotkey_str = hotkey_str
54
-
55
-
56
51
  async def regen_coldkey(
57
52
  wallet: Wallet,
58
53
  mnemonic: Optional[str],
@@ -60,6 +55,7 @@ async def regen_coldkey(
60
55
  json_path: Optional[str] = None,
61
56
  json_password: Optional[str] = "",
62
57
  use_password: Optional[bool] = True,
58
+ overwrite: Optional[bool] = False,
63
59
  ):
64
60
  """Creates a new coldkey under this wallet"""
65
61
  json_str: Optional[str] = None
@@ -69,13 +65,21 @@ async def regen_coldkey(
69
65
  with open(json_path, "r") as f:
70
66
  json_str = f.read()
71
67
  try:
72
- wallet.regenerate_coldkey(
68
+ new_wallet = wallet.regenerate_coldkey(
73
69
  mnemonic=mnemonic,
74
70
  seed=seed,
75
71
  json=(json_str, json_password) if all([json_str, json_password]) else None,
76
72
  use_password=use_password,
77
- overwrite=False,
73
+ overwrite=overwrite,
78
74
  )
75
+
76
+ if isinstance(new_wallet, Wallet):
77
+ console.print(
78
+ "\n✅ [dark_sea_green]Regenerated coldkey successfully!\n",
79
+ f"[dark_sea_green]Wallet name: ({new_wallet.name}), path: ({new_wallet.path}), coldkey ss58: ({new_wallet.coldkeypub.ss58_address})",
80
+ )
81
+ except ValueError:
82
+ print_error("Mnemonic phrase is invalid")
79
83
  except KeyFileError:
80
84
  print_error("KeyFileError: File is not writable")
81
85
 
@@ -84,14 +88,20 @@ async def regen_coldkey_pub(
84
88
  wallet: Wallet,
85
89
  ss58_address: str,
86
90
  public_key_hex: str,
91
+ overwrite: Optional[bool] = False,
87
92
  ):
88
93
  """Creates a new coldkeypub under this wallet."""
89
94
  try:
90
- wallet.regenerate_coldkeypub(
95
+ new_coldkeypub = wallet.regenerate_coldkeypub(
91
96
  ss58_address=ss58_address,
92
97
  public_key=public_key_hex,
93
- overwrite=False,
98
+ overwrite=overwrite,
94
99
  )
100
+ if isinstance(new_coldkeypub, Wallet):
101
+ console.print(
102
+ "\n✅ [dark_sea_green]Regenerated coldkeypub successfully!\n",
103
+ f"[dark_sea_green]Wallet name: ({new_coldkeypub.name}), path: ({new_coldkeypub.path}), coldkey ss58: ({new_coldkeypub.coldkeypub.ss58_address})",
104
+ )
95
105
  except KeyFileError:
96
106
  print_error("KeyFileError: File is not writable")
97
107
 
@@ -103,6 +113,7 @@ async def regen_hotkey(
103
113
  json_path: Optional[str],
104
114
  json_password: Optional[str] = "",
105
115
  use_password: Optional[bool] = False,
116
+ overwrite: Optional[bool] = False,
106
117
  ):
107
118
  """Creates a new hotkey under this wallet."""
108
119
  json_str: Optional[str] = None
@@ -114,13 +125,20 @@ async def regen_hotkey(
114
125
  json_str = f.read()
115
126
 
116
127
  try:
117
- wallet.regenerate_hotkey(
128
+ new_hotkey = wallet.regenerate_hotkey(
118
129
  mnemonic=mnemonic,
119
130
  seed=seed,
120
131
  json=(json_str, json_password) if all([json_str, json_password]) else None,
121
132
  use_password=use_password,
122
- overwrite=False,
133
+ overwrite=overwrite,
123
134
  )
135
+ if isinstance(new_hotkey, Wallet):
136
+ console.print(
137
+ "\n✅ [dark_sea_green]Regenerated hotkey successfully!\n",
138
+ f"[dark_sea_green]Wallet name: ({new_hotkey.name}), path: ({new_hotkey.path}), hotkey ss58: ({new_hotkey.hotkey.ss58_address})",
139
+ )
140
+ except ValueError:
141
+ print_error("Mnemonic phrase is invalid")
124
142
  except KeyFileError:
125
143
  print_error("KeyFileError: File is not writable")
126
144
 
@@ -130,6 +148,7 @@ async def new_hotkey(
130
148
  n_words: int,
131
149
  use_password: bool,
132
150
  uri: Optional[str] = None,
151
+ overwrite: Optional[bool] = False,
133
152
  ):
134
153
  """Creates a new hotkey under this wallet."""
135
154
  try:
@@ -146,7 +165,7 @@ async def new_hotkey(
146
165
  wallet.create_new_hotkey(
147
166
  n_words=n_words,
148
167
  use_password=use_password,
149
- overwrite=False,
168
+ overwrite=overwrite,
150
169
  )
151
170
  console.print("[dark_sea_green]Hotkey created[/dark_sea_green]")
152
171
  except KeyFileError:
@@ -158,6 +177,7 @@ async def new_coldkey(
158
177
  n_words: int,
159
178
  use_password: bool,
160
179
  uri: Optional[str] = None,
180
+ overwrite: Optional[bool] = False,
161
181
  ):
162
182
  """Creates a new coldkey under this wallet."""
163
183
  try:
@@ -175,7 +195,7 @@ async def new_coldkey(
175
195
  wallet.create_new_coldkey(
176
196
  n_words=n_words,
177
197
  use_password=use_password,
178
- overwrite=False,
198
+ overwrite=overwrite,
179
199
  )
180
200
  console.print("[dark_sea_green]Coldkey created[/dark_sea_green]")
181
201
  except KeyFileError:
@@ -187,6 +207,7 @@ async def wallet_create(
187
207
  n_words: int = 12,
188
208
  use_password: bool = True,
189
209
  uri: Optional[str] = None,
210
+ overwrite: Optional[bool] = False,
190
211
  ):
191
212
  """Creates a new wallet."""
192
213
  if uri:
@@ -205,7 +226,7 @@ async def wallet_create(
205
226
  wallet.create_new_coldkey(
206
227
  n_words=n_words,
207
228
  use_password=use_password,
208
- overwrite=False,
229
+ overwrite=overwrite,
209
230
  )
210
231
  console.print("[dark_sea_green]Coldkey created[/dark_sea_green]")
211
232
  except KeyFileError:
@@ -215,7 +236,7 @@ async def wallet_create(
215
236
  wallet.create_new_hotkey(
216
237
  n_words=n_words,
217
238
  use_password=False,
218
- overwrite=False,
239
+ overwrite=overwrite,
219
240
  )
220
241
  console.print("[dark_sea_green]Hotkey created[/dark_sea_green]")
221
242
  except KeyFileError:
@@ -487,7 +508,9 @@ async def wallet_list(wallet_path: str):
487
508
  wallet_tree = root.add(
488
509
  f"[bold blue]Coldkey[/bold blue] [green]{wallet.name}[/green] ss58_address [green]{coldkeypub_str}[/green]"
489
510
  )
490
- hotkeys = utils.get_hotkey_wallets_for_wallet(wallet, show_nulls=True)
511
+ hotkeys = utils.get_hotkey_wallets_for_wallet(
512
+ wallet, show_nulls=True, show_encrypted=True
513
+ )
491
514
  for hkey in hotkeys:
492
515
  data = f"[bold red]Hotkey[/bold red][green] {hkey}[/green] (?)"
493
516
  if hkey:
@@ -1047,11 +1070,17 @@ async def transfer(
1047
1070
  subtensor: SubtensorInterface,
1048
1071
  destination: str,
1049
1072
  amount: float,
1073
+ transfer_all: bool,
1050
1074
  prompt: bool,
1051
1075
  ):
1052
1076
  """Transfer token of amount to destination."""
1053
1077
  await transfer_extrinsic(
1054
- subtensor, wallet, destination, Balance.from_tao(amount), prompt=prompt
1078
+ subtensor=subtensor,
1079
+ wallet=wallet,
1080
+ destination=destination,
1081
+ amount=Balance.from_tao(amount),
1082
+ transfer_all=transfer_all,
1083
+ prompt=prompt,
1055
1084
  )
1056
1085
 
1057
1086
 
@@ -1211,13 +1240,14 @@ async def faucet(
1211
1240
  output_in_place: bool,
1212
1241
  log_verbose: bool,
1213
1242
  max_successes: int = 3,
1243
+ prompt: bool = True,
1214
1244
  ):
1215
1245
  # TODO: - work out prompts to be passed through the cli
1216
1246
  success = await run_faucet_extrinsic(
1217
1247
  subtensor,
1218
1248
  wallet,
1219
1249
  tpb=threads_per_block,
1220
- prompt=False,
1250
+ prompt=prompt,
1221
1251
  update_interval=update_interval,
1222
1252
  num_processes=processes,
1223
1253
  cuda=use_cuda,
@@ -1303,10 +1333,7 @@ async def set_id(
1303
1333
  )
1304
1334
  return False
1305
1335
 
1306
- try:
1307
- wallet.unlock_coldkey()
1308
- except KeyFileError:
1309
- err_console.print("Error decrypting coldkey (possibly incorrect password)")
1336
+ if not unlock_key(wallet).success:
1310
1337
  return False
1311
1338
 
1312
1339
  call = await subtensor.substrate.compose_call(
@@ -1398,30 +1425,15 @@ async def check_coldkey_swap(wallet: Wallet, subtensor: SubtensorInterface):
1398
1425
  async def sign(wallet: Wallet, message: str, use_hotkey: str):
1399
1426
  """Sign a message using the provided wallet or hotkey."""
1400
1427
 
1401
- def _unlock(key: str):
1402
- try:
1403
- getattr(wallet, f"unlock_{key}")()
1404
- return True
1405
- except PasswordError:
1406
- err_console.print(
1407
- ":cross_mark: [red]The password used to decrypt your keyfile is invalid[/red]"
1408
- )
1409
- return False
1410
- except KeyFileError:
1411
- err_console.print(
1412
- ":cross_mark: [red]Keyfile is corrupt, non-writable, or non-readable[/red]:"
1413
- )
1414
- return False
1415
-
1416
1428
  if not use_hotkey:
1417
- if not _unlock("coldkey"):
1429
+ if not unlock_key(wallet, "cold").success:
1418
1430
  return False
1419
1431
  keypair = wallet.coldkey
1420
1432
  print_verbose(
1421
1433
  f"Signing using [{COLOR_PALETTE['GENERAL']['COLDKEY']}]coldkey: {wallet.name}"
1422
1434
  )
1423
1435
  else:
1424
- if not _unlock("hotkey"):
1436
+ if not unlock_key(wallet, "hot").success:
1425
1437
  return False
1426
1438
  keypair = wallet.hotkey
1427
1439
  print_verbose(