bittensor-cli 9.2.0__py3-none-any.whl → 9.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bittensor_cli/cli.py CHANGED
@@ -2,6 +2,7 @@
2
2
  import asyncio
3
3
  import curses
4
4
  import importlib
5
+ import json
5
6
  import os.path
6
7
  import re
7
8
  import ssl
@@ -14,11 +15,19 @@ from dataclasses import fields
14
15
  import rich
15
16
  import typer
16
17
  import numpy as np
18
+ from async_substrate_interface.errors import (
19
+ SubstrateRequestException,
20
+ ConnectionClosed,
21
+ InvalidHandshake,
22
+ )
17
23
  from bittensor_wallet import Wallet
18
24
  from rich import box
19
25
  from rich.prompt import Confirm, FloatPrompt, Prompt, IntPrompt
20
26
  from rich.table import Column, Table
21
27
  from rich.tree import Tree
28
+ from typing_extensions import Annotated
29
+ from yaml import safe_dump, safe_load
30
+
22
31
  from bittensor_cli.src import (
23
32
  defaults,
24
33
  HELP_PANELS,
@@ -31,7 +40,6 @@ from bittensor_cli.src import (
31
40
  from bittensor_cli.version import __version__, __version_as_int__
32
41
  from bittensor_cli.src.bittensor import utils
33
42
  from bittensor_cli.src.bittensor.balances import Balance
34
- from async_substrate_interface.errors import SubstrateRequestException
35
43
  from bittensor_cli.src.commands import sudo, wallets, view
36
44
  from bittensor_cli.src.commands import weights as weights_cmds
37
45
  from bittensor_cli.src.commands.subnets import price, subnets
@@ -48,6 +56,7 @@ from bittensor_cli.src.bittensor.utils import (
48
56
  console,
49
57
  err_console,
50
58
  verbose_console,
59
+ json_console,
51
60
  is_valid_ss58_address,
52
61
  print_error,
53
62
  validate_chain_endpoint,
@@ -61,9 +70,6 @@ from bittensor_cli.src.bittensor.utils import (
61
70
  is_linux,
62
71
  validate_rate_tolerance,
63
72
  )
64
- from typing_extensions import Annotated
65
- from websockets import ConnectionClosed, InvalidHandshake
66
- from yaml import safe_dump, safe_load
67
73
 
68
74
  try:
69
75
  from git import Repo, GitError
@@ -109,6 +115,17 @@ class Options:
109
115
  "--wallet.hotkey",
110
116
  help="Hotkey of the wallet",
111
117
  )
118
+ wallet_ss58_address = typer.Option(
119
+ None,
120
+ "--wallet-name",
121
+ "--name",
122
+ "--wallet_name",
123
+ "--wallet.name",
124
+ "--address",
125
+ "--ss58",
126
+ "--ss58-address",
127
+ help="SS58 address or wallet name to check. Leave empty to be prompted.",
128
+ )
112
129
  wallet_hotkey_ss58 = typer.Option(
113
130
  None,
114
131
  "--hotkey",
@@ -242,14 +259,15 @@ class Options:
242
259
  "--slippage-tolerance",
243
260
  "--tolerance",
244
261
  "--rate-tolerance",
245
- help="Set the rate tolerance percentage for transactions (default: 0.05%).",
262
+ help="Set the rate tolerance percentage for transactions (default: 0.05 for 5%).",
246
263
  callback=validate_rate_tolerance,
247
264
  )
248
265
  safe_staking = typer.Option(
249
266
  None,
250
267
  "--safe-staking/--no-safe-staking",
251
268
  "--safe/--unsafe",
252
- help="Enable or disable safe staking mode (default: enabled).",
269
+ show_default=False,
270
+ help="Enable or disable safe staking mode [dim](default: enabled)[/dim].",
253
271
  )
254
272
  allow_partial_stake = typer.Option(
255
273
  None,
@@ -257,7 +275,8 @@ class Options:
257
275
  "--partial/--no-partial",
258
276
  "--allow/--not-allow",
259
277
  "--allow-partial/--not-partial",
260
- help="Enable or disable partial stake mode (default: disabled).",
278
+ show_default=False,
279
+ help="Enable or disable partial stake mode [dim](default: disabled)[/dim].",
261
280
  )
262
281
  dashboard_path = typer.Option(
263
282
  None,
@@ -268,6 +287,15 @@ class Options:
268
287
  "--dashboard.path",
269
288
  help="Path to save the dashboard HTML file. For example: `~/.bittensor/dashboard`.",
270
289
  )
290
+ json_output = typer.Option(
291
+ False,
292
+ "--json-output",
293
+ "--json-out",
294
+ help="Outputs the result of the command as JSON.",
295
+ )
296
+ era: int = typer.Option(
297
+ 3, help="Length (in blocks) for which the transaction should be valid."
298
+ )
271
299
 
272
300
 
273
301
  def list_prompt(init_var: list, list_type: type, help_text: str) -> list:
@@ -313,22 +341,31 @@ def verbosity_console_handler(verbosity_level: int = 1) -> None:
313
341
  :param verbosity_level: int corresponding to verbosity level of console output (0 is quiet, 1 is normal, 2 is
314
342
  verbose)
315
343
  """
316
- if verbosity_level not in range(3):
344
+ if verbosity_level not in range(4):
317
345
  raise ValueError(
318
- f"Invalid verbosity level: {verbosity_level}. Must be one of: 0 (quiet), 1 (normal), 2 (verbose)"
346
+ f"Invalid verbosity level: {verbosity_level}. "
347
+ f"Must be one of: 0 (quiet + json output), 1 (normal), 2 (verbose), 3 (json output + verbose)"
319
348
  )
320
349
  if verbosity_level == 0:
321
350
  console.quiet = True
322
351
  err_console.quiet = True
323
352
  verbose_console.quiet = True
353
+ json_console.quiet = False
324
354
  elif verbosity_level == 1:
325
355
  console.quiet = False
326
356
  err_console.quiet = False
327
357
  verbose_console.quiet = True
358
+ json_console.quiet = True
328
359
  elif verbosity_level == 2:
329
360
  console.quiet = False
330
361
  err_console.quiet = False
331
362
  verbose_console.quiet = False
363
+ json_console.quiet = True
364
+ elif verbosity_level == 3:
365
+ console.quiet = True
366
+ err_console.quiet = True
367
+ verbose_console.quiet = False
368
+ json_console.quiet = False
332
369
 
333
370
 
334
371
  def get_optional_netuid(netuid: Optional[int], all_netuids: bool) -> Optional[int]:
@@ -684,6 +721,12 @@ class CLIManager:
684
721
  self.wallet_app.command(
685
722
  "swap-hotkey", rich_help_panel=HELP_PANELS["WALLET"]["SECURITY"]
686
723
  )(self.wallet_swap_hotkey)
724
+ self.wallet_app.command(
725
+ "swap-coldkey", rich_help_panel=HELP_PANELS["WALLET"]["SECURITY"]
726
+ )(self.wallet_swap_coldkey)
727
+ self.wallet_app.command(
728
+ "swap-check", rich_help_panel=HELP_PANELS["WALLET"]["SECURITY"]
729
+ )(self.wallet_check_ck_swap)
687
730
  self.wallet_app.command(
688
731
  "regen-coldkey", rich_help_panel=HELP_PANELS["WALLET"]["SECURITY"]
689
732
  )(self.wallet_regen_coldkey)
@@ -699,6 +742,9 @@ class CLIManager:
699
742
  self.wallet_app.command(
700
743
  "new-coldkey", rich_help_panel=HELP_PANELS["WALLET"]["MANAGEMENT"]
701
744
  )(self.wallet_new_coldkey)
745
+ self.wallet_app.command(
746
+ "associate-hotkey", rich_help_panel=HELP_PANELS["WALLET"]["MANAGEMENT"]
747
+ )(self.wallet_associate_hotkey)
702
748
  self.wallet_app.command(
703
749
  "create", rich_help_panel=HELP_PANELS["WALLET"]["MANAGEMENT"]
704
750
  )(self.wallet_create_wallet)
@@ -829,6 +875,12 @@ class CLIManager:
829
875
  self.subnets_app.command(
830
876
  "get-identity", rich_help_panel=HELP_PANELS["SUBNETS"]["IDENTITY"]
831
877
  )(self.subnets_get_identity)
878
+ self.subnets_app.command(
879
+ "start", rich_help_panel=HELP_PANELS["SUBNETS"]["CREATION"]
880
+ )(self.subnets_start)
881
+ self.subnets_app.command(
882
+ "check-start", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"]
883
+ )(self.subnets_check_start)
832
884
 
833
885
  # weights commands
834
886
  self.weights_app.command(
@@ -934,6 +986,7 @@ class CLIManager:
934
986
  """
935
987
  if not self.subtensor:
936
988
  if network:
989
+ network_ = None
937
990
  for item in network:
938
991
  if item.startswith("ws"):
939
992
  network_ = item
@@ -1068,12 +1121,15 @@ class CLIManager:
1068
1121
  except ModuleNotFoundError:
1069
1122
  self.asyncio_runner = asyncio.run
1070
1123
 
1071
- def verbosity_handler(self, quiet: bool, verbose: bool):
1124
+ def verbosity_handler(
1125
+ self, quiet: bool, verbose: bool, json_output: bool = False
1126
+ ) -> None:
1072
1127
  if quiet and verbose:
1073
1128
  err_console.print("Cannot specify both `--quiet` and `--verbose`")
1074
1129
  raise typer.Exit()
1075
-
1076
- if quiet:
1130
+ if json_output and verbose:
1131
+ verbosity_console_handler(3)
1132
+ elif json_output or quiet:
1077
1133
  verbosity_console_handler(0)
1078
1134
  elif verbose:
1079
1135
  verbosity_console_handler(2)
@@ -1136,12 +1192,14 @@ class CLIManager:
1136
1192
  "--safe-staking/--no-safe-staking",
1137
1193
  "--safe/--unsafe",
1138
1194
  help="Enable or disable safe staking mode.",
1195
+ show_default=False,
1139
1196
  ),
1140
1197
  allow_partial_stake: Optional[bool] = typer.Option(
1141
1198
  None,
1142
1199
  "--allow-partial-stake/--no-allow-partial-stake",
1143
1200
  "--partial/--no-partial",
1144
1201
  "--allow/--not-allow",
1202
+ show_default=False,
1145
1203
  ),
1146
1204
  dashboard_path: Optional[str] = Options.dashboard_path,
1147
1205
  ):
@@ -1203,7 +1261,8 @@ class CLIManager:
1203
1261
  elif arg == "rate_tolerance":
1204
1262
  while True:
1205
1263
  val = FloatPrompt.ask(
1206
- f"What percentage would you like to set for [red]{arg}[/red]?\nValues are percentages (e.g. 0.05 for 5%)",
1264
+ f"What percentage would you like to set for [red]{arg}[/red]?\n"
1265
+ f"Values are percentages (e.g. 0.05 for 5%)",
1207
1266
  default=0.05,
1208
1267
  )
1209
1268
  try:
@@ -1503,7 +1562,7 @@ class CLIManager:
1503
1562
  wallet_name: Optional[str],
1504
1563
  wallet_path: Optional[str],
1505
1564
  wallet_hotkey: Optional[str],
1506
- ask_for: list[str] = [],
1565
+ ask_for: Optional[list[str]] = None,
1507
1566
  validate: WV = WV.WALLET,
1508
1567
  ) -> Wallet:
