bittensor-cli 9.8.7__tar.gz → 9.10.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 (57) hide show
  1. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/PKG-INFO +14 -6
  2. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/README.md +9 -1
  3. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/cli.py +229 -55
  4. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/extrinsics/registration.py +29 -25
  5. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/extrinsics/root.py +6 -5
  6. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/extrinsics/transfer.py +35 -18
  7. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/subtensor_interface.py +34 -15
  8. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/utils.py +13 -1
  9. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/stake/add.py +86 -30
  10. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/stake/children_hotkeys.py +16 -14
  11. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/stake/move.py +106 -84
  12. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/stake/remove.py +119 -11
  13. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/subnets/price.py +126 -30
  14. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/subnets/subnets.py +3 -2
  15. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/sudo.py +13 -11
  16. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/wallets.py +159 -19
  17. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/weights.py +2 -1
  18. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli.egg-info/PKG-INFO +14 -6
  19. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli.egg-info/requires.txt +4 -4
  20. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/pyproject.toml +5 -5
  21. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/MANIFEST.in +0 -0
  22. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/__init__.py +0 -0
  23. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/doc_generation_helper.py +0 -0
  24. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/__init__.py +0 -0
  25. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/__init__.py +0 -0
  26. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/balances.py +0 -0
  27. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/chain_data.py +0 -0
  28. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/extrinsics/__init__.py +0 -0
  29. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/minigraph.py +0 -0
  30. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/networking.py +0 -0
  31. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/main-filters.j2 +0 -0
  32. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/main-header.j2 +0 -0
  33. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/neuron-details.j2 +0 -0
  34. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/price-multi.j2 +0 -0
  35. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/price-single.j2 +0 -0
  36. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/subnet-details-header.j2 +0 -0
  37. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/subnet-details.j2 +0 -0
  38. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/subnet-metrics.j2 +0 -0
  39. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/subnets-table.j2 +0 -0
  40. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/table.j2 +0 -0
  41. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/view.css +0 -0
  42. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/view.j2 +0 -0
  43. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/bittensor/templates/view.js +0 -0
  44. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/__init__.py +0 -0
  45. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/liquidity/__init__.py +0 -0
  46. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/liquidity/liquidity.py +0 -0
  47. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/liquidity/utils.py +0 -0
  48. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/stake/__init__.py +0 -0
  49. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/stake/list.py +0 -0
  50. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/subnets/__init__.py +0 -0
  51. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/src/commands/view.py +0 -0
  52. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli/version.py +0 -0
  53. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli.egg-info/SOURCES.txt +0 -0
  54. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli.egg-info/dependency_links.txt +0 -0
  55. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli.egg-info/entry_points.txt +0 -0
  56. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/bittensor_cli.egg-info/top_level.txt +0 -0
  57. {bittensor_cli-9.8.7 → bittensor_cli-9.10.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bittensor-cli
3
- Version: 9.8.7
3
+ Version: 9.10.0
4
4
  Summary: Bittensor CLI
5
5
  Author: bittensor.com
6
6
  Project-URL: homepage, https://github.com/opentensor/btcli
@@ -8,10 +8,9 @@ Project-URL: Repository, https://github.com/opentensor/btcli
8
8
  Requires-Python: <3.14,>=3.9
9
9
  Description-Content-Type: text/markdown
10
10
  Requires-Dist: wheel
11
- Requires-Dist: async-substrate-interface>=1.1.0
11
+ Requires-Dist: async-substrate-interface>=1.4.2
12
12
  Requires-Dist: aiohttp~=3.10.2
13
13
  Requires-Dist: backoff~=2.2.1
14
- Requires-Dist: click<8.2.0
15
14
  Requires-Dist: GitPython>=3.0.0
16
15
  Requires-Dist: netaddr~=1.3.0
17
16
  Requires-Dist: numpy<3.0.0,>=2.0.1
@@ -20,8 +19,9 @@ Requires-Dist: pycryptodome<4.0.0,>=3.0.0
20
19
  Requires-Dist: PyYAML~=6.0.1
21
20
  Requires-Dist: rich<15.0,>=13.7
22
21
  Requires-Dist: scalecodec==1.2.11
23
- Requires-Dist: typer<0.16,>=0.12
24
- Requires-Dist: bittensor-wallet>=3.0.7
22
+ Requires-Dist: typer>=0.16
23
+ Requires-Dist: bittensor-wallet>=4.0.0
24
+ Requires-Dist: packaging
25
25
  Requires-Dist: plotille>=5.0.0
26
26
  Requires-Dist: plotly>=6.0.0
27
27
  Provides-Extra: cuda
@@ -71,7 +71,15 @@ Installation steps are described below. For a full documentation on how to use `
71
71
 
72
72
  ## Install on macOS and Linux
73
73
 
74
- You can install `btcli` on your local machine directly from source, PyPI, or Homebrew. **Make sure you verify your installation after you install**:
74
+ You can install `btcli` on your local machine directly from source, PyPI, or Homebrew.
75
+ **Make sure you verify your installation after you install**.
76
+
77
+ ### For macOS users
78
+ Note that the macOS preinstalled CPython installation is compiled with LibreSSL instead of OpenSSL. There are a number
79
+ of issues with LibreSSL, and as such is not fully supported by the libraries used by btcli. Thus we highly recommend, if
80
+ you are using a Mac, to first install Python from [Homebrew](https://brew.sh/). Additionally, the Rust FFI bindings
81
+ [if installing from precompiled wheels (default)] require the Homebrew-installed OpenSSL pacakge. If you choose to use
82
+ the preinstalled Python version from macOS, things may not work completely.
75
83
 
76
84
 
77
85
  ### Install from [PyPI](https://pypi.org/project/bittensor/)
@@ -38,7 +38,15 @@ Installation steps are described below. For a full documentation on how to use `
38
38
 
39
39
  ## Install on macOS and Linux
40
40
 
41
- You can install `btcli` on your local machine directly from source, PyPI, or Homebrew. **Make sure you verify your installation after you install**:
41
+ You can install `btcli` on your local machine directly from source, PyPI, or Homebrew.
42
+ **Make sure you verify your installation after you install**.
43
+
44
+ ### For macOS users
45
+ Note that the macOS preinstalled CPython installation is compiled with LibreSSL instead of OpenSSL. There are a number
46
+ of issues with LibreSSL, and as such is not fully supported by the libraries used by btcli. Thus we highly recommend, if
47
+ you are using a Mac, to first install Python from [Homebrew](https://brew.sh/). Additionally, the Rust FFI bindings
48
+ [if installing from precompiled wheels (default)] require the Homebrew-installed OpenSSL pacakge. If you choose to use
49
+ the preinstalled Python version from macOS, things may not work completely.
42
50
 
43
51
 
44
52
  ### Install from [PyPI](https://pypi.org/project/bittensor/)
@@ -58,6 +58,7 @@ from bittensor_cli.src.bittensor.utils import (
58
58
  validate_uri,
59
59
  prompt_for_subnet_identity,
60
60
  validate_rate_tolerance,
61
+ get_hotkey_pub_ss58,
61
62
  )
62
63
  from bittensor_cli.src.commands import sudo, wallets, view
63
64
  from bittensor_cli.src.commands import weights as weights_cmds
@@ -250,7 +251,7 @@ class Options:
250
251
  True,
251
252
  "--prompt/--no-prompt",
252
253
  " /--yes",
253
- "--prompt/--no_prompt",
254
+ " /--no_prompt",
254
255
  " /-y",
255
256
  help="Enable or disable interactive prompts.",
256
257
  )
@@ -643,8 +644,21 @@ class CLIManager:
643
644
  # },
644
645
  }
