bittensor-cli 8.1.0__tar.gz → 8.2.0__tar.gz

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.
Files changed (36) hide show
  1. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/PKG-INFO +1 -1
  2. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/__init__.py +1 -1
  3. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/cli.py +174 -60
  4. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/async_substrate_interface.py +3 -3
  5. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/extrinsics/registration.py +6 -2
  6. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/extrinsics/root.py +8 -6
  7. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/extrinsics/transfer.py +6 -3
  8. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/subtensor_interface.py +5 -3
  9. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/utils.py +80 -13
  10. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/commands/root.py +17 -8
  11. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/commands/stake/children_hotkeys.py +9 -3
  12. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/commands/stake/stake.py +1 -2
  13. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/commands/subnets.py +1 -1
  14. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/commands/wallets.py +12 -3
  15. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/commands/weights.py +44 -43
  16. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli.egg-info/PKG-INFO +1 -1
  17. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli.egg-info/requires.txt +1 -1
  18. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/README.md +0 -0
  19. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/doc_generation_helper.py +0 -0
  20. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/__init__.py +0 -0
  21. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/__init__.py +0 -0
  22. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/balances.py +0 -0
  23. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/chain_data.py +0 -0
  24. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/extrinsics/__init__.py +0 -0
  25. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/minigraph.py +0 -0
  26. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/networking.py +0 -0
  27. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/bittensor/templates/table.j2 +0 -0
  28. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/commands/__init__.py +0 -0
  29. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/commands/stake/__init__.py +0 -0
  30. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli/src/commands/sudo.py +0 -0
  31. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli.egg-info/SOURCES.txt +0 -0
  32. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli.egg-info/dependency_links.txt +0 -0
  33. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli.egg-info/entry_points.txt +0 -0
  34. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/bittensor_cli.egg-info/top_level.txt +0 -0
  35. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/setup.cfg +0 -0
  36. {bittensor-cli-8.1.0 → bittensor-cli-8.2.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bittensor-cli
3
- Version: 8.1.0
3
+ Version: 8.2.0
4
4
  Summary: Bittensor CLI
5
5
  Home-page: https://github.com/opentensor/btcli
6
6
  Author: bittensor.com
@@ -18,6 +18,6 @@
18
18
  from .cli import CLIManager
19
19
 
20
20
 
21
- __version__ = "8.1.0"
21
+ __version__ = "8.2.0"
22
22
 
23
23
  __all__ = ["CLIManager", "__version__"]
@@ -15,7 +15,6 @@ import rich
15
15
  import typer
16
16
  import numpy as np
17
17
  from bittensor_wallet import Wallet
18
- from git import Repo, GitError
19
18
  from rich import box
20
19
  from rich.prompt import Confirm, FloatPrompt, Prompt, IntPrompt
21
20
  from rich.table import Column, Table
@@ -27,6 +26,7 @@ from bittensor_cli.src import (
27
26
  Constants,
28
27
  )
29
28
  from bittensor_cli.src.bittensor import utils
29
+ from bittensor_cli.src.bittensor.balances import Balance
30
30
  from bittensor_cli.src.bittensor.async_substrate_interface import (
31
31
  SubstrateRequestException,
32
32
  )
@@ -49,7 +49,16 @@ from textwrap import dedent
49
49
  from websockets import ConnectionClosed
50
50
  from yaml import safe_dump, safe_load
51
51
 
52
- __version__ = "8.1.0"
52
+ try:
53
+ from git import Repo, GitError
54
+ except ImportError:
55
+
56
+ class GitError(Exception):
57
+ pass
58
+
59
+
60
+ __version__ = "8.2.0"
61
+
53
62
 
54
63
  _core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0)
55
64
  _version_split = _core_version.split(".")
@@ -121,7 +130,9 @@ class Options:
121
130
  flag_value=False,
122
131
  )
123
132
  public_hex_key = typer.Option(None, help="The public key in hex format.")