1509
1568
  """
@@ -1512,9 +1571,10 @@ class CLIManager:
1512
1571
  :param wallet_path: root path of the wallets
1513
1572
  :param wallet_hotkey: name of the wallet hotkey file
1514
1573
  :param validate: flag whether to check for the wallet's validity
1515
- :param ask_type: aspect of the wallet (name, path, hotkey) to prompt the user for
1574
+ :param ask_for: aspect of the wallet (name, path, hotkey) to prompt the user for
1516
1575
  :return: created Wallet object
1517
1576
  """
1577
+ ask_for = ask_for or []
1518
1578
  # Prompt for missing attributes specified in ask_for
1519
1579
  if WO.NAME in ask_for and not wallet_name:
1520
1580
  if self.config.get("wallet_name"):
@@ -1587,6 +1647,7 @@ class CLIManager:
1587
1647
  wallet_path: str = Options.wallet_path,
1588
1648
  quiet: bool = Options.quiet,
1589
1649
  verbose: bool = Options.verbose,
1650
+ json_output: bool = Options.json_output,
1590
1651
  ):
1591
1652
  """
1592
1653
  Displays all the wallets and their corresponding hotkeys that are located in the wallet path specified in the config.
@@ -1602,11 +1663,11 @@ class CLIManager:
1602
1663
 
1603
1664
  [bold]NOTE[/bold]: This command is read-only and does not modify the filesystem or the blockchain state. It is intended for use with the Bittensor CLI to provide a quick overview of the user's wallets.
1604
1665
  """
1605
- self.verbosity_handler(quiet, verbose)
1666
+ self.verbosity_handler(quiet, verbose, json_output)
1606
1667
  wallet = self.wallet_ask(
1607
1668
  None, wallet_path, None, ask_for=[WO.PATH], validate=WV.NONE
1608
1669
  )
1609
- return self._run_command(wallets.wallet_list(wallet.path))
1670
+ return self._run_command(wallets.wallet_list(wallet.path, json_output))
1610
1671
 
1611
1672
  def wallet_overview(
1612
1673
  self,
@@ -1646,6 +1707,7 @@ class CLIManager:
1646
1707
  network: Optional[list[str]] = Options.network,
1647
1708
  quiet: bool = Options.quiet,
1648
1709
  verbose: bool = Options.verbose,
1710
+ json_output: bool = Options.json_output,
1649
1711
  ):
1650
1712
  """
1651
1713
  Displays a detailed overview of the user's registered accounts on the Bittensor network.
@@ -1662,7 +1724,7 @@ class CLIManager:
1662
1724
  It provides a quick and comprehensive view of the user's network presence, making it useful for monitoring account status,
1663
1725
  stake distribution, and overall contribution to the Bittensor network.
1664
1726
  """
1665
- self.verbosity_handler(quiet, verbose)
1727
+ self.verbosity_handler(quiet, verbose, json_output)
1666
1728
  if include_hotkeys and exclude_hotkeys:
1667
1729
  utils.err_console.print(
1668
1730
  "[red]You have specified both the inclusion and exclusion options. Only one of these options is allowed currently."
@@ -1707,6 +1769,7 @@ class CLIManager:
1707
1769
  exclude_hotkeys,
1708
1770
  netuids_filter=netuids,
1709
1771
  verbose=verbose,
1772
+ json_output=json_output,
1710
1773
  )
1711
1774
  )
1712
1775
 
@@ -1729,6 +1792,7 @@ class CLIManager:
1729
1792
  transfer_all: bool = typer.Option(
1730
1793
  False, "--all", prompt=False, help="Transfer all available balance."
1731
1794
  ),
1795
+ era: int = Options.era,
1732
1796
  wallet_name: str = Options.wallet_name,
1733
1797
  wallet_path: str = Options.wallet_path,
1734
1798
  wallet_hotkey: str = Options.wallet_hotkey,
@@ -1736,6 +1800,7 @@ class CLIManager:
1736
1800
  prompt: bool = Options.prompt,
1737
1801
  quiet: bool = Options.quiet,
1738
1802
  verbose: bool = Options.verbose,
1803
+ json_output: bool = Options.json_output,
1739
1804
  ):
1740
1805
  """
1741
1806
  Send TAO tokens from one wallet to another wallet on the Bittensor network.
@@ -1759,7 +1824,7 @@ class CLIManager:
1759
1824
  print_error("You have entered an incorrect ss58 address. Please try again.")
1760
1825
  raise typer.Exit()
1761
1826
 
1762
- self.verbosity_handler(quiet, verbose)
1827
+ self.verbosity_handler(quiet, verbose, json_output)
1763
1828
  wallet = self.wallet_ask(
1764
1829
  wallet_name,
1765
1830
  wallet_path,
@@ -1777,12 +1842,14 @@ class CLIManager:
1777
1842
  amount = FloatPrompt.ask("Enter amount (in TAO) to transfer.")
1778
1843
  return self._run_command(
1779
1844
  wallets.transfer(
1780
- wallet,
1781
- subtensor,
1782
- destination_ss58_address,
1783
- amount,
1784
- transfer_all,
1785
- prompt,
1845
+ wallet=wallet,
1846
+ subtensor=subtensor,
1847
+ destination=destination_ss58_address,
1848
+ amount=amount,
1849
+ transfer_all=transfer_all,
1850
+ era=era,
1851
+ prompt=prompt,
1852
+ json_output=json_output,
1786
1853
  )
1787
1854
  )
1788
1855
 
@@ -1798,6 +1865,7 @@ class CLIManager:
1798
1865
  quiet: bool = Options.quiet,
1799
1866
  verbose: bool = Options.verbose,
1800
1867
  prompt: bool = Options.prompt,
1868
+ json_output: bool = Options.json_output,
1801
1869
  ):
1802
1870
  """
1803
1871
  Swap hotkeys of a given wallet on the blockchain. For a registered key pair, for example, a (coldkeyA, hotkeyA) pair, this command swaps the hotkeyA with a new, unregistered, hotkeyB to move the original registration to the (coldkeyA, hotkeyB) pair.
@@ -1816,7 +1884,7 @@ class CLIManager:
1816
1884
 
1817
1885
  [green]$[/green] btcli wallet swap_hotkey destination_hotkey_name --wallet-name your_wallet_name --wallet-hotkey original_hotkey
1818
1886
  """
1819
- self.verbosity_handler(quiet, verbose)
1887
+ self.verbosity_handler(quiet, verbose, json_output)
1820
1888
  original_wallet = self.wallet_ask(
1821
1889
  wallet_name,
1822
1890
  wallet_path,
@@ -1838,7 +1906,9 @@ class CLIManager:
1838
1906
  )
1839
1907
  self.initialize_chain(network)
1840
1908
  return self._run_command(
1841
- wallets.swap_hotkey(original_wallet, new_wallet, self.subtensor, prompt)
1909
+ wallets.swap_hotkey(
1910
+ original_wallet, new_wallet, self.subtensor, prompt, json_output
1911
+ )
1842
1912
  )
1843
1913
 
1844
1914
  def wallet_inspect(
@@ -1857,6 +1927,7 @@ class CLIManager:
1857
1927
  netuids: str = Options.netuids,
1858
1928
  quiet: bool = Options.quiet,
1859
1929
  verbose: bool = Options.verbose,
1930
+ json_output: bool = Options.json_output,
1860
1931
  ):
1861
1932
  """
1862
1933
  Displays the details of the user's wallet pairs (coldkey, hotkey) on the Bittensor network.
@@ -1891,7 +1962,7 @@ class CLIManager:
1891
1962
  """
1892
1963
  print_error("This command is disabled on the 'rao' network.")
1893
1964
  raise typer.Exit()
1894
- self.verbosity_handler(quiet, verbose)
1965
+ self.verbosity_handler(quiet, verbose, json_output)
1895
1966
 
1896
1967
  if netuids:
1897
1968
  netuids = parse_to_list(
@@ -1988,6 +2059,7 @@ class CLIManager:
1988
2059
 
1989
2060
  [bold]Note[/bold]: This command is meant for used in local environments where users can experiment with the blockchain without using real TAO tokens. Users must have the necessary hardware setup, especially when opting for CUDA-based GPU calculations. It is currently disabled on testnet and mainnet (finney). You can only use this command on a local blockchain.
1990
2061
  """
2062
+ # TODO should we add json_output?
1991
2063
  wallet = self.wallet_ask(
1992
2064
  wallet_name,
1993
2065
  wallet_path,
@@ -2024,6 +2096,7 @@ class CLIManager:
2024
2096
  overwrite: bool = Options.overwrite,
2025
2097
  quiet: bool = Options.quiet,
2026
2098
  verbose: bool = Options.verbose,
2099
+ json_output: bool = Options.json_output,
2027
2100
  ):
2028
2101
  """
2029
2102
  Regenerate a coldkey for a wallet on the Bittensor blockchain network.
@@ -2041,7 +2114,7 @@ class CLIManager:
2041
2114
 
2042
2115
  [bold]Note[/bold]: This command is critical for users who need to regenerate their coldkey either for recovery or for security reasons.
2043
2116
  """
2044
- self.verbosity_handler(quiet, verbose)
2117
+ self.verbosity_handler(quiet, verbose, json_output)
2045
2118
 
2046
2119
  if not wallet_path:
2047
2120
  wallet_path = Prompt.ask(
@@ -2069,6 +2142,7 @@ class CLIManager:
2069
2142
  json_password,
2070
2143
  use_password,
2071
2144
  overwrite,
2145
+ json_output,
2072
2146
  )
2073
2147
  )
2074
2148
 
@@ -2082,6 +2156,7 @@ class CLIManager:
2082
2156
  overwrite: bool = Options.overwrite,
2083
2157
  quiet: bool = Options.quiet,
2084
2158
  verbose: bool = Options.verbose,
2159
+ json_output: bool = Options.json_output,
2085
2160
  ):
2086
2161
  """
2087
2162
  Regenerates the public part of a coldkey (coldkeypub.txt) for a wallet.
@@ -2098,7 +2173,7 @@ class CLIManager:
2098
2173
 
2099
2174
  [bold]Note[/bold]: This command is particularly useful for users who need to regenerate their coldkeypub, perhaps due to file corruption or loss. You will need either ss58 address or public hex key from your old coldkeypub.txt for the wallet. It is a recovery-focused utility that ensures continued access to your wallet functionalities.
2100
2175
  """
2101
- self.verbosity_handler(quiet, verbose)
2176
+ self.verbosity_handler(quiet, verbose, json_output)
2102
2177
 
2103
2178
  if not wallet_path:
2104
2179
  wallet_path = Prompt.ask(
@@ -2127,7 +2202,9 @@ class CLIManager:
2127
2202
  rich.print("[red]Error: Invalid SS58 address or public key![/red]")
2128
2203
  raise typer.Exit()
2129
2204
  return self._run_command(
2130
- wallets.regen_coldkey_pub(wallet, ss58_address, public_key_hex, overwrite)
2205
+ wallets.regen_coldkey_pub(
2206
+ wallet, ss58_address, public_key_hex, overwrite, json_output
2207
+ )
2131
2208
  )
2132
2209
 
2133
2210
  def wallet_regen_hotkey(
@@ -2146,6 +2223,7 @@ class CLIManager:
2146
2223
  overwrite: bool = Options.overwrite,
2147
2224
  quiet: bool = Options.quiet,
2148
2225
  verbose: bool = Options.verbose,
2226
+ json_output: bool = Options.json_output,
2149
2227
  ):
2150
2228
  """
2151
2229
  Regenerates a hotkey for a wallet.
@@ -2164,7 +2242,7 @@ class CLIManager:
2164
2242
  [bold]Note[/bold]: This command is essential for users who need to regenerate their hotkey, possibly for security upgrades or key recovery.
2165
2243
  It should be used with caution to avoid accidental overwriting of existing keys.
2166
2244
  """
2167
- self.verbosity_handler(quiet, verbose)
2245
+ self.verbosity_handler(quiet, verbose, json_output)
2168
2246
  wallet = self.wallet_ask(
2169
2247
  wallet_name,
2170
2248
  wallet_path,
@@ -2184,6 +2262,7 @@ class CLIManager:
2184
2262
  json_password,
2185
2263
  use_password,
2186
2264
  overwrite,
2265
+ json_output,
2187
2266
  )
2188
2267
  )