645
646
  self.subtensor = None
647
+
648
+ if sys.version_info < (3, 10):
649
+ # For Python 3.9 or lower
650
+ self.event_loop = asyncio.new_event_loop()
651
+ else:
652
+ try:
653
+ uvloop = importlib.import_module("uvloop")
654
+ self.event_loop = uvloop.new_event_loop()
655
+ except ModuleNotFoundError:
656
+ self.event_loop = asyncio.new_event_loop()
657
+
646
658
  self.config_base_path = os.path.expanduser(defaults.config.base_path)
647
- self.config_path = os.path.expanduser(defaults.config.path)
659
+ self.config_path = os.getenv("BTCLI_CONFIG_PATH") or os.path.expanduser(
660
+ defaults.config.path
661
+ )
648
662
 
649
663
  self.app = typer.Typer(
650
664
  rich_markup_mode="rich",
@@ -652,7 +666,12 @@ class CLIManager:
652
666
  epilog=_epilog,
653
667
  no_args_is_help=True,
654
668
  )
655
- self.config_app = typer.Typer(epilog=_epilog)
669
+ self.config_app = typer.Typer(
670
+ epilog=_epilog,
671
+ help=f"Allows for getting/setting the config. "
672
+ f"Default path for the config file is [{COLORS.G.ARG}]{defaults.config.path}[/{COLORS.G.ARG}]. "
673
+ f"You can set your own with the env var [{COLORS.G.ARG}]BTCLI_CONFIG_PATH[/{COLORS.G.ARG}]",
674
+ )
656
675
  self.wallet_app = typer.Typer(epilog=_epilog)
