bittensor-cli 9.9.0__py3-none-any.whl → 9.10.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.
@@ -10,6 +10,7 @@ import plotille
10
10
  import plotly.graph_objects as go
11
11
 
12
12
  from bittensor_cli.src import COLOR_PALETTE
13
+ from bittensor_cli.src.bittensor.chain_data import DynamicInfo
13
14
  from bittensor_cli.src.bittensor.utils import (
14
15
  console,
15
16
  err_console,
@@ -27,7 +28,8 @@ async def price(
27
28
  subtensor: "SubtensorInterface",
28
29
  netuids: list[int],
29
30
  all_netuids: bool = False,
30
- interval_hours: int = 24,
31
+ interval_hours: int = 4,
32
+ current_only: bool = False,
31
33
  html_output: bool = False,
32
34
  log_scale: bool = False,
33
35
  json_output: bool = False,
@@ -41,45 +43,96 @@ async def price(
41
43
  blocks_per_hour = int(3600 / 12) # ~300 blocks per hour
42
44
  total_blocks = blocks_per_hour * interval_hours
43
45
 
44
- with console.status(":chart_increasing: Fetching historical price data..."):
45
- current_block_hash = await subtensor.substrate.get_chain_head()
46
- current_block = await subtensor.substrate.get_block_number(current_block_hash)
46
+ if not current_only:
47
+ with console.status(":chart_increasing: Fetching historical price data..."):
48
+ current_block_hash = await subtensor.substrate.get_chain_head()
49
+ current_block = await subtensor.substrate.get_block_number(
50
+ current_block_hash
51
+ )
47
52
 
48
- step = 300
49
- start_block = max(0, current_block - total_blocks)
50
- block_numbers = list(range(start_block, current_block + 1, step))
53
+ step = 300
54
+ start_block = max(0, current_block - total_blocks)
55
+ block_numbers = list(range(start_block, current_block + 1, step))
51
56
 
52
- # Block hashes
53
- block_hash_cors = [
54
- subtensor.substrate.get_block_hash(bn) for bn in block_numbers
55
- ]
56
- block_hashes = await asyncio.gather(*block_hash_cors)
57
+ # Block hashes
58
+ block_hash_cors = [
59
+ subtensor.substrate.get_block_hash(bn) for bn in block_numbers
60
+ ]
61
+ block_hashes = await asyncio.gather(*block_hash_cors)
57
62
 
58
- # We fetch all subnets when there is more than one netuid
59
- if all_netuids or len(netuids) > 1:
60
- subnet_info_cors = [subtensor.all_subnets(bh) for bh in block_hashes]
61
- else:
62
- # If there is only one netuid, we fetch the subnet info for that netuid
63
- netuid = netuids[0]
64
- subnet_info_cors = [subtensor.subnet(netuid, bh) for bh in block_hashes]
65
- all_subnet_infos = await asyncio.gather(*subnet_info_cors)
63
+ # We fetch all subnets when there is more than one netuid
64
+ if all_netuids or len(netuids) > 1:
65
+ subnet_info_cors = [subtensor.all_subnets(bh) for bh in block_hashes]
66
+ else:
67
+ # If there is only one netuid, we fetch the subnet info for that netuid
68
+ netuid = netuids[0]
69
+ subnet_info_cors = [subtensor.subnet(netuid, bh) for bh in block_hashes]
70
+ all_subnet_infos = await asyncio.gather(*subnet_info_cors)
66
71
 
67
72
  subnet_data = _process_subnet_data(
68
73
  block_numbers, all_subnet_infos, netuids, all_netuids
69
74
  )
75
+ if not subnet_data:
76
+ err_console.print("[red]No valid price data found for any subnet[/red]")
77
+ return
70
78
 
71
- if not subnet_data:
72
- err_console.print("[red]No valid price data found for any subnet[/red]")
73
- return
74
-
75
- if html_output:
76
- await _generate_html_output(
77
- subnet_data, block_numbers, interval_hours, log_scale
79
+ if html_output:
80
+ await _generate_html_output(
81
+ subnet_data, block_numbers, interval_hours, log_scale
82
+ )
83
+ elif json_output:
84
+ json_console.print(json.dumps(_generate_json_output(subnet_data)))
85
+ else:
86
+ _generate_cli_output(subnet_data, block_numbers, interval_hours, log_scale)
87
+ else:
88
+ with console.status("Fetching current price data..."):
89
+ if all_netuids or len(netuids) > 1:
90
+ all_subnet_info = await subtensor.all_subnets()
91
+ else:
92
+ all_subnet_info = [await subtensor.subnet(netuid=netuids[0])]
93
+ subnet_data = _process_current_subnet_data(
94
+ all_subnet_info, netuids, all_netuids
78
95
  )
79
- elif json_output:
80
- json_console.print(json.dumps(_generate_json_output(subnet_data)))
96
+ if json_output:
97
+ json_console.print(json.dumps(_generate_json_output(subnet_data)))
98
+ else:
99
+ _generate_cli_output_current(subnet_data)
100
+
101
+
102
+ def _process_current_subnet_data(subnet_infos: list[DynamicInfo], netuids, all_netuids):
103
+ subnet_data = {}
104
+ if all_netuids or len(netuids) > 1:
105
+ # Most recent data for statistics
106
+ for subnet_info in subnet_infos:
107
+ stats = {
108
+ "current_price": subnet_info.price,
109
+ "supply": subnet_info.alpha_in.tao + subnet_info.alpha_out.tao,
110
+ "market_cap": subnet_info.price.tao
111
+ * (subnet_info.alpha_in.tao + subnet_info.alpha_out.tao),
112
+ "emission": subnet_info.emission.tao,
113
+ "stake": subnet_info.alpha_out.tao,
114
+ "symbol": subnet_info.symbol,
115
+ "name": get_subnet_name(subnet_info),
116
+ }
117
+ subnet_data[subnet_info.netuid] = {
118
+ "stats": stats,
119
+ }
81
120
  else:
82
- _generate_cli_output(subnet_data, block_numbers, interval_hours, log_scale)
121
+ subnet_info = subnet_infos[0]
122
+ stats = {
123
+ "current_price": subnet_info.price.tao,
124
+ "supply": subnet_info.alpha_in.tao + subnet_info.alpha_out.tao,
125
+ "market_cap": subnet_info.price.tao
126
+ * (subnet_info.alpha_in.tao + subnet_info.alpha_out.tao),
127
+ "emission": subnet_info.emission.tao,
128
+ "stake": subnet_info.alpha_out.tao,
129
+ "symbol": subnet_info.symbol,
130
+ "name": get_subnet_name(subnet_info),
131
+ }
132
+ subnet_data[subnet_info.netuid] = {
133
+ "stats": stats,
134
+ }
135
+ return subnet_data
83
136
 
84
137
 
85
138
  def _process_subnet_data(block_numbers, all_subnet_infos, netuids, all_netuids):
@@ -626,3 +679,46 @@ def _generate_cli_output(subnet_data, block_numbers, interval_hours, log_scale):
626
679
  )
627
680
 
628
681
  console.print(stats_text)
682
+
683
+
684
+ def _generate_cli_output_current(subnet_data):
685
+ for netuid, data in subnet_data.items():
686
+ stats = data["stats"]
687
+
688
+ if netuid != 0:
689
+ console.print(
690
+ f"\n[{COLOR_PALETTE.G.SYM}]Subnet {netuid} - {stats['symbol']} "
691
+ f"[cyan]{stats['name']}[/cyan][/{COLOR_PALETTE.G.SYM}]\n"
692
+ f"Current: [blue]{stats['current_price']:.6f}{stats['symbol']}[/blue]\n"
693
+ )
694
+ else:
695
+ console.print(
696
+ f"\n[{COLOR_PALETTE.G.SYM}]Subnet {netuid} - {stats['symbol']} "
697
+ f"[cyan]{stats['name']}[/cyan][/{COLOR_PALETTE.G.SYM}]\n"
698
+ f"Current: [blue]{stats['symbol']} {stats['current_price']:.6f}[/blue]\n"
699
+ )
700
+
701
+ if netuid != 0:
702
+ stats_text = (
703
+ "\nLatest stats:\n"
704
+ f"Supply: [{COLOR_PALETTE.P.ALPHA_IN}]"
705
+ f"{stats['supply']:,.2f} {stats['symbol']}[/{COLOR_PALETTE.P.ALPHA_IN}]\n"
706
+ f"Market Cap: [steel_blue3]{stats['market_cap']:,.2f} {stats['symbol']} / 21M[/steel_blue3]\n"
707
+ f"Emission: [{COLOR_PALETTE.P.EMISSION}]"
708
+ f"{stats['emission']:,.2f} {stats['symbol']}[/{COLOR_PALETTE.P.EMISSION}]\n"
709
+ f"Stake: [{COLOR_PALETTE.S.TAO}]"
710
+ f"{stats['stake']:,.2f} {stats['symbol']}[/{COLOR_PALETTE.S.TAO}]"
711
+ )
712
+ else:
713
+ stats_text = (
714
+ "\nLatest stats:\n"
715
+ f"Supply: [{COLOR_PALETTE.P.ALPHA_IN}]"
716
+ f"{stats['symbol']} {stats['supply']:,.2f}[/{COLOR_PALETTE.P.ALPHA_IN}]\n"
717
+ f"Market Cap: [steel_blue3]{stats['symbol']} {stats['market_cap']:,.2f} / 21M[/steel_blue3]\n"
718
+ f"Emission: [{COLOR_PALETTE.P.EMISSION}]"
719
+ f"{stats['symbol']} {stats['emission']:,.2f}[/{COLOR_PALETTE.P.EMISSION}]\n"
720
+ f"Stake: [{COLOR_PALETTE.S.TAO}]"
721
+ f"{stats['symbol']} {stats['stake']:,.2f}[/{COLOR_PALETTE.S.TAO}]"
722
+ )
723
+
724
+ console.print(stats_text)
@@ -36,6 +36,7 @@ from bittensor_cli.src.bittensor.utils import (
36
36
  unlock_key,
37
37
  blocks_to_duration,
38
38
  json_console,
39
+ get_hotkey_pub_ss58,
39
40
  )
40
41
 
41
42
  if TYPE_CHECKING:
@@ -114,7 +115,7 @@ async def register_subnetwork_extrinsic(
114
115
  return False, None
115
116
 
116
117
  call_params = {
117
- "hotkey": wallet.hotkey.ss58_address,
118
+ "hotkey": get_hotkey_pub_ss58(wallet),
118
119
  "mechid": 1,
119
120
  }
120
121
  call_function = "register_network"
@@ -1654,7 +1655,7 @@ async def register(
1654
1655
  str(netuid),
1655
1656
  f"{Balance.get_unit(netuid)}",
1656
1657
  f"τ {current_recycle.tao:.4f}",
1657
- f"{wallet.hotkey.ss58_address}",
1658
+ f"{get_hotkey_pub_ss58(wallet)}",
1658
1659
  f"{wallet.coldkeypub.ss58_address}",
1659
1660
  )
1660
1661
  console.print(table)
@@ -26,6 +26,7 @@ from bittensor_cli.src.bittensor.utils import (
26
26
  json_console,
27
27
  string_to_u16,
28
28
  string_to_u64,
29
+ get_hotkey_pub_ss58,
29
30
  )
30
31
 
31
32
  if TYPE_CHECKING:
@@ -176,7 +177,7 @@ async def set_hyperparameter_extrinsic(
176
177
  wait_for_inclusion: bool = False,
177
178
  wait_for_finalization: bool = True,
178
179
  prompt: bool = True,
179
- ) -> bool:
180
+ ) -> tuple[bool, str]:
180
181
  """Sets a hyperparameter for a specific subnetwork.
181
182
 
182
183
  :param subtensor: initialized SubtensorInterface object
@@ -199,13 +200,14 @@ async def set_hyperparameter_extrinsic(
199
200
  params=[netuid],
200
201
  )
201
202
  if subnet_owner != wallet.coldkeypub.ss58_address:
202
- err_console.print(
203
+ err_msg = (
203
204
  ":cross_mark: [red]This wallet doesn't own the specified subnet.[/red]"
204
205
  )
205
- return False
206
+ err_console.print(err_msg)
207
+ return False, err_msg
206
208
 
207
- if not unlock_key(wallet).success:
208
- return False
209
+ if not (ulw := unlock_key(wallet)).success:
210
+ return False, ulw.message
209
211
 
210
212
  arbitrary_extrinsic = False
211
213
 
@@ -217,15 +219,14 @@ async def set_hyperparameter_extrinsic(
217
219
  )
218
220
  extrinsic = parameter
219
221
  if not arbitrary_extrinsic:
220
- err_console.print(
221
- ":cross_mark: [red]Invalid hyperparameter specified.[/red]"
222
- )
223
- return False
222
+ err_msg = ":cross_mark: [red]Invalid hyperparameter specified.[/red]"
223
+ err_console.print(err_msg)
224
+ return False, err_msg
224
225
  if sudo_ and prompt:
225
226
  if not Confirm.ask(
226
227
  "This hyperparam is only settable by root sudo users. If you are not, this will fail. Please confirm"
227
228
  ):
228
- return False
229
+ return False, "This hyperparam is only settable by root sudo users"
229
230
 
230
231
  substrate = subtensor.substrate
231
232
  msg_value = value if not arbitrary_extrinsic else call_params
@@ -253,10 +254,11 @@ async def set_hyperparameter_extrinsic(
253
254
  ]
254
255
 
255
256
  if len(value) < len(non_netuid_fields):
256
- err_console.print(
257
+ err_msg = (
257
258
  "Not enough values provided in the list for all parameters"
258
259
  )
259
- return False
260
+ err_console.print(err_msg)
261
+ return False, err_msg
260
262
 
261
263
  call_params.update(
262
264
  {name: val for name, val in zip(non_netuid_fields, value)}
@@ -289,20 +291,20 @@ async def set_hyperparameter_extrinsic(
289
291
  )
290
292
  if not success:
291
293
  err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
292
- return False
294
+ return False, err_msg
293
295
  elif arbitrary_extrinsic:
294
296
  console.print(
295
297
  f":white_heavy_check_mark: "
296
298
  f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]"
297
299
  )
298
- return True
300
+ return True, ""
299
301
  # Successful registration, final check for membership
300
302
  else:
301
303
  console.print(
302
304
  f":white_heavy_check_mark: "
303
305
  f"[dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]"
304
306
  )
305
- return True
307
+ return True, ""
306
308
 
307
309
 
308
310
  async def _get_senate_members(
@@ -497,7 +499,7 @@ async def vote_senate_extrinsic(
497
499
  call_module="SubtensorModule",
498
500
  call_function="vote",
499
501
  call_params={
500
- "hotkey": wallet.hotkey.ss58_address,
502
+ "hotkey": get_hotkey_pub_ss58(wallet),
501
503
  "proposal": proposal_hash,
502
504
  "index": proposal_idx,
503
505
  "approve": vote,
@@ -513,9 +515,10 @@ async def vote_senate_extrinsic(
513
515
  # Successful vote, final check for data
514
516
  else:
515
517
  if vote_data := await subtensor.get_vote_data(proposal_hash):
518
+ hotkey_ss58 = get_hotkey_pub_ss58(wallet)
516
519
  if (
517
- vote_data.ayes.count(wallet.hotkey.ss58_address) > 0
518
- or vote_data.nays.count(wallet.hotkey.ss58_address) > 0
520
+ vote_data.ayes.count(hotkey_ss58) > 0
521
+ or vote_data.nays.count(hotkey_ss58) > 0
519
522
  ):
520
523
  console.print(":white_heavy_check_mark: [green]Vote cast.[/green]")
521
524
  return True
@@ -617,25 +620,26 @@ async def sudo_set_hyperparameter(
617
620
  param_value: Optional[str],
618
621
  prompt: bool,
619
622
  json_output: bool,
620
- ):
623
+ ) -> tuple[bool, str]:
621
624
  """Set subnet hyperparameters."""
622
625
  is_allowed_value, value = allowed_value(param_name, param_value)
623
626
  if not is_allowed_value:
624
- err_console.print(
627
+ err_msg = (
625
628
  f"Hyperparameter [dark_orange]{param_name}[/dark_orange] value is not within bounds. "
626
629
  f"Value is {param_value} but must be {value}"
627
630
  )
628
- return False
629
- success = await set_hyperparameter_extrinsic(
631
+ err_console.print(err_msg)
632
+ return False, err_msg
633
+ success, err_msg = await set_hyperparameter_extrinsic(
630
634
  subtensor, wallet, netuid, param_name, value, prompt=prompt
631
635
  )
632
636
  if json_output:
633
- return success
637
+ return success, err_msg
634
638
  if success:
635
639
  console.print("\n")
636
640
  print_verbose("Fetching hyperparameters")
637
641
  await get_hyperparameters(subtensor, netuid=netuid)
638
- return success
642
+ return success, err_msg
639
643
 
640
644
 
641
645
  async def get_hyperparameters(
@@ -859,10 +863,9 @@ async def senate_vote(
859
863
  return False
860
864
 
861
865
  print_verbose(f"Fetching senate status of {wallet.hotkey_str}")
862
- if not await _is_senate_member(subtensor, hotkey_ss58=wallet.hotkey.ss58_address):
863
- err_console.print(
864
- f"Aborting: Hotkey {wallet.hotkey.ss58_address} isn't a senate member."
865
- )
866
+ hotkey_ss58 = get_hotkey_pub_ss58(wallet)
867
+ if not await _is_senate_member(subtensor, hotkey_ss58=hotkey_ss58):
868
+ err_console.print(f"Aborting: Hotkey {hotkey_ss58} isn't a senate member.")
866
869
  return False
867
870
 
868
871
  # Unlock the wallet.
@@ -890,7 +893,7 @@ async def senate_vote(
890
893
 
891
894
 
892
895
  async def get_current_take(subtensor: "SubtensorInterface", wallet: Wallet):
893
- current_take = await subtensor.current_take(wallet.hotkey.ss58_address)
896
+ current_take = await subtensor.current_take(get_hotkey_pub_ss58(wallet))
894
897
  return current_take
895
898
 
896
899
 
@@ -912,12 +915,13 @@ async def set_take(
912
915
  return False
913
916
 
914
917
  block_hash = await subtensor.substrate.get_chain_head()
918
+ hotkey_ss58 = get_hotkey_pub_ss58(wallet)
915
919
  netuids_registered = await subtensor.get_netuids_for_hotkey(
916
- wallet.hotkey.ss58_address, block_hash=block_hash
920
+ hotkey_ss58, block_hash=block_hash
917
921
  )
918
922
  if not len(netuids_registered) > 0:
919
923
  err_console.print(
920
- f"Hotkey [{COLOR_PALETTE.G.HK}]{wallet.hotkey.ss58_address}[/{COLOR_PALETTE.G.HK}] is not registered to"
924
+ f"Hotkey [{COLOR_PALETTE.G.HK}]{hotkey_ss58}[/{COLOR_PALETTE.G.HK}] is not registered to"
921
925
  f" any subnet. Please register using [{COLOR_PALETTE.G.SUBHEAD}]`btcli subnets register`"
922
926
  f"[{COLOR_PALETTE.G.SUBHEAD}] and try again."
923
927
  )
@@ -926,7 +930,7 @@ async def set_take(
926
930
  result: bool = await set_take_extrinsic(
927
931
  subtensor=subtensor,
928
932
  wallet=wallet,
929
- delegate_ss58=wallet.hotkey.ss58_address,
933
+ delegate_ss58=hotkey_ss58,
930
934
  take=take,
931
935
  )
932
936
 
@@ -3,7 +3,7 @@ import itertools
3
3
  import json
4
4
  import os
5
5
  from collections import defaultdict
6
- from typing import Generator, Optional
6
+ from typing import Generator, Optional, Union
7
7
 
8
8
  import aiohttp
9
9
  from bittensor_wallet import Wallet, Keypair
@@ -48,6 +48,7 @@ from bittensor_cli.src.bittensor.utils import (
48
48
  WalletLike,
49
49
  blocks_to_duration,
50
50
  decode_account_id,
51
+ get_hotkey_pub_ss58,
51
52
  )
52
53
 
53
54
 
@@ -159,7 +160,7 @@ async def regen_coldkey(
159
160
  "name": new_wallet.name,
160
161
  "path": new_wallet.path,
161
162
  "hotkey": new_wallet.hotkey_str,
162
- "hotkey_ss58": new_wallet.hotkey.ss58_address,
163
+ "hotkey_ss58": get_hotkey_pub_ss58(new_wallet),
163
164
  "coldkey_ss58": new_wallet.coldkeypub.ss58_address,
164
165
  },
165
166
  "error": "",
@@ -209,7 +210,7 @@ async def regen_coldkey_pub(
209
210
  "name": new_coldkeypub.name,
210
211
  "path": new_coldkeypub.path,
211
212
  "hotkey": new_coldkeypub.hotkey_str,
212
- "hotkey_ss58": new_coldkeypub.hotkey.ss58_address,
213
+ "hotkey_ss58": get_hotkey_pub_ss58(new_coldkeypub),
213
214
  "coldkey_ss58": new_coldkeypub.coldkeypub.ss58_address,
214
215
  },
215
216
  "error": "",
@@ -255,7 +256,7 @@ async def regen_hotkey(
255
256
  console.print(
256
257
  "\n✅ [dark_sea_green]Regenerated hotkey successfully!\n",
257
258
  f"[dark_sea_green]Wallet name: ({new_hotkey_.name}), path: ({new_hotkey_.path}), "
258
- f"hotkey ss58: ({new_hotkey_.hotkey.ss58_address})",
259
+ f"hotkey ss58: ({new_hotkey_.hotkeypub.ss58_address})",
259
260
  )
260
261
  if json_output:
261
262
  json_console.print(
@@ -266,7 +267,7 @@ async def regen_hotkey(
266
267
  "name": new_hotkey_.name,
267
268
  "path": new_hotkey_.path,
268
269
  "hotkey": new_hotkey_.hotkey_str,
269
- "hotkey_ss58": new_hotkey_.hotkey.ss58_address,
270
+ "hotkey_ss58": new_hotkey_.hotkeypub.ss58_address,
270
271
  "coldkey_ss58": new_hotkey_.coldkeypub.ss58_address,
271
272
  },
272
273
  "error": "",
@@ -287,6 +288,50 @@ async def regen_hotkey(
287
288
  )
288
289
 
289
290
 
291
+ async def regen_hotkey_pub(
292
+ wallet: Wallet,
293
+ ss58_address: str,
294
+ public_key_hex: str,
295
+ overwrite: Optional[bool] = False,
296
+ json_output: bool = False,
297
+ ):
298
+ """Creates a new hotkeypub under this wallet."""
299
+ try:
300
+ new_hotkeypub = wallet.regenerate_hotkeypub(
301
+ ss58_address=ss58_address,
302
+ public_key=public_key_hex,
303
+ overwrite=overwrite,
304
+ )
305
+ if isinstance(new_hotkeypub, Wallet):
306
+ console.print(
307
+ "\n✅ [dark_sea_green]Regenerated coldkeypub successfully!\n",
308
+ f"[dark_sea_green]Wallet name: ({new_hotkeypub.name}), path: ({new_hotkeypub.path}), "
309
+ f"coldkey ss58: ({new_hotkeypub.coldkeypub.ss58_address})",
310
+ )
311
+ if json_output:
312
+ json_console.print(
313
+ json.dumps(
314
+ {
315
+ "success": True,
316
+ "data": {
317
+ "name": new_hotkeypub.name,
318
+ "path": new_hotkeypub.path,
319
+ "hotkey": new_hotkeypub.hotkey_str,
320
+ "hotkey_ss58": new_hotkeypub.hotkeypub.ss58_address,
321
+ "coldkey_ss58": new_hotkeypub.coldkeypub.ss58_address,
322
+ },
323
+ "error": "",
324
+ }
325
+ )
326
+ )
327
+ except KeyFileError:
328
+ print_error("KeyFileError: File is not writable")
329
+ if json_output:
330
+ json_console.print(
331
+ '{"success": false, "error": "Keyfile is not writable", "data": null}'
332
+ )
333
+
334
+
290
335
  async def new_hotkey(
291
336
  wallet: Wallet,
292
337
  n_words: int,
@@ -323,7 +368,7 @@ async def new_hotkey(
323
368
  "name": wallet.name,
324
369
  "path": wallet.path,
325
370
  "hotkey": wallet.hotkey_str,
326
- "hotkey_ss58": wallet.hotkey.ss58_address,
371
+ "hotkey_ss58": get_hotkey_pub_ss58(wallet),
327
372
  "coldkey_ss58": wallet.coldkeypub.ss58_address,
328
373
  },
329
374
  "error": "",
@@ -402,19 +447,26 @@ async def wallet_create(
402
447
  json_output: bool = False,
403
448
  ):
404
449
  """Creates a new wallet."""
405
- output_dict = {"success": False, "error": "", "data": None}
450
+ output_dict: dict[str, Optional[Union[bool, str, dict]]] = {
451
+ "success": False,
452
+ "error": "",
453
+ "data": None,
454
+ }
455
+
406
456
  if uri:
407
457
  try:
408
458
  keypair = Keypair.create_from_uri(uri)
409
- wallet.set_coldkey(keypair=keypair, encrypt=False, overwrite=False)
410
- wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=False)
411
- wallet.set_hotkey(keypair=keypair, encrypt=False, overwrite=False)
459
+ wallet.set_coldkey(keypair=keypair, encrypt=False, overwrite=overwrite)
460
+ wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=overwrite)
461
+ wallet.set_hotkey(keypair=keypair, encrypt=False, overwrite=overwrite)
462
+ wallet.set_hotkeypub(keypair=keypair, encrypt=False, overwrite=overwrite)
463
+ wallet.set_coldkeypub(keypair=keypair, encrypt=False, overwrite=overwrite)
412
464
  output_dict["success"] = True
413
465
  output_dict["data"] = {
414
466
  "name": wallet.name,
415
467
  "path": wallet.path,
416
468
  "hotkey": wallet.hotkey_str,
417
- "hotkey_ss58": wallet.hotkey.ss58_address,
469
+ "hotkey_ss58": wallet.hotkeypub.ss58_address,
418
470
  "coldkey_ss58": wallet.coldkeypub.ss58_address,
419
471
  }
420
472
  except Exception as e:
@@ -455,7 +507,7 @@ async def wallet_create(
455
507
  "name": wallet.name,
456
508
  "path": wallet.path,
457
509
  "hotkey": wallet.hotkey_str,
458
- "hotkey_ss58": wallet.hotkey.ss58_address,
510
+ "hotkey_ss58": wallet.hotkeypub.ss58_address,
459
511
  }
460
512
  except KeyFileError as error:
461
513
  err = str(error)
@@ -794,13 +846,14 @@ async def wallet_list(wallet_path: str, json_output: bool):
794
846
  data = f"[bold red]Hotkey[/bold red][green] {hkey}[/green] (?)"
795
847
  hk_data = {"name": hkey.name, "ss58_address": "?"}
796
848
  if hkey:
849
+ hkey_ss58 = get_hotkey_pub_ss58(hkey)
797
850
  try:
798
851
  data = (
799
852
  f"[bold red]Hotkey[/bold red] [green]{hkey.hotkey_str}[/green] "
800
- f"ss58_address [green]{hkey.hotkey.ss58_address}[/green]\n"
853
+ f"ss58_address [green]{hkey_ss58}[/green]\n"
801
854
  )
802
855
  hk_data["name"] = hkey.hotkey_str
803
- hk_data["ss58_address"] = hkey.hotkey.ss58_address
856
+ hk_data["ss58_address"] = hkey_ss58
804
857
  except UnicodeDecodeError:
805
858
  pass
806
859
  wallet_tree.add(data)
@@ -1253,7 +1306,7 @@ def _get_hotkeys(
1253
1306
 
1254
1307
  def is_hotkey_matched(wallet: Wallet, item: str) -> bool:
1255
1308
  if is_valid_ss58_address(item):
1256
- return wallet.hotkey.ss58_address == item
1309
+ return get_hotkey_pub_ss58(wallet) == item
1257
1310
  else:
1258
1311
  return wallet.hotkey_str == item
1259
1312
 
@@ -1285,9 +1338,10 @@ def _get_key_address(all_hotkeys: list[Wallet]) -> tuple[list[str], dict[str, Wa
1285
1338
  hotkey_coldkey_to_hotkey_wallet = {}
1286
1339
  for hotkey_wallet in all_hotkeys:
1287
1340
  if hotkey_wallet.coldkeypub:
1288
- if hotkey_wallet.hotkey.ss58_address not in hotkey_coldkey_to_hotkey_wallet:
1289
- hotkey_coldkey_to_hotkey_wallet[hotkey_wallet.hotkey.ss58_address] = {}
1290
- hotkey_coldkey_to_hotkey_wallet[hotkey_wallet.hotkey.ss58_address][
1341
+ hotkey_ss58 = get_hotkey_pub_ss58(hotkey_wallet)
1342
+ if hotkey_ss58 not in hotkey_coldkey_to_hotkey_wallet:
1343
+ hotkey_coldkey_to_hotkey_wallet[hotkey_ss58] = {}
1344
+ hotkey_coldkey_to_hotkey_wallet[hotkey_ss58][
1291
1345
  hotkey_wallet.coldkeypub.ss58_address
1292
1346
  ] = hotkey_wallet
1293
1347
  else:
@@ -1471,7 +1525,7 @@ async def inspect(
1471
1525
  if hotkey_names := [
1472
1526
  w.hotkey_str
1473
1527
  for w in hotkeys
1474
- if w.hotkey.ss58_address == n.hotkey
1528
+ if get_hotkey_pub_ss58(w) == n.hotkey
1475
1529
  ]:
1476
1530
  hotkey_name = f"{hotkey_names[0]}-"
1477
1531
  yield [""] * 5 + [
@@ -15,6 +15,7 @@ from bittensor_cli.src.bittensor.utils import (
15
15
  console,
16
16
  format_error_message,
17
17
  json_console,
18
+ get_hotkey_pub_ss58,
18
19
  )
19
20
  from bittensor_cli.src.bittensor.extrinsics.root import (
20
21
  convert_weights_and_uids_for_emit,
@@ -128,7 +129,7 @@ class SetWeightsExtrinsic:
128
129
 
129
130
  # Generate the hash of the weights
130
131
  commit_hash = generate_weight_hash(
131
- address=self.wallet.hotkey.ss58_address,
132
+ address=get_hotkey_pub_ss58(self.wallet),
132
133
  netuid=self.netuid,
133
134
  uids=uids,
134
135
  values=weights,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bittensor-cli
3
- Version: 9.9.0
3
+ Version: 9.10.1
4
4
  Summary: Bittensor CLI
5
5
  Author: bittensor.com
6
6
  Project-URL: homepage, https://github.com/opentensor/btcli
@@ -11,7 +11,6 @@ Requires-Dist: wheel
11
11
  Requires-Dist: async-substrate-interface>=1.4.2
12
12
  Requires-Dist: aiohttp~=3.10.2
13
13
  Requires-Dist: backoff~=2.2.1
14
- Requires-Dist: click<8.2.0
15
14
  Requires-Dist: GitPython>=3.0.0
16
15
  Requires-Dist: netaddr~=1.3.0
17
16
  Requires-Dist: numpy<3.0.0,>=2.0.1
@@ -20,8 +19,9 @@ Requires-Dist: pycryptodome<4.0.0,>=3.0.0
20
19
  Requires-Dist: PyYAML~=6.0.1
21
20
  Requires-Dist: rich<15.0,>=13.7
22
21
  Requires-Dist: scalecodec==1.2.11
23
- Requires-Dist: typer<0.16,>=0.12
24
- Requires-Dist: bittensor-wallet>=3.0.7
22
+ Requires-Dist: typer>=0.16
23
+ Requires-Dist: bittensor-wallet>=4.0.0
24
+ Requires-Dist: packaging
25
25
  Requires-Dist: plotille>=5.0.0
26
26
  Requires-Dist: plotly>=6.0.0
27
27
  Provides-Extra: cuda
@@ -71,7 +71,15 @@ Installation steps are described below. For a full documentation on how to use `
71
71
 
72
72
  ## Install on macOS and Linux
73
73
 
74
- You can install `btcli` on your local machine directly from source, PyPI, or Homebrew. **Make sure you verify your installation after you install**:
74
+ You can install `btcli` on your local machine directly from source, PyPI, or Homebrew.
75
+ **Make sure you verify your installation after you install**.
76
+
77
+ ### For macOS users
78
+ Note that the macOS preinstalled CPython installation is compiled with LibreSSL instead of OpenSSL. There are a number
79
+ of issues with LibreSSL, and as such is not fully supported by the libraries used by btcli. Thus we highly recommend, if
80
+ you are using a Mac, to first install Python from [Homebrew](https://brew.sh/). Additionally, the Rust FFI bindings
81
+ [if installing from precompiled wheels (default)] require the Homebrew-installed OpenSSL pacakge. If you choose to use
82
+ the preinstalled Python version from macOS, things may not work completely.
75
83
 
76
84
 
77
85
  ### Install from [PyPI](https://pypi.org/project/bittensor/)