2189
2268
 
@@ -2206,6 +2285,7 @@ class CLIManager:
2206
2285
  overwrite: bool = Options.overwrite,
2207
2286
  quiet: bool = Options.quiet,
2208
2287
  verbose: bool = Options.verbose,
2288
+ json_output: bool = Options.json_output,
2209
2289
  ):
2210
2290
  """
2211
2291
  Create a new hotkey for a wallet.
@@ -2221,7 +2301,7 @@ class CLIManager:
2221
2301
 
2222
2302
  [italic]Note[/italic]: This command is useful to create additional hotkeys for different purposes, such as running multiple subnet miners or subnet validators or separating operational roles within the Bittensor network.
2223
2303
  """
2224
- self.verbosity_handler(quiet, verbose)
2304
+ self.verbosity_handler(quiet, verbose, json_output)
2225
2305
 
2226
2306
  if not wallet_name:
2227
2307
  wallet_name = Prompt.ask(
@@ -2245,7 +2325,77 @@ class CLIManager:
2245
2325
  if not uri:
2246
2326
  n_words = get_n_words(n_words)
2247
2327
  return self._run_command(
2248
- wallets.new_hotkey(wallet, n_words, use_password, uri, overwrite)
2328
+ wallets.new_hotkey(
2329
+ wallet, n_words, use_password, uri, overwrite, json_output
2330
+ )
2331
+ )
2332
+
2333
+ def wallet_associate_hotkey(
2334
+ self,
2335
+ wallet_name: Optional[str] = Options.wallet_name,
2336
+ wallet_path: Optional[str] = Options.wallet_path,
2337
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey_ss58,
2338
+ network: Optional[list[str]] = Options.network,
2339
+ prompt: bool = Options.prompt,
2340
+ quiet: bool = Options.quiet,
2341
+ verbose: bool = Options.verbose,
2342
+ ):
2343
+ """
2344
+ Associate a hotkey with a wallet(coldkey).
2345
+
2346
+ USAGE
2347
+
2348
+ This command is used to associate a hotkey with a wallet(coldkey).
2349
+
2350
+ EXAMPLE
2351
+
2352
+ [green]$[/green] btcli wallet associate-hotkey --hotkey-name hotkey_name
2353
+ [green]$[/green] btcli wallet associate-hotkey --hotkey-ss58 5DkQ4...
2354
+ """
2355
+ self.verbosity_handler(quiet, verbose)
2356
+ if not wallet_name:
2357
+ wallet_name = Prompt.ask(
2358
+ "Enter the [blue]wallet name[/blue] [dim](which you want to associate with the hotkey)[/dim]",
2359
+ default=self.config.get("wallet_name") or defaults.wallet.name,
2360
+ )
2361
+ if not wallet_hotkey:
2362
+ wallet_hotkey = Prompt.ask(
2363
+ "Enter the [blue]hotkey[/blue] name or "
2364
+ "[blue]hotkey ss58 address[/blue] [dim](to associate with your coldkey)[/dim]"
2365
+ )
2366
+
2367
+ hotkey_display = None
2368
+ if is_valid_ss58_address(wallet_hotkey):
2369
+ hotkey_ss58 = wallet_hotkey
2370
+ wallet = self.wallet_ask(
2371
+ wallet_name,
2372
+ wallet_path,
2373
+ None,
2374
+ ask_for=[WO.NAME, WO.PATH],
2375
+ validate=WV.WALLET,
2376
+ )
2377
+ hotkey_display = (
2378
+ f"hotkey [{COLORS.GENERAL.HK}]{hotkey_ss58}[/{COLORS.GENERAL.HK}]"
2379
+ )
2380
+ else:
2381
+ wallet = self.wallet_ask(
2382
+ wallet_name,
2383
+ wallet_path,
2384
+ wallet_hotkey,
2385
+ ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
2386
+ validate=WV.WALLET_AND_HOTKEY,
2387
+ )
2388
+ hotkey_ss58 = wallet.hotkey.ss58_address
2389
+ hotkey_display = f"hotkey [blue]{wallet_hotkey}[/blue] [{COLORS.GENERAL.HK}]({hotkey_ss58})[/{COLORS.GENERAL.HK}]"
2390
+
2391
+ return self._run_command(
2392
+ wallets.associate_hotkey(
2393
+ wallet,
2394
+ self.initialize_chain(network),
2395
+ hotkey_ss58,
2396
+ hotkey_display,
2397
+ prompt,
2398
+ )
2249
2399
  )
2250
2400
 
2251
2401
  def wallet_new_coldkey(
@@ -2264,6 +2414,7 @@ class CLIManager:
2264
2414
  overwrite: bool = Options.overwrite,
2265
2415
  quiet: bool = Options.quiet,
2266
2416
  verbose: bool = Options.verbose,
2417
+ json_output: bool = Options.json_output,
2267
2418
  ):
2268
2419
  """
2269
2420
  Create a new coldkey. A coldkey is required for holding TAO balances and performing high-value transactions.
@@ -2278,7 +2429,7 @@ class CLIManager:
2278
2429
 
2279
2430
  [bold]Note[/bold]: This command is crucial for users who need to create a new coldkey for enhanced security or as part of setting up a new wallet. It is a foundational step in establishing a secure presence on the Bittensor network.
2280
2431
  """
2281
- self.verbosity_handler(quiet, verbose)
2432
+ self.verbosity_handler(quiet, verbose, json_output)
2282
2433
 
2283
2434
  if not wallet_path:
2284
2435
  wallet_path = Prompt.ask(
@@ -2301,33 +2452,100 @@ class CLIManager:
2301
2452
  if not uri:
2302
2453
  n_words = get_n_words(n_words)
2303
2454
  return self._run_command(
2304
- wallets.new_coldkey(wallet, n_words, use_password, uri, overwrite)
2455
+ wallets.new_coldkey(
2456
+ wallet, n_words, use_password, uri, overwrite, json_output
2457
+ )
2305
2458
  )
2306
2459
 
2307
2460
  def wallet_check_ck_swap(
2308
2461
  self,
2309
- wallet_name: Optional[str] = Options.wallet_name,
2462
+ wallet_ss58_address: Optional[str] = Options.wallet_ss58_address,
2310
2463
  wallet_path: Optional[str] = Options.wallet_path,
2311
2464
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2465
+ scheduled_block: Optional[int] = typer.Option(
2466
+ None,
2467
+ "--block",
2468
+ help="Block number where the swap was scheduled",
2469
+ ),
2470
+ show_all: bool = typer.Option(
2471
+ False,
2472
+ "--all",
2473
+ "-a",
2474
+ help="Show all pending coldkey swaps",
2475
+ ),
2312
2476
  network: Optional[list[str]] = Options.network,
2313
2477
  quiet: bool = Options.quiet,
2314
2478
  verbose: bool = Options.verbose,
2315
2479
  ):
2316
2480
  """
2317
- Check the status of your scheduled coldkey swap.
2481
+ Check the status of scheduled coldkey swaps.
2318
2482
 
2319
2483
  USAGE
2320
2484
 
2321
- Users should provide the old coldkey wallet to check the swap status.
2485
+ This command can be used in three ways:
2486
+ 1. Show all pending swaps (--all)
2487
+ 2. Check status of a specific wallet's swap or SS58 address
2488
+ 3. Check detailed swap status with block number (--block)
2322
2489
 
2323
- EXAMPLE
2490
+ EXAMPLES
2491
+
2492
+ Show all pending swaps:
2493
+ [green]$[/green] btcli wallet swap-check --all
2494
+
2495
+ Check specific wallet's swap:
2496
+ [green]$[/green] btcli wallet swap-check --wallet-name my_wallet
2497
+
2498
+ Check swap using SS58 address:
2499
+ [green]$[/green] btcli wallet swap-check --ss58 5DkQ4...
2324
2500
 
2325
- [green]$[/green] btcli wallet check_coldkey_swap
2501
+ Check swap details with block number:
2502
+ [green]$[/green] btcli wallet swap-check --wallet-name my_wallet --block 12345
2326
2503
  """
2504
+ # TODO add json_output if this ever gets used again (doubtful)
2327
2505
  self.verbosity_handler(quiet, verbose)
2328
- wallet = self.wallet_ask(wallet_name, wallet_path, wallet_hotkey)
2329
2506
  self.initialize_chain(network)
2330
- return self._run_command(wallets.check_coldkey_swap(wallet, self.subtensor))
2507
+
2508
+ if show_all:
2509
+ return self._run_command(
2510
+ wallets.check_swap_status(self.subtensor, None, None)
2511
+ )
2512
+
2513
+ if not wallet_ss58_address:
2514
+ wallet_ss58_address = Prompt.ask(
2515
+ "Enter [blue]wallet name[/blue] or [blue]SS58 address[/blue] [dim](leave blank to show all pending swaps)[/dim]"
2516
+ )
2517
+ if not wallet_ss58_address:
2518
+ return self._run_command(
2519
+ wallets.check_swap_status(self.subtensor, None, None)
2520
+ )
2521
+
2522
+ if is_valid_ss58_address(wallet_ss58_address):
2523
+ ss58_address = wallet_ss58_address
2524
+ else:
2525
+ wallet = self.wallet_ask(
2526
+ wallet_ss58_address,
2527
+ wallet_path,
2528
+ wallet_hotkey,
2529
+ ask_for=[WO.NAME, WO.PATH],
2530
+ validate=WV.WALLET,
2531
+ )
2532
+ ss58_address = wallet.coldkeypub.ss58_address
2533
+
2534
+ if not scheduled_block:
2535
+ block_input = Prompt.ask(
2536
+ "[blue]Enter the block number[/blue] where the swap was scheduled [dim](optional, press enter to skip)[/dim]",
2537
+ default="",
2538
+ )
2539
+ if block_input:
2540
+ try:
2541
+ scheduled_block = int(block_input)
2542
+ except ValueError:
2543
+ print_error("Invalid block number")
2544
+ raise typer.Exit()
2545
+
2546
+ return self._run_command(
2547
+ wallets.check_swap_status(self.subtensor, ss58_address, scheduled_block)
2548
+ )
2331
2549
 
2332
2550
  def wallet_create_wallet(
2333
2551
  self,
@@ -2340,6 +2558,7 @@ class CLIManager:
2340
2558
  overwrite: bool = Options.overwrite,
2341
2559
  quiet: bool = Options.quiet,
2342
2560
  verbose: bool = Options.verbose,
2561
+ json_output: bool = Options.json_output,
2343
2562
  ):
2344
2563
  """
2345
2564
  Create a complete wallet by setting up both coldkey and hotkeys.
@@ -2354,6 +2573,7 @@ class CLIManager:
2354
2573
 
2355
2574
  [bold]Note[/bold]: This command is for new users setting up their wallet for the first time, or for those who wish to completely renew their wallet keys. It ensures a fresh start with new keys for secure and effective participation in the Bittensor network.
2356
2575
  """
2576
+ self.verbosity_handler(quiet, verbose, json_output)
2357
2577
  if not wallet_path:
2358
2578
  wallet_path = Prompt.ask(
2359
2579
  "Enter the path of wallets directory", default=defaults.wallet.path
@@ -2370,7 +2590,6 @@ class CLIManager:
2370
2590
  default=defaults.wallet.hotkey,
2371
2591
  )
2372
2592
 
2373
- self.verbosity_handler(quiet, verbose)
2374
2593
  wallet = self.wallet_ask(
2375
2594
  wallet_name,
2376
2595
  wallet_path,
@@ -2381,7 +2600,9 @@ class CLIManager:
2381
2600
  if not uri:
2382
2601
  n_words = get_n_words(n_words)
2383
2602
  return self._run_command(
2384
- wallets.wallet_create(wallet, n_words, use_password, uri, overwrite)
2603
+ wallets.wallet_create(
2604
+ wallet, n_words, use_password, uri, overwrite, json_output
2605
+ )
2385
2606
  )
2386
2607
 
2387
2608
  def wallet_balance(
@@ -2399,6 +2620,7 @@ class CLIManager:
2399
2620
  network: Optional[list[str]] = Options.network,
2400
2621
  quiet: bool = Options.quiet,
2401
2622
  verbose: bool = Options.verbose,
2623
+ json_output: bool = Options.json_output,
2402
2624
  ):
2403
2625
  """
2404
2626
  Check the balance of the wallet. This command shows a detailed view of the wallet's coldkey balances, including free and staked balances.
@@ -2424,7 +2646,7 @@ class CLIManager:
2424
2646
  [green]$[/green] btcli w balance --ss58 <ss58_address> --ss58 <ss58_address>
2425
2647
 
2426
2648
  """
2427
- self.verbosity_handler(quiet, verbose)
2649
+ self.verbosity_handler(quiet, verbose, json_output)
2428
2650
  wallet = None
2429
2651
  if all_balances:
2430
2652
  ask_for = [WO.PATH]
@@ -2487,7 +2709,9 @@ class CLIManager:
2487
2709
  )
2488
2710
  subtensor = self.initialize_chain(network)
2489
2711
  return self._run_command(
2490
- wallets.wallet_balance(wallet, subtensor, all_balances, ss58_addresses)
2712
+ wallets.wallet_balance(
2713
+ wallet, subtensor, all_balances, ss58_addresses, json_output
2714
+ )
2491
2715
  )
2492
2716
 
2493
2717
  def wallet_history(
@@ -2511,6 +2735,7 @@ class CLIManager:
2511
2735
 
2512
2736
  """
2513
2737
  # TODO: Fetch effective network and redirect users accordingly - this only works on finney
2738
+ # TODO: Add json_output if this gets re-enabled
2514
2739
  # no_use_config_str = "Using the network [dark_orange]finney[/dark_orange] and ignoring network/chain configs"
2515
2740
 
2516
2741
  # if self.config.get("network"):
@@ -2577,6 +2802,7 @@ class CLIManager:
2577
2802
  quiet: bool = Options.quiet,
2578
2803
  verbose: bool = Options.verbose,
2579
2804
  prompt: bool = Options.prompt,
2805
+ json_output: bool = Options.json_output,
2580
2806
  ):
2581
2807
  """
2582
2808
  Create or update the on-chain identity of a coldkey or a hotkey on the Bittensor network. [bold]Incurs a 1 TAO transaction fee.[/bold]
@@ -2595,7 +2821,7 @@ class CLIManager:
2595
2821
 
2596
2822
  [bold]Note[/bold]: This command should only be used if the user is willing to incur the a recycle fee associated with setting an identity on the blockchain. It is a high-level command that makes changes to the blockchain state and should not be used programmatically as part of other scripts or applications.
2597
2823
  """
2598
- self.verbosity_handler(quiet, verbose)
2824
+ self.verbosity_handler(quiet, verbose, json_output)
2599
2825
  wallet = self.wallet_ask(
2600
2826
  wallet_name,
2601
2827
  wallet_path,
@@ -2644,6 +2870,7 @@ class CLIManager:
2644
2870
  identity["additional"],
2645
2871
  identity["github_repo"],
2646
2872
  prompt,
2873
+ json_output,
2647
2874
  )
2648
2875
  )
2649
2876
 
@@ -2665,6 +2892,7 @@ class CLIManager:
2665
2892
  network: Optional[list[str]] = Options.network,
2666
2893
  quiet: bool = Options.quiet,
2667
2894
  verbose: bool = Options.verbose,
2895
+ json_output: bool = Options.json_output,
2668
2896
  ):
2669
2897
  """
2670
2898
  Shows the identity details of a user's coldkey or hotkey.
@@ -2683,7 +2911,7 @@ class CLIManager:
2683
2911
 
2684
2912
  [bold]Note[/bold]: This command is primarily used for informational purposes and has no side effects on the blockchain network state.
2685
2913
  """
2686
- wallet = None
2914
+ self.verbosity_handler(quiet, verbose, json_output)
2687
2915
  if not wallet_name:
2688
2916
  if coldkey_ss58:
2689
2917
  if not is_valid_ss58_address(coldkey_ss58):
@@ -2708,9 +2936,8 @@ class CLIManager:
2708
2936
  )
2709
2937
  coldkey_ss58 = wallet.coldkeypub.ss58_address
2710
2938
 
2711
- self.verbosity_handler(quiet, verbose)
2712
2939
  return self._run_command(
2713
- wallets.get_id(self.initialize_chain(network), coldkey_ss58)
2940
+ wallets.get_id(self.initialize_chain(network), coldkey_ss58, json_output)
2714
2941
  )
2715
2942
 
2716
2943
  def wallet_sign(
@@ -2726,6 +2953,7 @@ class CLIManager:
2726
2953
  message: str = typer.Option("", help="The message to encode and sign"),
2727
2954
  quiet: bool = Options.quiet,
2728
2955
  verbose: bool = Options.verbose,
2956
+ json_output: bool = Options.json_output,
2729
2957
  ):
2730
2958
  """
2731
2959
  Allows users to sign a message with the provided wallet or wallet hotkey. Use this command to easily prove your ownership of a coldkey or a hotkey.
@@ -2741,7 +2969,7 @@ class CLIManager:
2741
2969
  [green]$[/green] btcli wallet sign --wallet-name default --wallet-hotkey hotkey --message
2742
2970
  '{"something": "here", "timestamp": 1719908486}'
2743
2971
  """
2744
- self.verbosity_handler(quiet, verbose)
2972
+ self.verbosity_handler(quiet, verbose, json_output)
2745
2973
  if use_hotkey is None:
2746
2974
  use_hotkey = Confirm.ask(
2747
2975
  f"Would you like to sign the transaction using your [{COLORS.G.HK}]hotkey[/{COLORS.G.HK}]?"
@@ -2760,7 +2988,92 @@ class CLIManager:
2760
2988
  if not message:
2761
2989
  message = Prompt.ask("Enter the [blue]message[/blue] to encode and sign")
2762
2990
 
2763
- return self._run_command(wallets.sign(wallet, message, use_hotkey))
2991
+ return self._run_command(wallets.sign(wallet, message, use_hotkey, json_output))
2992
+
2993
+ def wallet_swap_coldkey(
2994
+ self,
2995
+ wallet_name: Optional[str] = Options.wallet_name,
2996
+ wallet_path: Optional[str] = Options.wallet_path,
2997
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2998
+ new_wallet_or_ss58: Optional[str] = typer.Option(
2999
+ None,
3000
+ "--new-coldkey",
3001
+ "--new-coldkey-ss58",
3002
+ "--new-wallet",
3003
+ "--new",
3004
+ help="SS58 address of the new coldkey that will replace the current one.",
3005
+ ),
3006
+ network: Optional[list[str]] = Options.network,
3007
+ quiet: bool = Options.quiet,
3008
+ verbose: bool = Options.verbose,
3009
+ force_swap: bool = typer.Option(
3010
+ False,
3011
+ "--force",
3012
+ "-f",
3013
+ "--force-swap",
3014
+ help="Force the swap even if the new coldkey is already scheduled for a swap.",
3015
+ ),
3016
+ ):
3017
+ """
3018
+ Schedule a coldkey swap for a wallet.
3019
+
3020
+ This command allows you to schedule a coldkey swap for a wallet. You can either provide a new wallet name, or SS58 address.
3021
+
3022
+ EXAMPLES
3023
+
3024
+ [green]$[/green] btcli wallet schedule-coldkey-swap --new-wallet my_new_wallet
3025
+
3026
+ [green]$[/green] btcli wallet schedule-coldkey-swap --new-coldkey-ss58 5Dk...X3q
3027
+ """
3028
+ self.verbosity_handler(quiet, verbose)
3029
+
3030
+ if not wallet_name:
3031
+ wallet_name = Prompt.ask(
3032
+ "Enter the [blue]wallet name[/blue] which you want to swap the coldkey for",
3033
+ default=self.config.get("wallet_name") or defaults.wallet.name,
3034
+ )
3035
+ wallet = self.wallet_ask(
3036
+ wallet_name,
3037
+ wallet_path,
3038
+ wallet_hotkey,
3039
+ ask_for=[WO.NAME],
3040
+ validate=WV.WALLET,
3041
+ )
3042
+ console.print(
3043
+ f"\nWallet selected to swap the [blue]coldkey[/blue] from: \n"
3044
+ f"[dark_sea_green3]{wallet}[/dark_sea_green3]\n"
3045
+ )
3046
+
3047
+ if not new_wallet_or_ss58:
3048
+ new_wallet_or_ss58 = Prompt.ask(
3049
+ "Enter the [blue]new wallet name[/blue] or [blue]SS58 address[/blue] of the new coldkey",
3050
+ )
3051
+
3052
+ if is_valid_ss58_address(new_wallet_or_ss58):
3053
+ new_wallet_coldkey_ss58 = new_wallet_or_ss58
3054
+ else:
3055
+ new_wallet_name = new_wallet_or_ss58
3056
+ new_wallet = self.wallet_ask(
3057
+ new_wallet_name,
3058
+ wallet_path,
3059
+ wallet_hotkey,
3060
+ ask_for=[WO.NAME],
3061
+ validate=WV.WALLET,
3062
+ )
3063
+ console.print(
3064
+ f"\nNew wallet to swap the [blue]coldkey[/blue] to: \n"
3065
+ f"[dark_sea_green3]{new_wallet}[/dark_sea_green3]\n"
3066
+ )
3067
+ new_wallet_coldkey_ss58 = new_wallet.coldkeypub.ss58_address
3068
+
3069
+ return self._run_command(
3070
+ wallets.schedule_coldkey_swap(
3071
+ wallet=wallet,
3072
+ subtensor=self.initialize_chain(network),
3073
+ new_coldkey_ss58=new_wallet_coldkey_ss58,
3074
+ force_swap=force_swap,
3075
+ )
3076
+ )
2764
3077
 
2765
3078
  def stake_list(
2766
3079
  self,
@@ -2780,6 +3093,7 @@ class CLIManager:
2780
3093
  quiet: bool = Options.quiet,
2781
3094
  verbose: bool = Options.verbose,
2782
3095
  no_prompt: bool = Options.prompt,
3096
+ json_output: bool = Options.json_output,
2783
3097
  # TODO add: all-wallets, reuse_last, html_output
2784
3098
  ):
2785
3099
  """
@@ -2801,7 +3115,7 @@ class CLIManager:
2801
3115
  4. Verbose output with full values:
2802
3116
  [green]$[/green] btcli stake list --wallet.name my_wallet --verbose
2803
3117
  """
2804
- self.verbosity_handler(quiet, verbose)
3118
+ self.verbosity_handler(quiet, verbose, json_output)
2805
3119
 
2806
3120
  wallet = None
2807
3121
  if coldkey_ss58:
@@ -2832,6 +3146,7 @@ class CLIManager:
2832
3146
  live,
2833
3147
  verbose,
2834
3148
  no_prompt,
3149
+ json_output,
2835
3150
  )
2836
3151
  )
2837
3152
 
@@ -2875,9 +3190,11 @@ class CLIManager:
2875
3190
  rate_tolerance: Optional[float] = Options.rate_tolerance,
2876
3191
  safe_staking: Optional[bool] = Options.safe_staking,
2877
3192
  allow_partial_stake: Optional[bool] = Options.allow_partial_stake,
3193
+ era: int = Options.era,
2878
3194
  prompt: bool = Options.prompt,
2879
3195
  quiet: bool = Options.quiet,
2880
3196
  verbose: bool = Options.verbose,
3197
+ json_output: bool = Options.json_output,
2881
3198
  ):
2882
3199
  """
2883
3200
  Stake TAO to one or more hotkeys on specific netuids with your coldkey.
@@ -2910,7 +3227,7 @@ class CLIManager:
2910
3227
  • [blue]--partial[/blue]: Complete partial stake if rates exceed tolerance
2911
3228
 
2912
3229
  """
2913
- self.verbosity_handler(quiet, verbose)
3230
+ self.verbosity_handler(quiet, verbose, json_output)
2914
3231
  safe_staking = self.ask_safe_staking(safe_staking)
2915
3232
  if safe_staking:
2916
3233
  rate_tolerance = self.ask_rate_tolerance(rate_tolerance)
@@ -3068,6 +3385,8 @@ class CLIManager:
3068
3385
  safe_staking,
3069
3386
  rate_tolerance,
3070
3387
  allow_partial_stake,
3388
+ json_output,
3389
+ era,
3071
3390
  )
3072
3391
  )
3073
3392
 
@@ -3119,6 +3438,7 @@ class CLIManager:
3119
3438
  rate_tolerance: Optional[float] = Options.rate_tolerance,
3120
3439
  safe_staking: Optional[bool] = Options.safe_staking,
3121
3440
  allow_partial_stake: Optional[bool] = Options.allow_partial_stake,
3441
+ era: int = Options.era,
3122
3442
  prompt: bool = Options.prompt,
3123
3443
  interactive: bool = typer.Option(
3124
3444
  False,
@@ -3128,6 +3448,7 @@ class CLIManager:
3128
3448
  ),
3129
3449
  quiet: bool = Options.quiet,
3130
3450
  verbose: bool = Options.verbose,
3451
+ json_output: bool = Options.json_output,
3131
3452
  ):
3132
3453
  """
3133
3454
  Unstake TAO from one or more hotkeys and transfer them back to the user's coldkey wallet.
@@ -3159,7 +3480,7 @@ class CLIManager:
3159
3480
  • [blue]--tolerance[/blue]: Max allowed rate change (0.05 = 5%)
3160
3481
  • [blue]--partial[/blue]: Complete partial unstake if rates exceed tolerance
3161
3482
  """
3162
- self.verbosity_handler(quiet, verbose)
3483
+ self.verbosity_handler(quiet, verbose, json_output)
3163
3484
  if not unstake_all and not unstake_all_alpha:
3164
3485
  safe_staking = self.ask_safe_staking(safe_staking)
3165
3486
  if safe_staking:
@@ -3171,7 +3492,8 @@ class CLIManager:
3171
3492
  [hotkey_ss58_address, include_hotkeys, exclude_hotkeys, all_hotkeys]
3172
3493
  ):
3173
3494
  print_error(
3174
- "Interactive mode cannot be used with hotkey selection options like --include-hotkeys, --exclude-hotkeys, --all-hotkeys, or --hotkey."
3495
+ "Interactive mode cannot be used with hotkey selection options like "
3496
+ "--include-hotkeys, --exclude-hotkeys, --all-hotkeys, or --hotkey."
3175
3497
  )
3176
3498
  raise typer.Exit()
3177
3499
 
@@ -3308,6 +3630,8 @@ class CLIManager:
3308
3630
  include_hotkeys=include_hotkeys,
3309
3631
  exclude_hotkeys=exclude_hotkeys,
3310
3632
  prompt=prompt,
3633
+ json_output=json_output,
3634
+ era=era,
3311
3635
  )
3312
3636
  )
3313
3637
  elif (
@@ -3362,6 +3686,8 @@ class CLIManager:
3362
3686
  safe_staking=safe_staking,
3363
3687
  rate_tolerance=rate_tolerance,
3364
3688
  allow_partial_stake=allow_partial_stake,
3689
+ json_output=json_output,
3690
+ era=era,
3365
3691
  )
3366
3692
  )
3367
3693
 
@@ -3389,9 +3715,11 @@ class CLIManager:
3389
3715
  stake_all: bool = typer.Option(
3390
3716
  False, "--stake-all", "--all", help="Stake all", prompt=False
3391
3717
  ),
3718
+ era: int = Options.era,
3392
3719
  prompt: bool = Options.prompt,
3393
3720
  quiet: bool = Options.quiet,
3394
3721
  verbose: bool = Options.verbose,
3722
+ json_output: bool = Options.json_output,
3395
3723
  ):
3396
3724
  """
3397
3725
  Move staked TAO between hotkeys while keeping the same coldkey ownership.
@@ -3413,13 +3741,14 @@ class CLIManager:
3413
3741
 
3414
3742
  [green]$[/green] btcli stake move
3415
3743
  """
3416
- self.verbosity_handler(quiet, verbose)
3744
+ self.verbosity_handler(quiet, verbose, json_output)
3417
3745
  console.print(
3418
3746
  "[dim]This command moves stake from one hotkey to another hotkey while keeping the same coldkey.[/dim]"
3419
3747
  )
3420
3748
  if not destination_hotkey:
3421
3749
  dest_wallet_or_ss58 = Prompt.ask(
3422
- "Enter the [blue]destination wallet[/blue] where destination hotkey is located or [blue]ss58 address[/blue]"
3750
+ "Enter the [blue]destination wallet[/blue] where destination hotkey is located or "
3751
+ "[blue]ss58 address[/blue]"
3423
3752
  )
3424
3753
  if is_valid_ss58_address(dest_wallet_or_ss58):
3425
3754
  destination_hotkey = dest_wallet_or_ss58
@@ -3506,7 +3835,7 @@ class CLIManager:
3506
3835
  "Enter the [blue]destination subnet[/blue] (netuid) to move stake to"
3507
3836
  )
3508
3837
 
3509
- return self._run_command(
3838
+ result = self._run_command(
3510
3839
  move_stake.move_stake(
3511
3840
  subtensor=self.initialize_chain(network),
3512
3841
  wallet=wallet,
@@ -3516,10 +3845,14 @@ class CLIManager:
3516
3845
  destination_hotkey=destination_hotkey,
3517
3846
  amount=amount,
3518
3847
  stake_all=stake_all,
3848
+ era=era,
3519
3849
  interactive_selection=interactive_selection,
3520
3850
  prompt=prompt,
3521
3851
  )
3522
3852
  )
3853
+ if json_output:
3854
+ json_console.print(json.dumps({"success": result}))
3855
+ return result
3523
3856
 
3524
3857
  def stake_transfer(
3525
3858
  self,
@@ -3553,9 +3886,11 @@ class CLIManager:
3553
3886
  stake_all: bool = typer.Option(
3554
3887
  False, "--stake-all", "--all", help="Stake all", prompt=False
3555
3888
  ),
3889
+ era: int = Options.era,
3556
3890
  prompt: bool = Options.prompt,
3557
3891
  quiet: bool = Options.quiet,
3558
3892
  verbose: bool = Options.verbose,
3893
+ json_output: bool = Options.json_output,
3559
3894
  ):
3560
3895
  """
3561
3896
  Transfer stake between coldkeys while keeping the same hotkey ownership.
@@ -3589,10 +3924,10 @@ class CLIManager:
3589
3924
  Transfer all available stake from origin hotkey:
3590
3925
  [green]$[/green] btcli stake transfer --all --origin-netuid 1 --dest-netuid 2
3591
3926
  """
3927
+ self.verbosity_handler(quiet, verbose, json_output)
3592
3928
  console.print(
3593
3929
  "[dim]This command transfers stake from one coldkey to another while keeping the same hotkey.[/dim]"
3594
3930
  )
3595
- self.verbosity_handler(quiet, verbose)
3596
3931
 
3597
3932
  if not dest_ss58:
3598
3933
  dest_ss58 = Prompt.ask(
@@ -3664,7 +3999,7 @@ class CLIManager:
3664
3999
  "Enter the [blue]destination subnet[/blue] (netuid)"
3665
4000
  )
3666
4001
 
3667
- return self._run_command(
4002
+ result = self._run_command(
3668
4003
  move_stake.transfer_stake(
3669
4004
  wallet=wallet,
3670
4005
  subtensor=self.initialize_chain(network),
@@ -3673,11 +4008,15 @@ class CLIManager:
3673
4008
  dest_netuid=dest_netuid,
3674
4009
  dest_coldkey_ss58=dest_ss58,
3675
4010
  amount=amount,
4011
+ era=era,
3676
4012
  interactive_selection=interactive_selection,
3677
4013
  stake_all=stake_all,
3678
4014
  prompt=prompt,
3679
4015
  )
3680
4016
  )
4017
+ if json_output:
4018
+ json_console.print(json.dumps({"success": result}))
4019
+ return result
3681
4020
 
3682
4021
  def stake_swap(
3683
4022
  self,
@@ -3711,11 +4050,13 @@ class CLIManager:
3711
4050
  "--all",
3712
4051
  help="Swap all available stake",
3713
4052
  ),
4053
+ era: int = Options.era,
3714
4054
  prompt: bool = Options.prompt,
3715
4055
  wait_for_inclusion: bool = Options.wait_for_inclusion,
3716
4056
  wait_for_finalization: bool = Options.wait_for_finalization,
3717
4057
  quiet: bool = Options.quiet,
3718
4058
  verbose: bool = Options.verbose,
4059
+ json_output: bool = Options.json_output,
3719
4060
  ):
3720
4061
  """
3721
4062
  Swap stake between different subnets while keeping the same coldkey-hotkey pair ownership.
@@ -3737,10 +4078,11 @@ class CLIManager:
3737
4078
  Swap 100 TAO from subnet 1 to subnet 2:
3738
4079
  [green]$[/green] btcli stake swap --wallet-name default --wallet-hotkey default --origin-netuid 1 --dest-netuid 2 --amount 100
3739
4080
  """
4081
+ self.verbosity_handler(quiet, verbose, json_output)
3740
4082
  console.print(
3741
- "[dim]This command moves stake from one subnet to another subnet while keeping the same coldkey-hotkey pair.[/dim]"
4083
+ "[dim]This command moves stake from one subnet to another subnet while keeping "
4084
+ "the same coldkey-hotkey pair.[/dim]"
3742
4085
  )
3743
- self.verbosity_handler(quiet, verbose)
3744
4086
 
3745
4087
  wallet = self.wallet_ask(
3746
4088
  wallet_name,
@@ -3765,7 +4107,7 @@ class CLIManager:
3765
4107
  if not amount and not swap_all:
3766
4108
  amount = FloatPrompt.ask("Enter the [blue]amount[/blue] to swap")
3767
4109
 
3768
- return self._run_command(
4110
+ result = self._run_command(
3769
4111
  move_stake.swap_stake(
3770
4112
  wallet=wallet,
3771
4113
  subtensor=self.initialize_chain(network),
@@ -3773,12 +4115,16 @@ class CLIManager:
3773
4115
  destination_netuid=dest_netuid,
3774
4116
  amount=amount,
3775
4117
  swap_all=swap_all,
4118
+ era=era,
3776
4119
  interactive_selection=interactive_selection,
3777
4120
  prompt=prompt,
3778
4121
  wait_for_inclusion=wait_for_inclusion,
3779
4122
  wait_for_finalization=wait_for_finalization,
3780
4123
  )
3781
4124
  )
4125
+ if json_output:
4126
+ json_console.print(json.dumps({"success": result}))
4127
+ return result
3782
4128
 
3783
4129
  def stake_get_children(
3784
4130
  self,
@@ -3800,6 +4146,7 @@ class CLIManager:
3800
4146
  ),
3801
4147
  quiet: bool = Options.quiet,
3802
4148
  verbose: bool = Options.verbose,
4149
+ json_output: bool = Options.json_output,
3803
4150
  ):
3804
4151
  """
3805
4152
  Get all the child hotkeys on a specified subnet.
@@ -3811,7 +4158,7 @@ class CLIManager:
3811
4158
  [green]$[/green] btcli stake child get --netuid 1
3812
4159
  [green]$[/green] btcli stake child get --all-netuids
3813
4160
  """
3814
- self.verbosity_handler(quiet, verbose)
4161
+ self.verbosity_handler(quiet, verbose, json_output)
3815
4162
  wallet = self.wallet_ask(
3816
4163
  wallet_name,
3817
4164
  wallet_path,
@@ -3832,11 +4179,14 @@ class CLIManager:
3832
4179
  "Enter a netuid (leave blank for all)", default=None, show_default=True
3833
4180
  )
3834
4181
 
3835
- return self._run_command(
4182
+ result = self._run_command(
3836
4183
  children_hotkeys.get_children(
3837
4184
  wallet, self.initialize_chain(network), netuid
3838
4185
  )
3839
4186
  )
4187
+ if json_output:
4188
+ json_console.print(json.dumps(result))
4189
+ return result
3840
4190
 
3841
4191
  def stake_set_children(
3842
4192
  self,
@@ -3861,6 +4211,7 @@ class CLIManager:
3861
4211
  quiet: bool = Options.quiet,
3862
4212
  verbose: bool = Options.verbose,
3863
4213
  prompt: bool = Options.prompt,
4214
+ json_output: bool = Options.json_output,
3864
4215
  ):
3865
4216
  """
3866
4217
  Set child hotkeys on a specified subnet (or all). Overrides currently set children.
@@ -3873,7 +4224,7 @@ class CLIManager:
3873
4224
 
3874
4225
  [green]$[/green] btcli stake child set -c 5FCL3gmjtQV4xxxxuEPEFQVhyyyyqYgNwX7drFLw7MSdBnxP -c 5Hp5dxxxxtGg7pu8dN2btyyyyVA1vELmM9dy8KQv3LxV8PA7 --hotkey default --netuid 1 -p 0.3 -p 0.7
3875
4226
  """
3876
- self.verbosity_handler(quiet, verbose)
4227
+ self.verbosity_handler(quiet, verbose, json_output)
3877
4228
  netuid = get_optional_netuid(netuid, all_netuids)
3878
4229
 
3879
4230
  children = list_prompt(
@@ -3885,7 +4236,8 @@ class CLIManager:
3885
4236
  proportions = list_prompt(
3886
4237
  proportions,
3887
4238
  float,
3888
- "Enter comma-separated proportions equal to the number of children (sum not exceeding a total of 1.0)",
4239
+ "Enter comma-separated proportions equal to the number of children "
4240
+ "(sum not exceeding a total of 1.0)",
3889
4241
  )
3890
4242
 
3891
4243
  if len(proportions) != len(children):
@@ -3913,6 +4265,7 @@ class CLIManager:
3913
4265
  wait_for_finalization=wait_for_finalization,
3914
4266
  wait_for_inclusion=wait_for_inclusion,
3915
4267
  prompt=prompt,
4268
+ json_output=json_output,
3916
4269
  )
3917
4270
  )
3918
4271
 
@@ -3939,6 +4292,7 @@ class CLIManager:
3939
4292
  quiet: bool = Options.quiet,
3940
4293
  verbose: bool = Options.verbose,
3941
4294
  prompt: bool = Options.prompt,
4295
+ json_output: bool = Options.json_output,
3942
4296
  ):
3943
4297
  """
3944
4298
  Remove all children hotkeys on a specified subnet (or all).
@@ -3949,7 +4303,7 @@ class CLIManager:
3949
4303
 
3950
4304
  [green]$[/green] btcli stake child revoke --hotkey <parent_hotkey> --netuid 1
3951
4305
  """
3952
- self.verbosity_handler(quiet, verbose)
4306
+ self.verbosity_handler(quiet, verbose, json_output)
3953
4307
  wallet = self.wallet_ask(
3954
4308
  wallet_name,
3955
4309
  wallet_path,
@@ -3974,6 +4328,7 @@ class CLIManager:
3974
4328
  wait_for_inclusion,
3975
4329
  wait_for_finalization,
3976
4330
  prompt=prompt,
4331
+ json_output=json_output,
3977
4332
  )
3978
4333
  )
3979
4334
 
@@ -4000,7 +4355,8 @@ class CLIManager:
4000
4355
  None,
4001
4356
  "--take",
4002
4357
  "-t",
4003
- help="Use to set the take value for your child hotkey. When not used, the command will fetch the current take value.",
4358
+ help="Use to set the take value for your child hotkey. When not used, the command will fetch the current "
4359
+ "take value.",
4004
4360
  prompt=False,
4005
4361
  ),
4006
4362
  wait_for_inclusion: bool = Options.wait_for_inclusion,
@@ -4008,6 +4364,7 @@ class CLIManager:
4008
4364
  prompt: bool = Options.prompt,
4009
4365
  quiet: bool = Options.quiet,
4010
4366
  verbose: bool = Options.verbose,
4367
+ json_output: bool = Options.json_output,
4011
4368
  ):
4012
4369
  """
4013
4370
  Get and set your child hotkey take on a specified subnet.
@@ -4024,7 +4381,7 @@ class CLIManager:
4024
4381
 
4025
4382
  [green]$[/green] btcli stake child take --hotkey <child_hotkey> --take 0.12 --netuid 1
4026
4383
  """
4027
- self.verbosity_handler(quiet, verbose)
4384
+ self.verbosity_handler(quiet, verbose, json_output)
4028
4385
  wallet = self.wallet_ask(
4029
4386
  wallet_name,
4030
4387
  wallet_path,
@@ -4041,7 +4398,7 @@ class CLIManager:
4041
4398
  netuid = IntPrompt.ask(
4042
4399
  "Enter netuid (leave blank for all)", default=None, show_default=True
4043
4400
  )
4044
- return self._run_command(
4401
+ results: list[tuple[Optional[int], bool]] = self._run_command(
4045
4402
  children_hotkeys.childkey_take(
4046
4403
  wallet=wallet,
4047
4404
  subtensor=self.initialize_chain(network),
@@ -4053,6 +4410,12 @@ class CLIManager:
4053
4410
  prompt=prompt,
4054
4411
  )
4055
4412
  )
4413
+ if json_output:
4414
+ output = {}
4415
+ for netuid_, success in results:
4416
+ output[netuid_] = success
4417
+ json_console.print(json.dumps(output))
4418
+ return results
4056
4419
 
4057
4420
  def sudo_set(
4058
4421
  self,
@@ -4069,6 +4432,7 @@ class CLIManager:
4069
4432
  ),
4070
4433
  quiet: bool = Options.quiet,
4071
4434
  verbose: bool = Options.verbose,
4435
+ json_output: bool = Options.json_output,
4072
4436
  ):
4073
4437
  """
4074
4438
  Used to set hyperparameters for a specific subnet.
@@ -4079,7 +4443,7 @@ class CLIManager:
4079
4443
 
4080
4444
  [green]$[/green] btcli sudo set --netuid 1 --param tempo --value 400
4081
4445
  """
4082
- self.verbosity_handler(quiet, verbose)
4446
+ self.verbosity_handler(quiet, verbose, json_output)
4083
4447
 
4084
4448
  if not param_name or not param_value:
4085
4449
  hyperparams = self._run_command(
@@ -4124,15 +4488,19 @@ class CLIManager:
4124
4488
  wallet = self.wallet_ask(
4125
4489
  wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
4126
4490
  )
4127
- return self._run_command(
4491
+ result = self._run_command(
4128
4492
  sudo.sudo_set_hyperparameter(
4129
4493
  wallet,
4130
4494
  self.initialize_chain(network),
4131
4495
  netuid,
4132
4496
  param_name,
4133
4497
  param_value,
4498
+ json_output,
4134
4499
  )
4135
4500
  )
4501
+ if json_output:
4502
+ json_console.print(json.dumps({"success": result}))
4503
+ return result
4136
4504
 
4137
4505
  def sudo_get(
4138
4506
  self,
@@ -4140,6 +4508,7 @@ class CLIManager:
4140
4508
  netuid: int = Options.netuid,
4141
4509
  quiet: bool = Options.quiet,
4142
4510
  verbose: bool = Options.verbose,
4511
+ json_output: bool = Options.json_output,
4143
4512
  ):
4144
4513
  """
4145
4514
  Shows a list of the hyperparameters for the specified subnet.
@@ -4148,9 +4517,11 @@ class CLIManager:
4148
4517
 
4149
4518
  [green]$[/green] btcli sudo get --netuid 1
4150
4519
  """
4151
- self.verbosity_handler(quiet, verbose)
4520
+ self.verbosity_handler(quiet, verbose, json_output)
4152
4521
  return self._run_command(
4153
- sudo.get_hyperparameters(self.initialize_chain(network), netuid)
4522
+ sudo.get_hyperparameters(
4523
+ self.initialize_chain(network), netuid, json_output
4524
+ )
4154
4525
  )
4155
4526
 
4156
4527
  def sudo_senate(
@@ -4158,6 +4529,7 @@ class CLIManager:
4158
4529
  network: Optional[list[str]] = Options.network,
4159
4530
  quiet: bool = Options.quiet,
4160
4531
  verbose: bool = Options.verbose,
4532
+ json_output: bool = Options.json_output,
4161
4533
  ):
4162
4534
  """
4163
4535
  Shows the Senate members of the Bittensor's governance protocol.
@@ -4167,14 +4539,17 @@ class CLIManager:
4167
4539
  EXAMPLE
4168
4540
  [green]$[/green] btcli sudo senate
4169
4541
  """
4170
- self.verbosity_handler(quiet, verbose)
4171
- return self._run_command(sudo.get_senate(self.initialize_chain(network)))
4542
+ self.verbosity_handler(quiet, verbose, json_output)
4543
+ return self._run_command(
4544
+ sudo.get_senate(self.initialize_chain(network), json_output)
4545
+ )
4172
4546
 
4173
4547
  def sudo_proposals(
4174
4548
  self,
4175
4549
  network: Optional[list[str]] = Options.network,
4176
4550
  quiet: bool = Options.quiet,
4177
4551
  verbose: bool = Options.verbose,
4552
+ json_output: bool = Options.json_output,
4178
4553
  ):
4179
4554
  """
4180
4555
  View active proposals for the senate in the Bittensor's governance protocol.
@@ -4184,9 +4559,9 @@ class CLIManager:
4184
4559
  EXAMPLE
4185
4560
  [green]$[/green] btcli sudo proposals
4186
4561
  """
4187
- self.verbosity_handler(quiet, verbose)
4562
+ self.verbosity_handler(quiet, verbose, json_output)
4188
4563
  return self._run_command(
4189
- sudo.proposals(self.initialize_chain(network), verbose)
4564
+ sudo.proposals(self.initialize_chain(network), verbose, json_output)
4190
4565
  )
4191
4566
 
4192
4567
  def sudo_senate_vote(
@@ -4223,6 +4598,7 @@ class CLIManager:
4223
4598
  EXAMPLE
4224
4599
  [green]$[/green] btcli sudo senate_vote --proposal <proposal_hash>
4225
4600
  """
4601
+ # TODO discuss whether this should receive json_output. I don't think it should.
4226
4602
  self.verbosity_handler(quiet, verbose)
4227
4603
  wallet = self.wallet_ask(
4228
4604
  wallet_name,
@@ -4246,6 +4622,7 @@ class CLIManager:
4246
4622
  take: float = typer.Option(None, help="The new take value."),
4247
4623
  quiet: bool = Options.quiet,
4248
4624
  verbose: bool = Options.verbose,
4625
+ json_output: bool = Options.json_output,
4249
4626
  ):
4250
4627
  """
4251
4628
  Allows users to change their delegate take percentage.
@@ -4258,7 +4635,7 @@ class CLIManager:
4258
4635
  """
4259
4636
  max_value = 0.18
4260
4637
  min_value = 0.00
4261
- self.verbosity_handler(quiet, verbose)
4638
+ self.verbosity_handler(quiet, verbose, json_output)
4262
4639
 
4263
4640
  wallet = self.wallet_ask(
4264
4641
  wallet_name,
@@ -4283,9 +4660,12 @@ class CLIManager:
4283
4660
  )
4284
4661
  raise typer.Exit()
4285
4662
 
4286
- return self._run_command(
4663
+ result = self._run_command(
4287
4664
  sudo.set_take(wallet, self.initialize_chain(network), take)
4288
4665
  )
4666
+ if json_output:
4667
+ json_console.print(json.dumps({"success": result}))
4668
+ return result
4289
4669
 
4290
4670
  def sudo_get_take(
4291
4671
  self,
@@ -4295,6 +4675,7 @@ class CLIManager:
4295
4675
  wallet_hotkey: Optional[str] = Options.wallet_hotkey,
4296
4676
  quiet: bool = Options.quiet,
4297
4677
  verbose: bool = Options.verbose,
4678
+ json_output: bool = Options.json_output,
4298
4679
  ):
4299
4680
  """
4300
4681
  Allows users to check their delegate take percentage.
@@ -4304,7 +4685,7 @@ class CLIManager:
4304
4685
  EXAMPLE
4305
4686
  [green]$[/green] btcli sudo get-take --wallet-name my_wallet --wallet-hotkey my_hotkey
4306
4687
  """
4307
- self.verbosity_handler(quiet, verbose)
4688
+ self.verbosity_handler(quiet, verbose, json_output)
4308
4689
 
4309
4690
  wallet = self.wallet_ask(
4310
4691
  wallet_name,
@@ -4313,10 +4694,15 @@ class CLIManager:
4313
4694
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
4314
4695
  validate=WV.WALLET_AND_HOTKEY,
4315
4696
  )
4316
-
4317
- self._run_command(
4318
- sudo.display_current_take(self.initialize_chain(network), wallet)
4319
- )
4697
+ if json_output:
4698
+ result = self._run_command(
4699
+ sudo.get_current_take(self.initialize_chain(network), wallet)
4700
+ )
4701
+ json_console.print(json.dumps({"current_take": result}))
4702
+ else:
4703
+ self._run_command(
4704
+ sudo.display_current_take(self.initialize_chain(network), wallet)
4705
+ )
4320
4706
 
4321
4707
  def subnets_list(
4322
4708
  self,
@@ -4324,6 +4710,7 @@ class CLIManager:
4324
4710
  quiet: bool = Options.quiet,
4325
4711
  verbose: bool = Options.verbose,
4326
4712
  live_mode: bool = Options.live,
4713
+ json_output: bool = Options.json_output,
4327
4714
  ):
4328
4715
  """
4329
4716
  List all subnets and their detailed information.
@@ -4351,7 +4738,10 @@ class CLIManager:
4351
4738
 
4352
4739
  [green]$[/green] btcli subnets list
4353
4740
  """
4354
- self.verbosity_handler(quiet, verbose)
4741
+ if json_output and live_mode:
4742
+ print_error("Cannot use `--json-output` and `--live` at the same time.")
4743
+ return
4744
+ self.verbosity_handler(quiet, verbose, json_output)
4355
4745
  subtensor = self.initialize_chain(network)
4356
4746
  return self._run_command(
4357
4747
  subnets.subnets_list(
@@ -4361,6 +4751,7 @@ class CLIManager:
4361
4751
  not self.config.get("use_cache", True),
4362
4752
  verbose,
4363
4753
  live_mode,
4754
+ json_output,
4364
4755
  )
4365
4756
  )
4366
4757
 
@@ -4393,6 +4784,9 @@ class CLIManager:
4393
4784
  help="Show the price in log scale.",
4394
4785
  ),
4395
4786
  html_output: bool = Options.html_output,
4787
+ quiet: bool = Options.quiet,
4788
+ verbose: bool = Options.verbose,
4789
+ json_output: bool = Options.json_output,
4396
4790
  ):
4397
4791
  """
4398
4792
  Shows the historical price of a subnet for the past 24 hours.
@@ -4410,6 +4804,10 @@ class CLIManager:
4410
4804
  [green]$[/green] btcli subnets price --all --html
4411
4805
  [green]$[/green] btcli subnets price --netuids 1,2,3,4 --html
4412
4806
  """
4807
+ if json_output and html_output:
4808
+ print_error("Cannot specify both `--json-output` and `--html`")
4809
+ return
4810
+ self.verbosity_handler(quiet=quiet, verbose=verbose, json_output=json_output)
4413
4811
  if netuids:
4414
4812
  netuids = parse_to_list(
4415
4813
  netuids,
@@ -4418,15 +4816,15 @@ class CLIManager:
4418
4816
  )
4419
4817
  if all_netuids and netuids:
4420
4818
  print_error("Cannot specify both --netuid and --all-netuids")
4421
- raise typer.Exit()
4819
+ return
4422
4820
 
4423
4821
  if not netuids and not all_netuids:
4424
4822
  netuids = Prompt.ask(
4425
- "Enter the [blue]netuid(s)[/blue] to view the price of in comma-separated format [dim](or Press Enter to view all subnets)[/dim]",
4823
+ "Enter the [blue]netuid(s)[/blue] to view the price of in comma-separated format [dim]"
4824
+ "(or Press Enter to view all subnets)[/dim]",
4426
4825
  )
4427
4826
  if not netuids:
4428
4827
  all_netuids = True
4429
- html_output = True
4430
4828
  else:
4431
4829
  netuids = parse_to_list(
4432
4830
  netuids,
@@ -4434,7 +4832,7 @@ class CLIManager:
4434
4832
  "Netuids must be a comma-separated list of ints, e.g., `--netuids 1,2,3,4`.",
4435
4833
  )
4436
4834
 
4437
- if all_netuids:
4835
+ if all_netuids and not json_output:
4438
4836
  html_output = True
4439
4837
 
4440
4838
  if html_output and is_linux():
@@ -4448,6 +4846,7 @@ class CLIManager:
4448
4846
  interval_hours,
4449
4847
  html_output,
4450
4848
  log_scale,
4849
+ json_output,
4451
4850
  )
4452
4851
  )