657
676
  self.stake_app = typer.Typer(epilog=_epilog)
658
677
  self.sudo_app = typer.Typer(epilog=_epilog)
@@ -773,6 +792,9 @@ class CLIManager:
773
792
  self.wallet_app.command(
774
793
  "regen-hotkey", rich_help_panel=HELP_PANELS["WALLET"]["SECURITY"]
775
794
  )(self.wallet_regen_hotkey)
795
+ self.wallet_app.command(
796
+ "regen-hotkeypub", rich_help_panel=HELP_PANELS["WALLET"]["SECURITY"]
797
+ )(self.wallet_regen_hotkey_pub)
776
798
  self.wallet_app.command(
777
799
  "new-hotkey", rich_help_panel=HELP_PANELS["WALLET"]["MANAGEMENT"]
778
800
  )(self.wallet_new_hotkey)
@@ -817,6 +839,9 @@ class CLIManager:
817
839
  self.wallet_app.command(
818
840
  "sign", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
819
841
  )(self.wallet_sign)
842
+ self.wallet_app.command(
843
+ "verify", rich_help_panel=HELP_PANELS["WALLET"]["OPERATIONS"]
844
+ )(self.wallet_verify)
820
845
 
821
846
  # stake commands
822
847
  self.stake_app.command(
@@ -952,6 +977,10 @@ class CLIManager:
952
977
  "regen_hotkey",
953
978
  hidden=True,
954
979
  )(self.wallet_regen_hotkey)
980
+ self.wallet_app.command(
981
+ "regen_hotkeypub",
982
+ hidden=True,
983
+ )(self.wallet_regen_hotkey_pub)
955
984
  self.wallet_app.command(
956
985
  "new_hotkey",
957
986
  hidden=True,
@@ -1076,11 +1105,11 @@ class CLIManager:
1076
1105
 
1077
1106
  self.subtensor = SubtensorInterface(network_)
1078
1107
  elif self.config["network"]:
1079
- self.subtensor = SubtensorInterface(self.config["network"])
1080
1108
  console.print(
1081
1109
  f"Using the specified network [{COLORS.G.LINKS}]{self.config['network']}"
1082
1110
  f"[/{COLORS.G.LINKS}] from config"
1083
1111
  )
1112
+ self.subtensor = SubtensorInterface(self.config["network"])
1084
1113
  else:
1085
1114
  self.subtensor = SubtensorInterface(defaults.subtensor.network)
1086
1115
  return self.subtensor
@@ -1094,12 +1123,9 @@ class CLIManager:
1094
1123
  initiated = False
1095
1124
  try:
1096
1125
  if self.subtensor:
1097
- async with self.subtensor:
1098
- initiated = True
1099
- result = await cmd
1100
- else:
1101
- initiated = True
1102
- result = await cmd
1126
+ await self.subtensor.substrate.initialize()
1127
+ initiated = True
1128
+ result = await cmd
1103
1129
  return result
1104
1130
  except (ConnectionRefusedError, ssl.SSLError, InvalidHandshake):
1105
1131
  err_console.print(f"Unable to connect to the chain: {self.subtensor}")
@@ -1125,12 +1151,14 @@ class CLIManager:
1125
1151
  exit_early is True
1126
1152
  ): # temporarily to handle multiple run commands in one session
1127
1153
  try:
1154
+ if self.subtensor:
1155
+ await self.subtensor.substrate.close()
1128
1156
  raise typer.Exit()
1129
1157
  except Exception as e: # ensures we always exit cleanly
1130
1158
  if not isinstance(e, (typer.Exit, RuntimeError)):
1131
1159
  err_console.print(f"An unknown error has occurred: {e}")
1132
1160
 
1133
- return self.asyncio_runner(_run())
1161
+ return self.event_loop.run_until_complete(_run())
1134
1162
 
1135
1163
  def main_callback(
1136
1164
  self,
@@ -1181,20 +1209,6 @@ class CLIManager:
1181
1209
  if k in self.config.keys():
1182
1210
  self.config[k] = v
1183
1211
 
1184
- if sys.version_info < (3, 10):
1185
- # For Python 3.9 or lower
1186
- self.asyncio_runner = asyncio.get_event_loop().run_until_complete
1187
- else:
1188
- try:
1189
- uvloop = importlib.import_module("uvloop")
1190
- if sys.version_info >= (3, 11):
1191
- self.asyncio_runner = uvloop.run
1192
- else:
1193
- uvloop.install()
1194
- self.asyncio_runner = asyncio.run
1195
- except ModuleNotFoundError:
1196
- self.asyncio_runner = asyncio.run
1197
-
1198
1212
  def verbosity_handler(
1199
1213
  self, quiet: bool, verbose: bool, json_output: bool = False
1200
1214
  ) -> None:
@@ -1499,6 +1513,8 @@ class CLIManager:
1499
1513
  Column("[bold white]Value", style="gold1"),
1500
1514
  Column("", style="medium_purple"),
1501
1515
  box=box.SIMPLE_HEAD,
1516
+ title=f"[{COLORS.G.HEADER}]BTCLI Config[/{COLORS.G.HEADER}]: "
1517
+ f"[{COLORS.G.ARG}]{self.config_path}[/{COLORS.G.ARG}]",
1502
1518
  )