124
- ss58_address = typer.Option(None, help="The SS58 address of the coldkey.")
133
+ ss58_address = typer.Option(
134
+ None, "--ss58", "--ss58-address", help="The SS58 address of the coldkey."
135
+ )
125
136
  overwrite_coldkey = typer.Option(
126
137
  False,
127
138
  help="Overwrite the old coldkey with the newly generated coldkey.",
@@ -275,8 +286,34 @@ def get_n_words(n_words: Optional[int]) -> int:
275
286
  return n_words
276
287
 
277
288
 
289
+ def parse_mnemonic(mnemonic: str) -> str:
290
+ if "-" in mnemonic:
291
+ items = sorted(
292
+ [tuple(item.split("-")) for item in mnemonic.split(" ")],
293
+ key=lambda x: int(x[0]),
294
+ )
295
+ if int(items[0][0]) != 1:
296
+ err_console.print("Numbered mnemonics must begin with 1")
297
+ raise typer.Exit()
298
+ if [int(x[0]) for x in items] != list(
299
+ range(int(items[0][0]), int(items[-1][0]) + 1)
300
+ ):
301
+ err_console.print(
302
+ "Missing or duplicate numbers in a numbered mnemonic. "
303
+ "Double-check your numbered mnemonics and try again."
304
+ )
305
+ raise typer.Exit()
306
+ response = " ".join(item[1] for item in items)
307
+ else:
308
+ response = mnemonic
309
+ return response
310
+
311
+
278
312
  def get_creation_data(
279
- mnemonic: str, seed: str, json: str, json_password: str
313
+ mnemonic: Optional[str],
314
+ seed: Optional[str],
315
+ json: Optional[str],
316
+ json_password: Optional[str],
280
317
  ) -> tuple[str, str, str, str]:
281
318
  """
282
319
  Determines which of the key creation elements have been supplied, if any. If None have been supplied,
@@ -289,9 +326,11 @@ def get_creation_data(
289
326
  if prompt_answer.startswith("0x"):
290
327
  seed = prompt_answer
291
328
  elif len(prompt_answer.split(" ")) > 1:
292
- mnemonic = prompt_answer
329
+ mnemonic = parse_mnemonic(prompt_answer)
293
330
  else:
294
331
  json = prompt_answer
332
+ elif mnemonic:
333
+ mnemonic = parse_mnemonic(mnemonic)
295
334
  if json and not json_password:
296
335
  json_password = Prompt.ask(
297
336
  "Enter the backup password for JSON file.", password=True
@@ -368,7 +407,7 @@ def version_callback(value: bool):
368
407
  f"{repo.active_branch.name}/"
369
408
  f"{repo.commit()}"
370
409
  )
371
- except GitError:
410
+ except (NameError, GitError):
372
411
  version = f"BTCLI version: {__version__}"
373
412
  typer.echo(version)
374
413
  raise typer.Exit()
@@ -393,6 +432,7 @@ class CLIManager:
393
432
  root_app: typer.Typer
394
433
  subnets_app: typer.Typer
395
434
  weights_app: typer.Typer
435
+ utils_app = typer.Typer(epilog=_epilog)
396
436
 
397
437
  def __init__(self):
398
438
  self.config = {
@@ -517,6 +557,9 @@ class CLIManager:
517
557
  self.weights_app, name="weight", hidden=True, no_args_is_help=True
518
558
  )
519
559
 
560
+ # utils app
561
+ self.app.add_typer(self.utils_app, name="utils", no_args_is_help=True)
562
+
520
563
  # config commands
521
564
  self.config_app.command("set")(self.set_config)
522
565
  self.config_app.command("get")(self.get_config)
@@ -739,7 +782,7 @@ class CLIManager:
739
782
 
740
783
  def initialize_chain(
741
784
  self,
742
- network: Optional[str] = None,
785
+ network: Optional[list[str]] = None,
743
786
  ) -> SubtensorInterface:
744
787
  """
745
788
  Intelligently initializes a connection to the chain, depending on the supplied (or in config) values. Sets the
@@ -750,7 +793,20 @@ class CLIManager:
750
793
  """
751
794
  if not self.subtensor:
752
795
  if network:
753
- self.subtensor = SubtensorInterface(network)
796
+ for item in network:
797
+ if item.startswith("ws"):
798
+ network_ = item
799
+ break
800
+ else:
801
+ network_ = item
802
+
803
+ not_selected_networks = [net for net in network if net != network_]
804
+ if not_selected_networks:
805
+ console.print(
806
+ f"Networks not selected: [dark_orange]{', '.join(not_selected_networks)}[/dark_orange]"
807
+ )
808
+
809
+ self.subtensor = SubtensorInterface(network_)
754
810
  elif self.config["network"]:
755
811
  self.subtensor = SubtensorInterface(self.config["network"])
756
812
  console.print(
@@ -1227,7 +1283,7 @@ class CLIManager:
1227
1283
  "If left empty, all hotkeys, except those in the '--include-hotkeys', will be excluded.",
1228
1284
  ),
1229
1285
  netuids: str = Options.netuids,
1230
- network: str = Options.network,
1286
+ network: Optional[list[str]] = Options.network,
1231
1287
  quiet: bool = Options.quiet,
1232
1288
  verbose: bool = Options.verbose,
1233
1289
  ):
@@ -1354,7 +1410,7 @@ class CLIManager:
1354
1410
  wallet_name: str = Options.wallet_name,
1355
1411
  wallet_path: str = Options.wallet_path,
1356
1412
  wallet_hotkey: str = Options.wallet_hotkey,
1357
- network: str = Options.network,
1413
+ network: Optional[list[str]] = Options.network,
1358
1414
  prompt: bool = Options.prompt,
1359
1415
  quiet: bool = Options.quiet,
1360
1416
  verbose: bool = Options.verbose,
@@ -1401,7 +1457,7 @@ class CLIManager:
1401
1457
  wallet_name: Optional[str] = Options.wallet_name,
1402
1458
  wallet_path: Optional[str] = Options.wallet_path,
1403
1459
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
1404
- network: Optional[str] = Options.network,
1460
+ network: Optional[list[str]] = Options.network,
1405
1461
  destination_hotkey_name: Optional[str] = typer.Argument(
1406
1462
  None, help="Destination hotkey name."
1407
1463
  ),
@@ -1463,7 +1519,7 @@ class CLIManager:
1463
1519
  wallet_name: str = Options.wallet_name,
1464
1520
  wallet_path: str = Options.wallet_path,
1465
1521
  wallet_hotkey: str = Options.wallet_hotkey,
1466
- network: str = Options.network,
1522
+ network: Optional[list[str]] = Options.network,
1467
1523
  netuids: str = Options.netuids,
1468
1524
  quiet: bool = Options.quiet,
1469
1525
  verbose: bool = Options.verbose,
@@ -1529,7 +1585,7 @@ class CLIManager:
1529
1585
  wallet_name: Optional[str] = Options.wallet_name,
1530
1586
  wallet_path: Optional[str] = Options.wallet_path,
1531
1587
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
1532
- network: Optional[str] = Options.network,
1588
+ network: Optional[list[str]] = Options.network,
1533
1589
  # TODO add the following to config
1534
1590
  processors: Optional[int] = typer.Option(
1535
1591
  defaults.pow_register.num_processes,
@@ -1897,7 +1953,7 @@ class CLIManager:
1897
1953
  wallet_name: Optional[str] = Options.wallet_name,
1898
1954
  wallet_path: Optional[str] = Options.wallet_path,
1899
1955
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
1900
- network: Optional[str] = Options.network,
1956
+ network: Optional[list[str]] = Options.network,
1901
1957
  quiet: bool = Options.quiet,
1902
1958
  verbose: bool = Options.verbose,
1903
1959
  ):
@@ -1977,19 +2033,22 @@ class CLIManager:
1977
2033
  wallet_name: Optional[str] = Options.wallet_name,
1978
2034
  wallet_path: Optional[str] = Options.wallet_path,
1979
2035
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2036
+ ss58_addresses: Optional[list[str]] = Options.ss58_address,
1980
2037
  all_balances: Optional[bool] = typer.Option(
1981
2038
  False,
1982
2039
  "--all",
1983
2040
  "-a",
1984
2041
  help="Whether to display the balances for all the wallets.",
1985
2042
  ),
1986
- network: str = Options.network,
2043
+ network: Optional[list[str]] = Options.network,
1987
2044
  quiet: bool = Options.quiet,
1988
2045
  verbose: bool = Options.verbose,
1989
2046
  ):
1990
2047
  """
1991
2048
  Check the balance of the wallet. This command shows a detailed view of the wallet's coldkey balances, including free and staked balances.
1992
2049
 
2050
+ You can also pass multiple ss58 addresses of coldkeys to check their balance (using --ss58).
2051
+
1993
2052
  EXAMPLES:
1994
2053
 
1995
2054
  - To display the balance of a single wallet, use the command with the `--wallet-name` argument and provide the wallet name:
@@ -2003,17 +2062,41 @@ class CLIManager:
2003
2062
  - To display the balances of all your wallets, use the `--all` argument:
2004
2063
 
2005
2064
  [green]$[/green] btcli w balance --all
2065
+
2066
+ - To display the balances of ss58 addresses, use the `--ss58` argument:
2067
+
2068
+ [green]$[/green] btcli w balance --ss58 <ss58_address> --ss58 <ss58_address>
2069
+
2006
2070
  """
2007
2071
  self.verbosity_handler(quiet, verbose)
2008
2072
 
2009
- ask_for = [WO.PATH] if all_balances else [WO.NAME, WO.PATH]
2010
- validate = WV.NONE if all_balances else WV.WALLET
2011
- wallet = self.wallet_ask(
2012
- wallet_name, wallet_path, wallet_hotkey, ask_for=ask_for, validate=validate
2013
- )
2073
+ if ss58_addresses:
2074
+ valid_ss58s = [
2075
+ ss58 for ss58 in set(ss58_addresses) if is_valid_ss58_address(ss58)
2076
+ ]
2077
+
2078
+ invalid_ss58s = set(ss58_addresses) - set(valid_ss58s)
2079
+ for invalid_ss58 in invalid_ss58s:
2080
+ print_error(f"Incorrect ss58 address: {invalid_ss58}. Skipping.")
2081
+
2082
+ if valid_ss58s:
2083
+ wallet = None
2084
+ ss58_addresses = valid_ss58s
2085
+ else:
2086
+ raise typer.Exit()
2087
+ else:
2088
+ ask_for = [WO.PATH] if all_balances else [WO.NAME, WO.PATH]
2089
+ validate = WV.NONE if all_balances else WV.WALLET
2090
+ wallet = self.wallet_ask(
2091
+ wallet_name,
2092
+ wallet_path,
2093
+ wallet_hotkey,
2094
+ ask_for=ask_for,
2095
+ validate=validate,
2096
+ )
2014
2097
  subtensor = self.initialize_chain(network)
2015
2098
  return self._run_command(
2016
- wallets.wallet_balance(wallet, subtensor, all_balances)
2099
+ wallets.wallet_balance(wallet, subtensor, all_balances, ss58_addresses)
2017
2100
  )
2018
2101
 
2019
2102
  def wallet_history(
@@ -2058,7 +2141,7 @@ class CLIManager:
2058
2141
  wallet_name: Optional[str] = Options.wallet_name,
2059
2142
  wallet_path: Optional[str] = Options.wallet_path,
2060
2143
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2061
- network: Optional[str] = Options.network,
2144
+ network: Optional[list[str]] = Options.network,
2062
2145
  display_name: str = typer.Option(
2063
2146
  "",
2064
2147
  "--display-name",
@@ -2238,7 +2321,7 @@ class CLIManager:
2238
2321
  help="The coldkey or hotkey ss58 address to query.",
2239
2322
  prompt=True,
2240
2323
  ),
2241
- network: Optional[str] = Options.network,
2324
+ network: Optional[list[str]] = Options.network,
2242
2325
  quiet: bool = Options.quiet,
2243
2326
  verbose: bool = Options.verbose,
2244
2327
  ):
@@ -2317,7 +2400,7 @@ class CLIManager:
2317
2400
 
2318
2401
  def root_list(
2319
2402
  self,
2320
- network: Optional[str] = Options.network,
2403
+ network: Optional[list[str]] = Options.network,
2321
2404
  quiet: bool = Options.quiet,
2322
2405
  verbose: bool = Options.verbose,
2323
2406
  ):
@@ -2341,7 +2424,7 @@ class CLIManager:
2341
2424
 
2342
2425
  def root_set_weights(
2343
2426
  self,
2344
- network: str = Options.network,
2427
+ network: Optional[list[str]] = Options.network,
2345
2428
  wallet_name: str = Options.wallet_name,
2346
2429
  wallet_path: str = Options.wallet_path,
2347
2430
  wallet_hotkey: str = Options.wallet_hotkey,
@@ -2418,7 +2501,7 @@ class CLIManager:
2418
2501
 
2419
2502
  def root_get_weights(
2420
2503
  self,
2421
- network: Optional[str] = Options.network,
2504
+ network: Optional[list[str]] = Options.network,
2422
2505
  limit_min_col: Optional[int] = typer.Option(
2423
2506
  None,
2424
2507
  "--limit-min-col",
@@ -2469,7 +2552,7 @@ class CLIManager:
2469
2552
 
2470
2553
  def root_boost(
2471
2554
  self,
2472
- network: Optional[str] = Options.network,
2555
+ network: Optional[list[str]] = Options.network,
2473
2556
  wallet_name: str = Options.wallet_name,
2474
2557
  wallet_path: Optional[str] = Options.wallet_path,
2475
2558
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
@@ -2509,7 +2592,7 @@ class CLIManager:
2509
2592
 
2510
2593
  def root_slash(
2511
2594
  self,
2512
- network: Optional[str] = Options.network,
2595
+ network: Optional[list[str]] = Options.network,
2513
2596
  wallet_name: str = Options.wallet_name,
2514
2597
  wallet_path: Optional[str] = Options.wallet_path,
2515
2598
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
@@ -2550,7 +2633,7 @@ class CLIManager:
2550
2633
 
2551
2634
  def root_senate_vote(
2552
2635
  self,
2553
- network: Optional[str] = Options.network,
2636
+ network: Optional[list[str]] = Options.network,
2554
2637
  wallet_name: Optional[str] = Options.wallet_name,
2555
2638
  wallet_path: Optional[str] = Options.wallet_path,
2556
2639
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
@@ -2600,7 +2683,7 @@ class CLIManager:
2600
2683
 
2601
2684
  def root_senate(
2602
2685
  self,
2603
- network: Optional[str] = Options.network,
2686
+ network: Optional[list[str]] = Options.network,
2604
2687
  quiet: bool = Options.quiet,
2605
2688
  verbose: bool = Options.verbose,
2606
2689
  ):
@@ -2618,7 +2701,7 @@ class CLIManager:
2618
2701
 
2619
2702
  def root_register(
2620
2703
  self,
2621
- network: Optional[str] = Options.network,
2704
+ network: Optional[list[str]] = Options.network,
2622
2705
  wallet_name: Optional[str] = Options.wallet_name,
2623
2706
  wallet_path: Optional[str] = Options.wallet_path,
2624
2707
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
@@ -2653,7 +2736,7 @@ class CLIManager:
2653
2736
 
2654
2737
  def root_proposals(
2655
2738
  self,
2656
- network: Optional[str] = Options.network,
2739
+ network: Optional[list[str]] = Options.network,
2657
2740
  quiet: bool = Options.quiet,
2658
2741
  verbose: bool = Options.verbose,
2659
2742
  ):
@@ -2671,7 +2754,7 @@ class CLIManager:
2671
2754
 
2672
2755
  def root_set_take(
2673
2756
  self,
2674
- network: Optional[str] = Options.network,
2757
+ network: Optional[list[str]] = Options.network,
2675
2758
  wallet_name: Optional[str] = Options.wallet_name,
2676
2759
  wallet_path: Optional[str] = Options.wallet_path,
2677
2760
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
@@ -2741,7 +2824,7 @@ class CLIManager:
2741
2824
  wallet_name: Optional[str] = Options.wallet_name,
2742
2825
  wallet_path: Optional[str] = Options.wallet_path,
2743
2826
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2744
- network: Optional[str] = Options.network,
2827
+ network: Optional[list[str]] = Options.network,
2745
2828
  prompt: bool = Options.prompt,
2746
2829
  quiet: bool = Options.quiet,
2747
2830
  verbose: bool = Options.verbose,
@@ -2816,7 +2899,7 @@ class CLIManager:
2816
2899
  wallet_name: Optional[str] = Options.wallet_name,
2817
2900
  wallet_path: Optional[str] = Options.wallet_path,
2818
2901
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2819
- network: Optional[str] = Options.network,
2902
+ network: Optional[list[str]] = Options.network,
2820
2903
  prompt: bool = Options.prompt,
2821
2904
  quiet: bool = Options.quiet,
2822
2905
  verbose: bool = Options.verbose,
@@ -2868,7 +2951,7 @@ class CLIManager:
2868
2951
 
2869
2952
  def root_my_delegates(
2870
2953
  self,
2871
- network: Optional[str] = Options.network,
2954
+ network: Optional[list[str]] = Options.network,
2872
2955
  wallet_name: Optional[str] = Options.wallet_name,
2873
2956
  wallet_path: Optional[str] = Options.wallet_path,
2874
2957
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
@@ -2935,7 +3018,7 @@ class CLIManager:
2935
3018
 
2936
3019
  def root_list_delegates(
2937
3020
  self,
2938
- network: Optional[str] = Options.network,
3021
+ network: Optional[list[str]] = Options.network,
2939
3022
  quiet: bool = Options.quiet,
2940
3023
  verbose: bool = Options.verbose,
2941
3024
  ):
@@ -2989,14 +3072,14 @@ class CLIManager:
2989
3072
  self.verbosity_handler(quiet, verbose)
2990
3073
 
2991
3074
  if network:
2992
- if network == "finney":
2993
- network = "wss://archive.chain.opentensor.ai:443"
3075
+ if "finney" in network:
3076
+ network = ["wss://archive.chain.opentensor.ai:443"]
2994
3077
  elif (conf_net := self.config.get("network")) == "finney":
2995
- network = "wss://archive.chain.opentensor.ai:443"
3078
+ network = ["wss://archive.chain.opentensor.ai:443"]
2996
3079
  elif conf_net:
2997
- network = conf_net
3080
+ network = [conf_net]
2998
3081
  else:
2999
- network = "wss://archive.chain.opentensor.ai:443"
3082
+ network = ["wss://archive.chain.opentensor.ai:443"]
3000
3083
 
3001
3084
  sub = self.initialize_chain(network)
3002
3085
  return self._run_command(root.list_delegates(sub))
@@ -3007,7 +3090,7 @@ class CLIManager:
3007
3090
  wallet_name: Optional[str] = Options.wallet_name,
3008
3091
  wallet_path: Optional[str] = Options.wallet_path,
3009
3092
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3010
- network: Optional[str] = Options.network,
3093
+ network: Optional[list[str]] = Options.network,
3011
3094
  prompt: bool = Options.prompt,
3012
3095
  quiet: bool = Options.quiet,
3013
3096
  verbose: bool = Options.verbose,
@@ -3056,7 +3139,7 @@ class CLIManager:
3056
3139
  "-a",
3057
3140
  help="When set, the command checks all the coldkey wallets of the user instead of just the specified wallet.",
3058
3141
  ),
3059
- network: Optional[str] = Options.network,
3142
+ network: Optional[list[str]] = Options.network,
3060
3143
  wallet_name: Optional[str] = Options.wallet_name,
3061
3144
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3062
3145
  wallet_path: Optional[str] = Options.wallet_path,
@@ -3167,7 +3250,7 @@ class CLIManager:
3167
3250
  wallet_name: str = Options.wallet_name,
3168
3251
  wallet_path: str = Options.wallet_path,
3169
3252
  wallet_hotkey: str = Options.wallet_hotkey,
3170
- network: str = Options.network,
3253
+ network: Optional[list[str]] = Options.network,
3171
3254
  prompt: bool = Options.prompt,
3172
3255
  quiet: bool = Options.quiet,
3173
3256
  verbose: bool = Options.verbose,
@@ -3281,7 +3364,7 @@ class CLIManager:
3281
3364
 
3282
3365
  def stake_remove(
3283
3366
  self,
3284
- network: Optional[str] = Options.network,
3367
+ network: Optional[list[str]] = Options.network,
3285
3368
  wallet_name: str = Options.wallet_name,
3286
3369
  wallet_path: str = Options.wallet_path,
3287
3370
  wallet_hotkey: str = Options.wallet_hotkey,
@@ -3439,7 +3522,7 @@ class CLIManager:
3439
3522
  wallet_name: Optional[str] = Options.wallet_name,
3440
3523
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3441
3524
  wallet_path: Optional[str] = Options.wallet_path,
3442
- network: Optional[str] = Options.network,
3525
+ network: Optional[list[str]] = Options.network,
3443
3526
  netuid: Optional[int] = typer.Option(
3444
3527
  None,
3445
3528
  help="The netuid of the subnet (e.g. 2)",
@@ -3500,7 +3583,7 @@ class CLIManager:
3500
3583
  wallet_name: str = Options.wallet_name,
3501
3584
  wallet_hotkey: str = Options.wallet_hotkey,
3502
3585
  wallet_path: str = Options.wallet_path,
3503
- network: str = Options.network,
3586
+ network: Optional[list[str]] = Options.network,
3504
3587
  netuid: Optional[int] = typer.Option(
3505
3588
  None,
3506
3589
  help="The netuid of the subnet, (e.g. 4)",
@@ -3591,7 +3674,7 @@ class CLIManager:
3591
3674
  wallet_name: Optional[str] = Options.wallet_name,
3592
3675
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3593
3676
  wallet_path: Optional[str] = Options.wallet_path,
3594
- network: Optional[str] = Options.network,
3677
+ network: Optional[list[str]] = Options.network,
3595
3678
  netuid: Optional[int] = typer.Option(
3596
3679
  None,
3597
3680
  help="The netuid of the subnet, (e.g. 8)",
@@ -3650,7 +3733,7 @@ class CLIManager:
3650
3733
  wallet_name: Optional[str] = Options.wallet_name,
3651
3734
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3652
3735
  wallet_path: Optional[str] = Options.wallet_path,
3653
- network: Optional[str] = Options.network,
3736
+ network: Optional[list[str]] = Options.network,
3654
3737
  hotkey: Optional[str] = None,
3655
3738
  netuid: Optional[int] = typer.Option(
3656
3739
  None,
@@ -3724,7 +3807,7 @@ class CLIManager:
3724
3807
 
3725
3808
  def sudo_set(
3726
3809
  self,
3727
- network: Optional[str] = Options.network,
3810
+ network: Optional[list[str]] = Options.network,
3728
3811
  wallet_name: str = Options.wallet_name,
3729
3812
  wallet_path: str = Options.wallet_path,
3730
3813
  wallet_hotkey: str = Options.wallet_hotkey,
@@ -3789,7 +3872,7 @@ class CLIManager:
3789
3872
 
3790
3873
  def sudo_get(
3791
3874
  self,
3792
- network: str = Options.network,
3875
+ network: Optional[list[str]] = Options.network,
3793
3876
  netuid: int = Options.netuid,
3794
3877
  quiet: bool = Options.quiet,
3795
3878
  verbose: bool = Options.verbose,
@@ -3810,7 +3893,7 @@ class CLIManager:
3810
3893
 
3811
3894
  def subnets_list(
3812
3895
  self,
3813
- network: str = Options.network,
3896
+ network: Optional[list[str]] = Options.network,
3814
3897
  reuse_last: bool = Options.reuse_last,
3815
3898
  html_output: bool = Options.html_output,
3816
3899
  quiet: bool = Options.quiet,
@@ -3856,7 +3939,7 @@ class CLIManager:
3856
3939
 
3857
3940
  def subnets_lock_cost(
3858
3941
  self,
3859
- network: str = Options.network,
3942
+ network: Optional[list[str]] = Options.network,
3860
3943
  quiet: bool = Options.quiet,
3861
3944
  verbose: bool = Options.verbose,
3862
3945
  ):
@@ -3877,7 +3960,7 @@ class CLIManager:
3877
3960
  wallet_name: str = Options.wallet_name,
3878
3961
  wallet_path: str = Options.wallet_path,
3879
3962
  wallet_hotkey: str = Options.wallet_hotkey,
3880
- network: str = Options.network,
3963
+ network: Optional[list[str]] = Options.network,
3881
3964
  prompt: bool = Options.prompt,
3882
3965
  quiet: bool = Options.quiet,
3883
3966
  verbose: bool = Options.verbose,
@@ -3906,7 +3989,7 @@ class CLIManager:
3906
3989
  wallet_name: Optional[str] = Options.wallet_name,
3907
3990
  wallet_path: Optional[str] = Options.wallet_path,
3908
3991
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
3909
- network: Optional[str] = Options.network,
3992
+ network: Optional[list[str]] = Options.network,
3910
3993
  netuid: int = Options.netuid,
3911
3994
  # TODO add the following to config
3912
3995
  processors: Optional[int] = typer.Option(
@@ -3993,7 +4076,7 @@ class CLIManager:
3993
4076
  wallet_name: str = Options.wallet_name,
3994
4077
  wallet_path: str = Options.wallet_path,
3995
4078
  wallet_hotkey: str = Options.wallet_hotkey,
3996
- network: str = Options.network,
4079
+ network: Optional[list[str]] = Options.network,
3997
4080
  netuid: int = Options.netuid,
3998
4081
  prompt: bool = Options.prompt,
3999
4082
  quiet: bool = Options.quiet,
@@ -4034,7 +4117,7 @@ class CLIManager:
4034
4117
  help="The netuid of the subnet (e.g. 1). This option "
4035
4118
  "is ignored when used with `--reuse-last`.",
4036
4119
  ),
4037
- network: str = Options.network,
4120
+ network: Optional[list[str]] = Options.network,
4038
4121
  reuse_last: bool = Options.reuse_last,
4039
4122
  html_output: bool = Options.html_output,
4040
4123
  quiet: bool = Options.quiet,
@@ -4124,7 +4207,7 @@ class CLIManager:
4124
4207
 
4125
4208
  def weights_reveal(
4126
4209
  self,
4127
- network: str = Options.network,
4210
+ network: Optional[list[str]] = Options.network,
4128
4211
  wallet_name: str = Options.wallet_name,
4129
4212
  wallet_path: str = Options.wallet_path,
4130
4213
  wallet_hotkey: str = Options.wallet_hotkey,
@@ -4220,7 +4303,7 @@ class CLIManager:
4220
4303
 
4221
4304
  def weights_commit(
4222
4305
  self,
4223
- network: str = Options.network,
4306
+ network: Optional[list[str]] = Options.network,
4224
4307
  wallet_name: str = Options.wallet_name,
4225
4308
  wallet_path: str = Options.wallet_path,
4226
4309
  wallet_hotkey: str = Options.wallet_hotkey,
@@ -4313,6 +4396,37 @@ class CLIManager:
4313
4396
  )
4314
4397
  )
4315
4398
 
4399
+ @staticmethod
4400
+ @utils_app.command("convert")
4401
+ def convert(
4402
+ from_rao: Optional[str] = typer.Option(
4403
+ None, "--rao", help="Convert amount from Rao"
4404
+ ),
4405
+ from_tao: Optional[float] = typer.Option(
4406
+ None, "--tao", help="Convert amount from Tao"
4407
+ ),
4408
+ ):
4409
+ """
4410
+ Allows for converting between tao and rao using the specified flags
4411
+ """
4412
+ if from_tao is None and from_rao is None:
4413
+ err_console.print("Specify `--rao` and/or `--tao`.")
4414
+ raise typer.Exit()
4415
+ if from_rao is not None:
4416
+ rao = int(float(from_rao))
4417
+ console.print(
4418
+ f"{rao}{Balance.rao_unit}",
4419
+ "=",
4420
+ Balance.from_rao(rao),
4421
+ )
4422
+ if from_tao is not None:
4423
+ tao = float(from_tao)
4424
+ console.print(
4425
+ f"{Balance.unit}{tao}",
4426
+ "=",
4427
+ f"{Balance.from_tao(tao).rao}{Balance.rao_unit}",
4428
+ )
4429
+
4316
4430
  def run(self):
4317
4431
  self.app()
4318
4432
 
@@ -679,10 +679,10 @@ class Websocket:
679
679
 
680
680
  async def shutdown(self):
681
681
  async with self._lock:
682
- self._receiving_task.cancel()
683
682
  try:
683
+ self._receiving_task.cancel()
684
684
  await self._receiving_task
685
- except asyncio.CancelledError:
685
+ except (AttributeError, asyncio.CancelledError):
686
686
  pass
687
687
  await self.ws.close()
688
688
  self.ws = None
@@ -1708,7 +1708,7 @@ class AsyncSubstrateInterface:
1708
1708
  result = await self._make_rpc_request(payloads, runtime=runtime)
1709
1709
  if "error" in result[payload_id][0]:
1710
1710
  raise SubstrateRequestException(
1711
- result["rpc_request"][0]["error"]["message"]
1711
+ result[payload_id][0]["error"]["message"]
1712
1712
  )
1713
1713
  if "result" in result[payload_id][0]:
1714
1714
  return result[payload_id][0]
@@ -614,7 +614,10 @@ async def register_extrinsic(
614
614
  if not await response.is_success:
615
615
  success, err_msg = (
616
616
  False,
617
- format_error_message(await response.error_message),
617
+ format_error_message(
618
+ await response.error_message,
619
+ substrate=subtensor.substrate,
620
+ ),
618
621
  )
619
622
 
620
623
  if not success:
@@ -785,7 +788,8 @@ async def run_faucet_extrinsic(
785
788
  await response.process_events()
786
789
  if not await response.is_success:
787
790
  err_console.print(
788
- f":cross_mark: [red]Failed[/red]: {format_error_message(await response.error_message)}"
791
+ f":cross_mark: [red]Failed[/red]: "
792
+ f"{format_error_message(await response.error_message, subtensor.substrate)}"
789
793
  )
790
794
  if attempts == max_allowed_attempts:
791
795
  raise MaxAttemptsException