4453
4852
 
@@ -4463,6 +4862,7 @@ class CLIManager:
4463
4862
  quiet: bool = Options.quiet,
4464
4863
  verbose: bool = Options.verbose,
4465
4864
  prompt: bool = Options.prompt,
4865
+ json_output: bool = Options.json_output,
4466
4866
  ):
4467
4867
  """
4468
4868
  Displays detailed information about a subnet including participants and their state.
@@ -4471,7 +4871,7 @@ class CLIManager:
4471
4871
 
4472
4872
  [green]$[/green] btcli subnets list
4473
4873
  """
4474
- self.verbosity_handler(quiet, verbose)
4874
+ self.verbosity_handler(quiet, verbose, json_output)
4475
4875
  subtensor = self.initialize_chain(network)
4476
4876
  return self._run_command(
4477
4877
  subnets.show(
@@ -4482,6 +4882,7 @@ class CLIManager:
4482
4882
  delegate_selection=False,
4483
4883
  verbose=verbose,
4484
4884
  prompt=prompt,
4885
+ json_output=json_output,
4485
4886
  )
4486
4887
  )
4487
4888
 
@@ -4490,6 +4891,7 @@ class CLIManager:
4490
4891
  network: Optional[list[str]] = Options.network,
4491
4892
  quiet: bool = Options.quiet,