1503
1519
 
1504
1520
  for key, value in self.config.items():
@@ -1719,7 +1735,7 @@ class CLIManager:
1719
1735
  if return_wallet_and_hotkey:
1720
1736
  valid = utils.is_valid_wallet(wallet)
1721
1737
  if valid[1]:
1722
- return wallet, wallet.hotkey.ss58_address
1738
+ return wallet, get_hotkey_pub_ss58(wallet)
1723
1739
  else:
1724
1740
  if wallet_hotkey and is_valid_ss58_address(wallet_hotkey):
1725
1741
  return wallet, wallet_hotkey
@@ -1889,6 +1905,12 @@ class CLIManager:
1889
1905
  transfer_all: bool = typer.Option(
1890
1906
  False, "--all", prompt=False, help="Transfer all available balance."
1891
1907
  ),
1908
+ allow_death: bool = typer.Option(
1909
+ False,
1910
+ "--allow-death",
1911
+ prompt=False,
1912
+ help="Transfer balance even if the resulting balance falls below the existential deposit.",
1913
+ ),
1892
1914
  period: int = Options.period,
1893
1915
  wallet_name: str = Options.wallet_name,
1894
1916
  wallet_path: str = Options.wallet_path,
@@ -1932,7 +1954,7 @@ class CLIManager:
1932
1954
  subtensor = self.initialize_chain(network)
1933
1955
  if transfer_all and amount:
1934
1956
  print_error("Cannot specify an amount and '--all' flag.")
1935
- raise typer.Exit()
1957
+ return False
1936
1958
  elif transfer_all:
1937
1959
  amount = 0
1938
1960
  elif not amount:
@@ -1944,6 +1966,7 @@ class CLIManager:
1944
1966
  destination=destination_ss58_address,
1945
1967
  amount=amount,
1946
1968
  transfer_all=transfer_all,
1969
+ allow_death=allow_death,
1947
1970
  era=period,
1948
1971
  prompt=prompt,
1949
1972
  json_output=json_output,
@@ -2219,14 +2242,15 @@ class CLIManager:
2219
2242
 
2220
2243
  if not wallet_path:
2221
2244
  wallet_path = Prompt.ask(
2222
- "Enter the path for the wallets directory", default=defaults.wallet.path
2245
+ "Enter the path for the wallets directory",
2246
+ default=self.config.get("wallet_path") or defaults.wallet.path,
2223
2247
  )
2224
2248
  wallet_path = os.path.expanduser(wallet_path)
2225
2249
 
2226
2250
  if not wallet_name:
2227
2251
  wallet_name = Prompt.ask(
2228
2252
  f"Enter the name of the [{COLORS.G.CK}]new wallet (coldkey)",
2229
- default=defaults.wallet.name,
2253
+ default=self.config.get("wallet_name") or defaults.wallet.name,
2230
2254
  )
2231
2255
 
2232
2256
  wallet = Wallet(wallet_name, wallet_hotkey, wallet_path)
@@ -2270,7 +2294,7 @@ class CLIManager:
2270
2294
 
2271
2295
  EXAMPLE
2272
2296
 
2273
- [green]$[/green] btcli wallet regen_coldkeypub --ss58_address 5DkQ4...
2297
+ [green]$[/green] btcli wallet regen-coldkeypub --ss58_address 5DkQ4...
2274
2298
 
2275
2299
  [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.
2276
2300
  """
@@ -2278,13 +2302,14 @@ class CLIManager:
2278
2302
 
2279
2303
  if not wallet_path:
2280
2304
  wallet_path = Prompt.ask(
2281
- "Enter the path to the wallets directory", default=defaults.wallet.path
2305
+ "Enter the path to the wallets directory",
2306
+ default=self.config.get("wallet_path") or defaults.wallet.path,
2282
2307
  )
2283
2308
  wallet_path = os.path.expanduser(wallet_path)
2284
2309
 
2285
2310
  if not wallet_name:
2286
2311
  wallet_name = Prompt.ask(
2287
- f"Enter the name of the [{COLORS.G.CK}]new wallet (coldkey)",
2312
+ f"Enter the name of the [{COLORS.G.CK}]wallet for the new coldkeypub",
2288
2313
  default=defaults.wallet.name,
2289
2314
  )
2290
2315
  wallet = Wallet(wallet_name, wallet_hotkey, wallet_path)
@@ -2301,7 +2326,7 @@ class CLIManager:
2301
2326
  address=ss58_address if ss58_address else public_key_hex
2302
2327
  ):
2303
2328
  rich.print("[red]Error: Invalid SS58 address or public key![/red]")
2304
- raise typer.Exit()
2329
+ return
2305
2330
  return self._run_command(
2306
2331
  wallets.regen_coldkey_pub(
2307
2332
  wallet, ss58_address, public_key_hex, overwrite, json_output
@@ -2367,6 +2392,68 @@ class CLIManager:
2367
2392
  )
2368
2393
  )
2369
2394
 
2395
+ def wallet_regen_hotkey_pub(
2396
+ self,
2397
+ wallet_name: Optional[str] = Options.wallet_name,
2398
+ wallet_path: Optional[str] = Options.wallet_path,
2399
+ wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2400
+ public_key_hex: Optional[str] = Options.public_hex_key,
2401
+ ss58_address: Optional[str] = Options.ss58_address,
2402
+ overwrite: bool = Options.overwrite,
2403
+ quiet: bool = Options.quiet,
2404
+ verbose: bool = Options.verbose,
2405
+ json_output: bool = Options.json_output,
2406
+ ):
2407
+ """
2408
+ Regenerates the public part of a hotkey (hotkeypub.txt) for a wallet.
2409
+
2410
+ Use this command when you need to move machine for subnet mining. Use the public key or SS58 address from your hotkeypub.txt that you have on another machine to regenerate the hotkeypub.txt on this new machine.
2411
+
2412
+ USAGE
2413
+
2414
+ The command requires either a public key in hexadecimal format or an ``SS58`` address from the existing hotkeypub.txt from old machine to regenerate the coldkeypub on the new machine.
2415
+
2416
+ EXAMPLE
2417
+
2418
+ [green]$[/green] btcli wallet regen-hotkeypub --ss58_address 5DkQ4...
2419
+
2420
+ [bold]Note[/bold]: This command is particularly useful for users who need to regenerate their hotkeypub, perhaps due to file corruption or loss. You will need either ss58 address or public hex key from your old hotkeypub.txt for the wallet. It is a recovery-focused utility that ensures continued access to your wallet functionalities.
2421
+ """
2422
+ self.verbosity_handler(quiet, verbose, json_output)
2423
+
2424
+ if not wallet_path:
2425
+ wallet_path = Prompt.ask(
2426
+ "Enter the path to the wallets directory",
2427
+ default=self.config.get("wallet_path") or defaults.wallet.path,
2428
+ )
2429
+ wallet_path = os.path.expanduser(wallet_path)
2430
+
2431
+ if not wallet_name:
2432
+ wallet_name = Prompt.ask(
2433
+ f"Enter the name of the [{COLORS.G.CK}]wallet for the new hotkeypub",
2434
+ default=defaults.wallet.name,
2435
+ )
2436
+ wallet = Wallet(wallet_name, wallet_hotkey, wallet_path)
2437
+
2438
+ if not ss58_address and not public_key_hex:
2439
+ prompt_answer = typer.prompt(
2440
+ "Enter the ss58_address or the public key in hex"
2441
+ )
2442
+ if prompt_answer.startswith("0x"):
2443
+ public_key_hex = prompt_answer
2444
+ else:
2445
+ ss58_address = prompt_answer
2446
+ if not utils.is_valid_bittensor_address_or_public_key(
2447
+ address=ss58_address if ss58_address else public_key_hex
2448
+ ):
2449
+ rich.print("[red]Error: Invalid SS58 address or public key![/red]")
2450
+ return False
2451
+ return self._run_command(
2452
+ wallets.regen_hotkey_pub(
2453
+ wallet, ss58_address, public_key_hex, overwrite, json_output
2454
+ )
2455
+ )
2456
+
2370
2457
  def wallet_new_hotkey(
2371
2458
  self,
2372
2459
  wallet_name: Optional[str] = Options.wallet_name,
@@ -2407,7 +2494,7 @@ class CLIManager:
2407
2494
  if not wallet_name:
2408
2495
  wallet_name = Prompt.ask(
2409
2496
  f"Enter the [{COLORS.G.CK}]wallet name",
2410
- default=defaults.wallet.name,
2497
+ default=self.config.get("wallet_name") or defaults.wallet.name,
2411
2498
  )
2412
2499
 
2413
2500
  if not wallet_hotkey:
@@ -2462,11 +2549,11 @@ class CLIManager:
2462
2549
  if not wallet_hotkey:
2463
2550
  wallet_hotkey = Prompt.ask(
2464
2551
  "Enter the [blue]hotkey[/blue] name or "
2465
- "[blue]hotkey ss58 address[/blue] [dim](to associate with your coldkey)[/dim]"
2552
+ "[blue]hotkey ss58 address[/blue] [dim](to associate with your coldkey)[/dim]",
2553
+ default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
2466
2554
  )
2467
2555
 
2468
- hotkey_display = None
2469
- if is_valid_ss58_address(wallet_hotkey):
2556
+ if wallet_hotkey and is_valid_ss58_address(wallet_hotkey):
2470
2557
  hotkey_ss58 = wallet_hotkey
2471
2558
  wallet = self.wallet_ask(
2472
2559
  wallet_name,
@@ -2486,8 +2573,11 @@ class CLIManager:
2486
2573
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
2487
2574
  validate=WV.WALLET_AND_HOTKEY,
2488
2575
  )
2489
- hotkey_ss58 = wallet.hotkey.ss58_address
2490
- hotkey_display = f"hotkey [blue]{wallet_hotkey}[/blue] [{COLORS.GENERAL.HK}]({hotkey_ss58})[/{COLORS.GENERAL.HK}]"
2576
+ hotkey_ss58 = get_hotkey_pub_ss58(wallet)
2577
+ hotkey_display = (
2578
+ f"hotkey [blue]{wallet_hotkey}[/blue] "
2579
+ f"[{COLORS.GENERAL.HK}]({hotkey_ss58})[/{COLORS.GENERAL.HK}]"
2580
+ )
2491
2581
 
2492
2582
  return self._run_command(
2493
2583
  wallets.associate_hotkey(
@@ -2534,13 +2624,14 @@ class CLIManager:
2534
2624
 
2535
2625
  if not wallet_path:
2536
2626
  wallet_path = Prompt.ask(
2537
- "Enter the path to the wallets directory", default=defaults.wallet.path
2627
+ "Enter the path to the wallets directory",
2628
+ default=self.config.get("wallet_path") or defaults.wallet.path,
2538
2629
  )
2539
2630
 
2540
2631
  if not wallet_name:
2541
2632
  wallet_name = Prompt.ask(
2542
2633
  f"Enter the name of the [{COLORS.G.CK}]new wallet (coldkey)",
2543
- default=defaults.wallet.name,
2634
+ default=self.config.get("wallet_name") or defaults.wallet.name,
2544
2635
  )
2545
2636
 
2546
2637
  wallet = self.wallet_ask(
@@ -2613,7 +2704,8 @@ class CLIManager:
2613
2704
 
2614
2705
  if not wallet_ss58_address:
2615
2706
  wallet_ss58_address = Prompt.ask(
2616
- "Enter [blue]wallet name[/blue] or [blue]SS58 address[/blue] [dim](leave blank to show all pending swaps)[/dim]"
2707
+ "Enter [blue]wallet name[/blue] or [blue]SS58 address[/blue] [dim]"
2708
+ "(leave blank to show all pending swaps)[/dim]"
2617
2709
  )
2618
2710
  if not wallet_ss58_address:
2619
2711
  return self._run_command(
@@ -2677,18 +2769,18 @@ class CLIManager:
2677
2769
  self.verbosity_handler(quiet, verbose, json_output)
2678
2770
  if not wallet_path:
2679
2771
  wallet_path = Prompt.ask(
2680
- "Enter the path of wallets directory", default=defaults.wallet.path
2772
+ "Enter the path of wallets directory",
2773
+ default=self.config.get("wallet_path") or defaults.wallet.path,
2681
2774
  )
2682
2775
 
2683
2776
  if not wallet_name:
2684
2777
  wallet_name = Prompt.ask(
2685
2778
  f"Enter the name of the [{COLORS.G.CK}]new wallet (coldkey)",
2686
- default=defaults.wallet.name,
2687
2779
  )
2688
2780
  if not wallet_hotkey:
2689
2781
  wallet_hotkey = Prompt.ask(
2690
2782
  f"Enter the the name of the [{COLORS.G.HK}]new hotkey",
2691
- default=defaults.wallet.hotkey,
2783
+ default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey,
2692
2784
  )
2693
2785
 
2694
2786
  wallet = self.wallet_ask(
@@ -3091,6 +3183,59 @@ class CLIManager:
3091
3183
 
3092
3184
  return self._run_command(wallets.sign(wallet, message, use_hotkey, json_output))
3093
3185
 
3186
+ def wallet_verify(
3187
+ self,
3188
+ message: Optional[str] = typer.Option(
3189
+ None, "--message", "-m", help="The message that was signed"
3190
+ ),
3191
+ signature: Optional[str] = typer.Option(
3192
+ None, "--signature", "-s", help="The signature to verify (hex format)"
3193
+ ),
3194
+ public_key_or_ss58: Optional[str] = typer.Option(
3195
+ None,
3196
+ "--address",
3197
+ "-a",
3198
+ "--public-key",
3199
+ "-p",
3200
+ help="SS58 address or public key (hex) of the signer",
3201
+ ),
3202
+ quiet: bool = Options.quiet,
3203
+ verbose: bool = Options.verbose,
3204
+ json_output: bool = Options.json_output,
3205
+ ):
3206
+ """
3207
+ Verify a message signature using the signer's public key or SS58 address.
3208
+
3209
+ This command allows you to verify that a message was signed by the owner of a specific address.
3210
+
3211
+ USAGE
3212
+
3213
+ Provide the original message, the signature (in hex format), and either the SS58 address
3214
+ or public key of the signer to verify the signature.
3215
+
3216
+ EXAMPLES
3217
+
3218
+ [green]$[/green] btcli wallet verify --message "Hello world" --signature "0xabc123..." --address "5GrwvaEF..."
3219
+
3220
+ [green]$[/green] btcli wallet verify -m "Test message" -s "0xdef456..." -p "0x1234abcd..."
3221
+ """
3222
+ self.verbosity_handler(quiet, verbose, json_output)
3223
+
3224
+ if not public_key_or_ss58:
3225
+ public_key_or_ss58 = Prompt.ask(
3226
+ "Enter the [blue]address[/blue] (SS58 or hex format)"
3227
+ )
3228
+
3229
+ if not message:
3230
+ message = Prompt.ask("Enter the [blue]message[/blue]")
3231
+
3232
+ if not signature:
3233
+ signature = Prompt.ask("Enter the [blue]signature[/blue]")
3234
+
3235
+ return self._run_command(
3236
+ wallets.verify(message, signature, public_key_or_ss58, json_output)
3237
+ )
3238
+
3094
3239
  def wallet_swap_coldkey(
3095
3240
  self,
3096
3241
  wallet_name: Optional[str] = Options.wallet_name,
@@ -3444,7 +3589,7 @@ class CLIManager:
3444
3589
  ask_for=[WO.NAME, WO.HOTKEY, WO.PATH],
3445
3590
  validate=WV.WALLET_AND_HOTKEY,
3446
3591
  )
3447
- include_hotkeys = wallet.hotkey.ss58_address
3592
+ include_hotkeys = get_hotkey_pub_ss58(wallet)
3448
3593
 
3449
3594
  elif all_hotkeys or include_hotkeys or exclude_hotkeys:
3450
3595
  wallet = self.wallet_ask(
@@ -3908,7 +4053,7 @@ class CLIManager:
3908
4053
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3909
4054
  validate=WV.WALLET_AND_HOTKEY,
3910
4055
  )
3911
- destination_hotkey = destination_wallet.hotkey.ss58_address
4056
+ destination_hotkey = get_hotkey_pub_ss58(destination_wallet)
3912
4057
  else:
3913
4058
  if is_valid_ss58_address(destination_hotkey):
3914
4059
  destination_hotkey = destination_hotkey
@@ -3947,7 +4092,7 @@ class CLIManager:
3947
4092
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
3948
4093
  validate=WV.WALLET_AND_HOTKEY,
3949
4094
  )
3950
- origin_hotkey = wallet.hotkey.ss58_address
4095
+ origin_hotkey = get_hotkey_pub_ss58(wallet)
3951
4096
  else:
3952
4097
  if is_valid_ss58_address(wallet_hotkey):
3953
4098
  origin_hotkey = wallet_hotkey
@@ -3959,7 +4104,7 @@ class CLIManager:
3959
4104
  ask_for=[],
3960
4105
  validate=WV.WALLET_AND_HOTKEY,
3961
4106
  )
3962
- origin_hotkey = wallet.hotkey.ss58_address
4107
+ origin_hotkey = get_hotkey_pub_ss58(wallet)
3963
4108
 
3964
4109
  if not interactive_selection:
3965
4110
  if origin_netuid is None:
@@ -4095,7 +4240,8 @@ class CLIManager:
4095
4240
  interactive_selection = False
4096
4241
  if not wallet_hotkey:
4097
4242
  origin_hotkey = Prompt.ask(
4098
- "Enter the [blue]origin hotkey[/blue] name or ss58 address [bold](stake will be transferred FROM here)[/bold] "
4243
+ "Enter the [blue]origin hotkey[/blue] name or ss58 address [bold]"
4244
+ "(stake will be transferred FROM here)[/bold] "
4099
4245
  "[dim](or press Enter to select from existing stakes)[/dim]"
4100
4246
  )
4101
4247
  if origin_hotkey == "":
@@ -4111,7 +4257,7 @@ class CLIManager:
4111
4257
  ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
4112
4258
  validate=WV.WALLET_AND_HOTKEY,
4113
4259
  )
4114
- origin_hotkey = wallet.hotkey.ss58_address
4260
+ origin_hotkey = get_hotkey_pub_ss58(wallet)
4115
4261
  else:
4116
4262
  if is_valid_ss58_address(wallet_hotkey):
4117
4263
  origin_hotkey = wallet_hotkey
@@ -4123,7 +4269,7 @@ class CLIManager:
4123
4269
  ask_for=[],
4124
4270
  validate=WV.WALLET_AND_HOTKEY,
4125
4271
  )
4126
- origin_hotkey = wallet.hotkey.ss58_address
4272
+ origin_hotkey = get_hotkey_pub_ss58(wallet)
4127
4273
 
4128
4274
  if not interactive_selection:
4129
4275
  if origin_netuid is None:
@@ -4946,7 +5092,7 @@ class CLIManager:
4946
5092
  "Netuids to show the price for. Separate multiple netuids with a comma, for example: `-n 0,1,2`.",
4947
5093
  ),
4948
5094
  interval_hours: int = typer.Option(
4949
- 24,
5095
+ 4,
4950
5096
  "--interval-hours",
4951
5097
  "--interval",
4952
5098
  help="The number of hours to show the historical price for.",
@@ -4963,6 +5109,11 @@ class CLIManager:
4963
5109
  "--log",
4964
5110
  help="Show the price in log scale.",
4965
5111
  ),
5112
+ current_only: bool = typer.Option(
5113
+ False,
5114
+ "--current",
5115
+ help="Show only the current data, and no historical data.",
5116
+ ),
4966
5117
  html_output: bool = Options.html_output,
4967
5118
  quiet: bool = Options.quiet,
4968
5119
  verbose: bool = Options.verbose,
@@ -4985,9 +5136,31 @@ class CLIManager:
4985
5136
  [green]$[/green] btcli subnets price --netuids 1,2,3,4 --html
4986
5137
  """
4987
5138
  if json_output and html_output:
4988
- print_error("Cannot specify both `--json-output` and `--html`")
5139
+ print_error(
5140
+ f"Cannot specify both [{COLORS.G.ARG}]--json-output[/{COLORS.G.ARG}] "
5141
+ f"and [{COLORS.G.ARG}]--html[/{COLORS.G.ARG}]"
5142
+ )
5143
+ return
5144
+ if current_only and html_output:
5145
+ print_error(
5146
+ f"Cannot specify both [{COLORS.G.ARG}]--current[/{COLORS.G.ARG}] "
5147
+ f"and [{COLORS.G.ARG}]--html[/{COLORS.G.ARG}]"
5148
+ )
4989
5149
  return
4990
5150
  self.verbosity_handler(quiet=quiet, verbose=verbose, json_output=json_output)
5151
+
5152
+ subtensor = self.initialize_chain(network)
5153
+ non_archives = ["finney", "latent-lite", "subvortex"]
5154
+ if not current_only and subtensor.network in non_archives + [
5155
+ Constants.network_map[x] for x in non_archives
5156
+ ]:
5157
+ err_console.print(
5158
+ f"[red]Error[/red] Running this command without [{COLORS.G.ARG}]--current[/{COLORS.G.ARG}] requires "
5159
+ "use of an archive node. "
5160
+ f"Try running again with the [{COLORS.G.ARG}]--network archive[/{COLORS.G.ARG}] flag."
5161
+ )
5162
+ return False
5163
+
4991
5164
  if netuids:
4992
5165
  netuids = parse_to_list(
4993
5166
  netuids,
@@ -5017,10 +5190,11 @@ class CLIManager:
5017
5190
 
5018
5191
  return self._run_command(
5019
5192
  price.price(
5020
- self.initialize_chain(network),
5193
+ subtensor,
5021
5194
  netuids,
5022
5195
  all_netuids,
5023
5196
  interval_hours,
5197
+ current_only,
5024
5198
  html_output,
5025
5199
  log_scale,
5026
5200
  json_output,