4492
4893
  verbose: bool = Options.verbose,
4894
+ json_output: bool = Options.json_output,
4493
4895
  ):
4494
4896
  """
4495
4897
  Shows the required amount of TAO to be recycled for creating a new subnet, i.e., cost of registering a new subnet.
@@ -4500,8 +4902,10 @@ class CLIManager:
4500
4902
 
4501
4903
  [green]$[/green] btcli subnets burn_cost
4502
4904
  """
4503
- self.verbosity_handler(quiet, verbose)
4504
- return self._run_command(subnets.burn_cost(self.initialize_chain(network)))
4905
+ self.verbosity_handler(quiet, verbose, json_output)
4906
+ return self._run_command(
4907
+ subnets.burn_cost(self.initialize_chain(network), json_output)
4908
+ )
4505
4909
 
4506
4910
  def subnets_create(
4507
4911
  self,
@@ -4510,7 +4914,7 @@ class CLIManager:
4510
4914
  wallet_hotkey: str = Options.wallet_hotkey,
4511
4915
  network: Optional[list[str]] = Options.network,
4512
4916
  subnet_name: Optional[str] = typer.Option(
4513
- None, "--subnet-name", "--name", help="Name of the subnet"
4917
+ None, "--subnet-name", help="Name of the subnet"
4514
4918
  ),
4515
4919
  github_repo: Optional[str] = typer.Option(
4516
4920
  None, "--github-repo", "--repo", help="GitHub repository URL"
@@ -4534,6 +4938,7 @@ class CLIManager:
4534
4938
  additional_info: Optional[str] = typer.Option(
4535
4939
  None, "--additional-info", help="Additional information"
4536
4940
  ),
4941
+ json_output: bool = Options.json_output,
4537
4942
  prompt: bool = Options.prompt,
4538
4943
  quiet: bool = Options.quiet,
4539
4944
  verbose: bool = Options.verbose,
@@ -4552,7 +4957,7 @@ class CLIManager:
4552
4957
  2. Create with GitHub repo and contact email:
4553
4958
  [green]$[/green] btcli subnets create --subnet-name MySubnet --github-repo https://github.com/myorg/mysubnet --subnet-contact team@mysubnet.net
4554
4959
  """
4555
- self.verbosity_handler(quiet, verbose)
4960
+ self.verbosity_handler(quiet, verbose, json_output)
4556
4961
  wallet = self.wallet_ask(
4557
4962
  wallet_name,
4558
4963
  wallet_path,
@@ -4574,27 +4979,75 @@ class CLIManager:
4574
4979
  description=description,
4575
4980
  additional=additional_info,
4576
4981
  )
4577
- success = self._run_command(
4578
- subnets.create(wallet, self.initialize_chain(network), identity, prompt),
4579
- exit_early=False,
4982
+ self._run_command(
4983
+ subnets.create(
4984
+ wallet, self.initialize_chain(network), identity, json_output, prompt
4985
+ )
4580
4986
  )
4581
4987
 
4582
- if success and prompt:
4583
- set_id = Confirm.ask(
4584
- "[dark_sea_green3]Do you want to set/update your identity?",
4585
- default=False,
4586
- show_default=True,
4587
- )
4588
- if set_id:
4589
- self.wallet_set_id(
4590
- wallet_name=wallet.name,
4591
- wallet_hotkey=wallet.hotkey,
4592
- wallet_path=wallet.path,
4593
- network=network,
4594
- prompt=prompt,
4595
- quiet=quiet,
4596
- verbose=verbose,
4597
- )
4988
+ def subnets_check_start(
4989
+ self,
4990
+ network: Optional[list[str]] = Options.network,
4991
+ netuid: int = Options.netuid,
4992
+ quiet: bool = Options.quiet,
4993
+ verbose: bool = Options.verbose,
4994
+ ):
4995
+ """
4996
+ Checks if a subnet's emission schedule can be started.
4997
+
4998
+ This command verifies if a subnet's emission schedule can be started based on the subnet's registration block.
4999
+
5000
+ Example:
5001
+ [green]$[/green] btcli subnets check_start --netuid 1
5002
+ """
5003
+ self.verbosity_handler(quiet, verbose)
5004
+ return self._run_command(
5005
+ subnets.get_start_schedule(self.initialize_chain(network), netuid)
5006
+ )
5007
+
5008
+ def subnets_start(
5009
+ self,
5010
+ wallet_name: str = Options.wallet_name,
5011
+ wallet_path: str = Options.wallet_path,
5012
+ wallet_hotkey: str = Options.wallet_hotkey,
5013
+ network: Optional[list[str]] = Options.network,
5014
+ netuid: int = Options.netuid,
5015
+ prompt: bool = Options.prompt,
5016
+ quiet: bool = Options.quiet,
5017
+ verbose: bool = Options.verbose,
5018
+ ):
5019
+ """
5020
+ Starts a subnet's emission schedule.
5021
+
5022
+ The owner of the subnet must call this command to start the emission schedule.
5023
+
5024
+ Example:
5025
+ [green]$[/green] btcli subnets start --netuid 1
5026
+ [green]$[/green] btcli subnets start --netuid 1 --wallet-name alice
5027
+ """
5028
+ self.verbosity_handler(quiet, verbose)
5029
+ if not wallet_name:
5030
+ wallet_name = Prompt.ask(
5031
+ "Enter the [blue]wallet name[/blue] [dim](which you used to create the subnet)[/dim]",
5032
+ default=self.config.get("wallet_name") or defaults.wallet.name,
5033
+ )
5034
+ wallet = self.wallet_ask(
5035
+ wallet_name,
5036
+ wallet_path,
5037
+ wallet_hotkey,
5038
+ ask_for=[
5039
+ WO.NAME,
5040
+ ],
5041
+ validate=WV.WALLET,
5042
+ )
5043
+ return self._run_command(
5044
+ subnets.start_subnet(
5045
+ wallet,
5046
+ self.initialize_chain(network),
5047
+ netuid,
5048
+ prompt,
5049
+ )
5050
+ )
4598
5051
 
4599
5052
  def subnets_get_identity(
4600
5053
  self,
@@ -4602,6 +5055,7 @@ class CLIManager:
4602
5055
  netuid: int = Options.netuid,
4603
5056
  quiet: bool = Options.quiet,
4604
5057
  verbose: bool = Options.verbose,
5058
+ json_output: bool = Options.json_output,
4605
5059
  ):
4606
5060
  """
4607
5061
  Get the identity information for a subnet.
@@ -4610,11 +5064,10 @@ class CLIManager:
4610
5064
 
4611
5065
  [green]$[/green] btcli subnets get-identity --netuid 1
4612
5066
  """
4613
- self.verbosity_handler(quiet, verbose)
5067
+ self.verbosity_handler(quiet, verbose, json_output)
4614
5068
  return self._run_command(
4615
5069
  subnets.get_identity(
4616
- self.initialize_chain(network),
4617
- netuid,
5070
+ self.initialize_chain(network), netuid, json_output=json_output
4618
5071
  )
4619
5072
  )
4620
5073
 
@@ -4650,6 +5103,7 @@ class CLIManager:
4650
5103
  additional_info: Optional[str] = typer.Option(
4651
5104
  None, "--additional-info", help="Additional information"
4652
5105
  ),
5106
+ json_output: bool = Options.json_output,
4653
5107
  prompt: bool = Options.prompt,
4654
5108
  quiet: bool = Options.quiet,
4655
5109
  verbose: bool = Options.verbose,
@@ -4667,7 +5121,7 @@ class CLIManager:
4667
5121
  2. Set subnet identity with specific values:
4668
5122
  [green]$[/green] btcli subnets set-identity --netuid 1 --subnet-name MySubnet --github-repo https://github.com/myorg/mysubnet --subnet-contact team@mysubnet.net
4669
5123
  """
4670
- self.verbosity_handler(quiet, verbose)
5124
+ self.verbosity_handler(quiet, verbose, json_output)
4671
5125
  wallet = self.wallet_ask(
4672
5126
  wallet_name,
4673
5127
  wallet_path,
@@ -4685,7 +5139,9 @@ class CLIManager:
4685
5139
  exit_early=False,
4686
5140
  )
4687
5141
  if current_identity is None:
4688
- raise typer.Exit()
5142
+ if json_output:
5143
+ json_console.print('{"success": false}')
5144
+ return
4689
5145
 
4690
5146
  identity = prompt_for_subnet_identity(
4691
5147
  current_identity=current_identity,
@@ -4698,15 +5154,13 @@ class CLIManager:
4698
5154
  additional=additional_info,
4699
5155
  )
4700
5156
 
4701
- return self._run_command(
5157
+ success = self._run_command(
4702
5158
  subnets.set_identity(
4703
- wallet,
4704
- self.initialize_chain(network),
4705
- netuid,
4706
- identity,
4707
- prompt,
5159
+ wallet, self.initialize_chain(network), netuid, identity, prompt
4708
5160
  )
4709
5161
  )
5162
+ if json_output:
5163
+ json_console.print(json.dumps({"success": success}))
4710
5164
 
4711
5165
  def subnets_pow_register(
4712
5166
  self,
@@ -4804,6 +5258,14 @@ class CLIManager:
4804
5258
  wallet_hotkey: str = Options.wallet_hotkey,
4805
5259
  network: Optional[list[str]] = Options.network,
4806
5260
  netuid: int = Options.netuid,
5261
+ era: Optional[
5262
+ int
5263
+ ] = typer.Option( # Should not be Options.era bc this needs to be an Optional[int]
5264
+ None,
5265
+ help="Length (in blocks) for which the transaction should be valid. Note that it is possible that if you "
5266
+ "use an era for this transaction that you may pay a different fee to register than the one stated.",
5267
+ ),
5268
+ json_output: bool = Options.json_output,
4807
5269
  prompt: bool = Options.prompt,
4808
5270
  quiet: bool = Options.quiet,
4809
5271
  verbose: bool = Options.verbose,
@@ -4819,7 +5281,7 @@ class CLIManager:
4819
5281
 
4820
5282
  [green]$[/green] btcli subnets register --netuid 1
4821
5283
  """
4822
- self.verbosity_handler(quiet, verbose)
5284
+ self.verbosity_handler(quiet, verbose, json_output)
4823
5285
  wallet = self.wallet_ask(
4824
5286
  wallet_name,
4825
5287
  wallet_path,
@@ -4832,6 +5294,8 @@ class CLIManager:
4832
5294
  wallet,
4833
5295
  self.initialize_chain(network),
4834
5296
  netuid,
5297
+ era,
5298
+ json_output,
4835
5299
  prompt,
4836
5300
  )
4837
5301
  )
@@ -4957,6 +5421,7 @@ class CLIManager:
4957
5421
  "-s",
4958
5422
  help="Corresponding salt for the hash function, e.g. -s 163,241,217 ...",
4959
5423
  ),
5424
+ json_output: bool = Options.json_output,
4960
5425
  quiet: bool = Options.quiet,
4961
5426
  verbose: bool = Options.verbose,
4962
5427
  prompt: bool = Options.prompt,
@@ -4970,7 +5435,7 @@ class CLIManager:
4970
5435
 
4971
5436
  [green]$[/green] btcli wt reveal --netuid 1 --uids 1,2,3,4 --weights 0.1,0.2,0.3,0.4 --salt 163,241,217,11,161,142,147,189
4972
5437
  """
4973
- self.verbosity_handler(quiet, verbose)
5438
+ self.verbosity_handler(quiet, verbose, json_output)
4974
5439
  uids = list_prompt(uids, int, "UIDs of interest for the specified netuid")
4975
5440
  weights = list_prompt(
4976
5441
  weights, float, "Corresponding weights for the specified UIDs"
@@ -5003,7 +5468,7 @@ class CLIManager:
5003
5468
  err_console.print(
5004
5469
  "The number of UIDs you specify must match up with the specified number of weights"
5005
5470
  )
5006
- raise typer.Exit()
5471
+ return
5007
5472
 
5008
5473
  if salt:
5009
5474
  salt = parse_to_list(
@@ -5032,6 +5497,7 @@ class CLIManager:
5032
5497
  salt,
5033
5498
  __version_as_int__,
5034
5499
  prompt=prompt,
5500
+ json_output=json_output,
5035
5501
  )
5036
5502
  )
5037
5503
 
@@ -5055,6 +5521,7 @@ class CLIManager:
5055
5521
  "-s",
5056
5522
  help="Corresponding salt for the hash function, e.g. -s 163 -s 241 -s 217 ...",
5057
5523
  ),
5524
+ json_output: bool = Options.json_output,
5058
5525
  quiet: bool = Options.quiet,
5059
5526
  verbose: bool = Options.verbose,
5060
5527
  prompt: bool = Options.prompt,
@@ -5072,7 +5539,7 @@ class CLIManager:
5072
5539
  [italic]Note[/italic]: This command is used to commit weights for a specific subnet and requires the user to have the necessary
5073
5540
  permissions.
5074
5541
  """
5075
- self.verbosity_handler(quiet, verbose)
5542
+ self.verbosity_handler(quiet, verbose, json_output)
5076
5543
 
5077
5544
  if uids:
5078
5545
  uids = parse_to_list(
@@ -5101,7 +5568,7 @@ class CLIManager:
5101
5568
  err_console.print(
5102
5569
  "The number of UIDs you specify must match up with the specified number of weights"
5103
5570
  )
5104
- raise typer.Exit()
5571
+ return
5105
5572
 
5106
5573
  if salt:
5107
5574
  salt = parse_to_list(
@@ -5128,6 +5595,7 @@ class CLIManager:
5128
5595
  weights,
5129
5596
  salt,
5130
5597
  __version_as_int__,
5598
+ json_output=json_output,
5131
5599
  prompt=prompt,
5132
5600
  )
5133
5601
  )