bittensor-cli 9.5.1__tar.gz → 9.7.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.
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/PKG-INFO +9 -3
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/README.md +8 -2
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/cli.py +77 -22
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/__init__.py +2 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/chain_data.py +4 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/extrinsics/registration.py +56 -16
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/subtensor_interface.py +15 -7
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/utils.py +30 -10
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/stake/add.py +46 -41
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/subnets/subnets.py +2 -1
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/sudo.py +71 -61
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/wallets.py +2 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli.egg-info/PKG-INFO +9 -3
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/pyproject.toml +1 -1
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/MANIFEST.in +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/__init__.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/doc_generation_helper.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/__init__.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/balances.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/extrinsics/__init__.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/extrinsics/root.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/extrinsics/transfer.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/minigraph.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/networking.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/main-filters.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/main-header.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/neuron-details.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/price-multi.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/price-single.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/subnet-details-header.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/subnet-details.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/subnet-metrics.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/subnets-table.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/table.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/view.css +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/view.j2 +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/view.js +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/__init__.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/stake/__init__.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/stake/children_hotkeys.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/stake/list.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/stake/move.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/stake/remove.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/subnets/__init__.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/subnets/price.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/view.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/weights.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/version.py +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli.egg-info/SOURCES.txt +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli.egg-info/dependency_links.txt +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli.egg-info/entry_points.txt +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli.egg-info/requires.txt +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli.egg-info/top_level.txt +0 -0
- {bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bittensor-cli
|
3
|
-
Version: 9.
|
3
|
+
Version: 9.7.0
|
4
4
|
Summary: Bittensor CLI
|
5
5
|
Author: bittensor.com
|
6
6
|
Project-URL: homepage, https://github.com/opentensor/btcli
|
@@ -71,10 +71,10 @@ 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, or
|
74
|
+
You can install `btcli` on your local machine directly from source, PyPI, or Homebrew. **Make sure you verify your installation after you install**:
|
75
75
|
|
76
76
|
|
77
|
-
### Install from PyPI
|
77
|
+
### Install from [PyPI](https://pypi.org/project/bittensor/)
|
78
78
|
|
79
79
|
Run
|
80
80
|
```
|
@@ -86,6 +86,12 @@ Alternatively, if you prefer to use [uv](https://pypi.org/project/uv/):
|
|
86
86
|
uv pip install bittensor-cli
|
87
87
|
```
|
88
88
|
|
89
|
+
### Install from [Homebrew](https://formulae.brew.sh/formula/btcli#default)
|
90
|
+
|
91
|
+
```shell
|
92
|
+
brew install btcli
|
93
|
+
```
|
94
|
+
|
89
95
|
### Install from source
|
90
96
|
|
91
97
|
1. Create and activate a virtual environment.
|
@@ -38,10 +38,10 @@ 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, or
|
41
|
+
You can install `btcli` on your local machine directly from source, PyPI, or Homebrew. **Make sure you verify your installation after you install**:
|
42
42
|
|
43
43
|
|
44
|
-
### Install from PyPI
|
44
|
+
### Install from [PyPI](https://pypi.org/project/bittensor/)
|
45
45
|
|
46
46
|
Run
|
47
47
|
```
|
@@ -53,6 +53,12 @@ Alternatively, if you prefer to use [uv](https://pypi.org/project/uv/):
|
|
53
53
|
uv pip install bittensor-cli
|
54
54
|
```
|
55
55
|
|
56
|
+
### Install from [Homebrew](https://formulae.brew.sh/formula/btcli#default)
|
57
|
+
|
58
|
+
```shell
|
59
|
+
brew install btcli
|
60
|
+
```
|
61
|
+
|
56
62
|
### Install from source
|
57
63
|
|
58
64
|
1. Create and activate a virtual environment.
|
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
import asyncio
|
3
3
|
import curses
|
4
|
+
import copy
|
4
5
|
import importlib
|
5
6
|
import json
|
6
7
|
import os.path
|
@@ -10,7 +11,7 @@ import sys
|
|
10
11
|
import traceback
|
11
12
|
import warnings
|
12
13
|
from pathlib import Path
|
13
|
-
from typing import Coroutine, Optional
|
14
|
+
from typing import Coroutine, Optional, Union
|
14
15
|
from dataclasses import fields
|
15
16
|
|
16
17
|
import rich
|
@@ -89,6 +90,23 @@ class Options:
|
|
89
90
|
Re-usable typer args
|
90
91
|
"""
|
91
92
|
|
93
|
+
@classmethod
|
94
|
+
def edit_help(cls, option_name: str, help_text: str):
|
95
|
+
"""
|
96
|
+
Edits the `help` attribute of a copied given Typer option in this class, returning
|
97
|
+
the modified Typer option.
|
98
|
+
|
99
|
+
Args:
|
100
|
+
option_name: the name of the option (e.g. "wallet_name")
|
101
|
+
help_text: New help text to be used (e.g. "Wallet's name")
|
102
|
+
|
103
|
+
Returns:
|
104
|
+
Modified Typer Option with new help text.
|
105
|
+
"""
|
106
|
+
copied_attr = copy.copy(getattr(cls, option_name))
|
107
|
+
setattr(copied_attr, "help", help_text)
|
108
|
+
return copied_attr
|
109
|
+
|
92
110
|
wallet_name = typer.Option(
|
93
111
|
None,
|
94
112
|
"--wallet-name",
|
@@ -1879,6 +1897,8 @@ class CLIManager:
|
|
1879
1897
|
wallet_name: Optional[str] = Options.wallet_name,
|
1880
1898
|
wallet_path: Optional[str] = Options.wallet_path,
|
1881
1899
|
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
1900
|
+
netuid: Optional[int] = Options.netuid_not_req,
|
1901
|
+
all_netuids: bool = Options.all_netuids,
|
1882
1902
|
network: Optional[list[str]] = Options.network,
|
1883
1903
|
destination_hotkey_name: Optional[str] = typer.Argument(
|
1884
1904
|
None, help="Destination hotkey name."
|
@@ -1899,12 +1919,14 @@ class CLIManager:
|
|
1899
1919
|
|
1900
1920
|
- Make sure that your original key pair (coldkeyA, hotkeyA) is already registered.
|
1901
1921
|
- Make sure that you use a newly created hotkeyB in this command. A hotkeyB that is already registered cannot be used in this command.
|
1922
|
+
- You can specify the netuid for which you want to swap the hotkey for. If it is not defined, the swap will be initiated for all subnets.
|
1902
1923
|
- Finally, note that this command requires a fee of 1 TAO for recycling and this fee is taken from your wallet (coldkeyA).
|
1903
1924
|
|
1904
1925
|
EXAMPLE
|
1905
1926
|
|
1906
|
-
[green]$[/green] btcli wallet swap_hotkey destination_hotkey_name --wallet-name your_wallet_name --wallet-hotkey original_hotkey
|
1927
|
+
[green]$[/green] btcli wallet swap_hotkey destination_hotkey_name --wallet-name your_wallet_name --wallet-hotkey original_hotkey --netuid 1
|
1907
1928
|
"""
|
1929
|
+
netuid = get_optional_netuid(netuid, all_netuids)
|
1908
1930
|
self.verbosity_handler(quiet, verbose, json_output)
|
1909
1931
|
original_wallet = self.wallet_ask(
|
1910
1932
|
wallet_name,
|
@@ -1928,7 +1950,7 @@ class CLIManager:
|
|
1928
1950
|
self.initialize_chain(network)
|
1929
1951
|
return self._run_command(
|
1930
1952
|
wallets.swap_hotkey(
|
1931
|
-
original_wallet, new_wallet, self.subtensor, prompt, json_output
|
1953
|
+
original_wallet, new_wallet, self.subtensor, netuid, prompt, json_output
|
1932
1954
|
)
|
1933
1955
|
)
|
1934
1956
|
|
@@ -3202,7 +3224,11 @@ class CLIManager:
|
|
3202
3224
|
help="When set, this command stakes to all hotkeys associated with the wallet. Do not use if specifying "
|
3203
3225
|
"hotkeys in `--include-hotkeys`.",
|
3204
3226
|
),
|
3205
|
-
|
3227
|
+
netuids: Optional[str] = Options.edit_help(
|
3228
|
+
"netuids",
|
3229
|
+
"Netuid(s) to for which to add stake. Specify multiple netuids by separating with a comma, e.g."
|
3230
|
+
"`btcli st add -n 1,2,3",
|
3231
|
+
),
|
3206
3232
|
all_netuids: bool = Options.all_netuids,
|
3207
3233
|
wallet_name: str = Options.wallet_name,
|
3208
3234
|
wallet_path: str = Options.wallet_path,
|
@@ -3242,42 +3268,65 @@ class CLIManager:
|
|
3242
3268
|
6. Stake all balance to a subnet:
|
3243
3269
|
[green]$[/green] btcli stake add --all --netuid 3
|
3244
3270
|
|
3271
|
+
7. Stake the same amount to multiple subnets:
|
3272
|
+
[green]$[/green] btcli stake add --amount 100 --netuids 4,5,6
|
3273
|
+
|
3245
3274
|
[bold]Safe Staking Parameters:[/bold]
|
3246
3275
|
• [blue]--safe[/blue]: Enables rate tolerance checks
|
3247
3276
|
• [blue]--tolerance[/blue]: Maximum % rate change allowed (0.05 = 5%)
|
3248
3277
|
• [blue]--partial[/blue]: Complete partial stake if rates exceed tolerance
|
3249
3278
|
|
3250
3279
|
"""
|
3280
|
+
netuids = netuids or []
|
3251
3281
|
self.verbosity_handler(quiet, verbose, json_output)
|
3252
3282
|
safe_staking = self.ask_safe_staking(safe_staking)
|
3253
3283
|
if safe_staking:
|
3254
3284
|
rate_tolerance = self.ask_rate_tolerance(rate_tolerance)
|
3255
3285
|
allow_partial_stake = self.ask_partial_stake(allow_partial_stake)
|
3256
3286
|
console.print("\n")
|
3257
|
-
|
3287
|
+
|
3288
|
+
if netuids:
|
3289
|
+
netuids = parse_to_list(
|
3290
|
+
netuids, int, "Netuids must be ints separated by commas", False
|
3291
|
+
)
|
3292
|
+
else:
|
3293
|
+
netuid_ = get_optional_netuid(None, all_netuids)
|
3294
|
+
netuids = [netuid_] if netuid_ else None
|
3295
|
+
if netuids:
|
3296
|
+
for netuid_ in netuids:
|
3297
|
+
# ensure no negative netuids make it into our list
|
3298
|
+
validate_netuid(netuid_)
|
3258
3299
|
|
3259
3300
|
if stake_all and amount:
|
3260
3301
|
print_error(
|
3261
3302
|
"Cannot specify an amount and 'stake-all'. Choose one or the other."
|
3262
3303
|
)
|
3263
|
-
|
3304
|
+
return
|
3264
3305
|
|
3265
3306
|
if stake_all and not amount:
|
3266
3307
|
if not Confirm.ask("Stake all the available TAO tokens?", default=False):
|
3267
|
-
|
3308
|
+
return
|
3309
|
+
|
3310
|
+
if (
|
3311
|
+
stake_all
|
3312
|
+
and (isinstance(netuids, list) and len(netuids) > 1)
|
3313
|
+
or (netuids is None)
|
3314
|
+
):
|
3315
|
+
print_error("Cannot stake all to multiple subnets.")
|
3316
|
+
return
|
3268
3317
|
|
3269
3318
|
if all_hotkeys and include_hotkeys:
|
3270
3319
|
print_error(
|
3271
3320
|
"You have specified hotkeys to include and also the `--all-hotkeys` flag. The flag"
|
3272
3321
|
"should only be used standalone (to use all hotkeys) or with `--exclude-hotkeys`."
|
3273
3322
|
)
|
3274
|
-
|
3323
|
+
return
|
3275
3324
|
|
3276
3325
|
if include_hotkeys and exclude_hotkeys:
|
3277
3326
|
print_error(
|
3278
3327
|
"You have specified options for both including and excluding hotkeys. Select one or the other."
|
3279
3328
|
)
|
3280
|
-
|
3329
|
+
return
|
3281
3330
|
|
3282
3331
|
if not wallet_hotkey and not all_hotkeys and not include_hotkeys:
|
3283
3332
|
if not wallet_name:
|
@@ -3285,9 +3334,10 @@ class CLIManager:
|
|
3285
3334
|
"Enter the [blue]wallet name[/blue]",
|
3286
3335
|
default=self.config.get("wallet_name") or defaults.wallet.name,
|
3287
3336
|
)
|
3288
|
-
if
|
3337
|
+
if netuids is not None:
|
3289
3338
|
hotkey_or_ss58 = Prompt.ask(
|
3290
|
-
"Enter the [blue]wallet hotkey[/blue] name or [blue]ss58 address[/blue] to stake to [dim]
|
3339
|
+
"Enter the [blue]wallet hotkey[/blue] name or [blue]ss58 address[/blue] to stake to [dim]"
|
3340
|
+
"(or Press Enter to view delegates)[/dim]",
|
3291
3341
|
)
|
3292
3342
|
else:
|
3293
3343
|
hotkey_or_ss58 = Prompt.ask(
|
@@ -3299,10 +3349,18 @@ class CLIManager:
|
|
3299
3349
|
wallet = self.wallet_ask(
|
3300
3350
|
wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH]
|
3301
3351
|
)
|
3352
|
+
if len(netuids) > 1:
|
3353
|
+
netuid_ = IntPrompt.ask(
|
3354
|
+
"Enter the netuid for which to show delegates",
|
3355
|
+
choices=[str(x) for x in netuids],
|
3356
|
+
)
|
3357
|
+
else:
|
3358
|
+
netuid_ = netuids[0]
|
3359
|
+
|
3302
3360
|
selected_hotkey = self._run_command(
|
3303
3361
|
subnets.show(
|
3304
3362
|
subtensor=self.initialize_chain(network),
|
3305
|
-
netuid=
|
3363
|
+
netuid=netuid_,
|
3306
3364
|
sort=False,
|
3307
3365
|
max_rows=12,
|
3308
3366
|
prompt=False,
|
@@ -3312,7 +3370,7 @@ class CLIManager:
|
|
3312
3370
|
)
|
3313
3371
|
if not selected_hotkey:
|
3314
3372
|
print_error("No delegate selected. Exiting.")
|
3315
|
-
|
3373
|
+
return
|
3316
3374
|
include_hotkeys = selected_hotkey
|
3317
3375
|
elif is_valid_ss58_address(hotkey_or_ss58):
|
3318
3376
|
wallet = self.wallet_ask(
|
@@ -3373,8 +3431,8 @@ class CLIManager:
|
|
3373
3431
|
)
|
3374
3432
|
if free_balance == Balance.from_tao(0):
|
3375
3433
|
print_error("You dont have any balance to stake.")
|
3376
|
-
|
3377
|
-
if
|
3434
|
+
return
|
3435
|
+
if netuids:
|
3378
3436
|
amount = FloatPrompt.ask(
|
3379
3437
|
f"Amount to [{COLORS.G.SUBHEAD_MAIN}]stake (TAO τ)"
|
3380
3438
|
)
|
@@ -3396,7 +3454,7 @@ class CLIManager:
|
|
3396
3454
|
add_stake.stake_add(
|
3397
3455
|
wallet,
|
3398
3456
|
self.initialize_chain(network),
|
3399
|
-
|
3457
|
+
netuids,
|
3400
3458
|
stake_all,
|
3401
3459
|
amount,
|
3402
3460
|
prompt,
|
@@ -4796,12 +4854,9 @@ class CLIManager:
|
|
4796
4854
|
def subnets_price(
|
4797
4855
|
self,
|
4798
4856
|
network: Optional[list[str]] = Options.network,
|
4799
|
-
netuids: str =
|
4800
|
-
|
4801
|
-
"
|
4802
|
-
"--netuid",
|
4803
|
-
"-n",
|
4804
|
-
help="Netuid(s) to show the price for.",
|
4857
|
+
netuids: str = Options.edit_help(
|
4858
|
+
"netuids",
|
4859
|
+
"Netuids to show the price for. Separate multiple netuids with a comma, for example: `-n 0,1,2`.",
|
4805
4860
|
),
|
4806
4861
|
interval_hours: int = typer.Option(
|
4807
4862
|
24,
|
@@ -658,6 +658,8 @@ HYPERPARAMS = {
|
|
658
658
|
"sudo_set_network_pow_registration_allowed",
|
659
659
|
False,
|
660
660
|
),
|
661
|
+
"yuma3_enabled": ("sudo_set_yuma3_enabled", False),
|
662
|
+
"alpha_sigmoid_steepness": ("sudo_set_alpha_sigmoid_steepness", True),
|
661
663
|
}
|
662
664
|
|
663
665
|
# Help Panels for cli help
|
@@ -177,6 +177,8 @@ class SubnetHyperparameters(InfoBase):
|
|
177
177
|
alpha_high: int
|
178
178
|
alpha_low: int
|
179
179
|
liquid_alpha_enabled: bool
|
180
|
+
yuma3_enabled: bool
|
181
|
+
alpha_sigmoid_steepness: int
|
180
182
|
|
181
183
|
@classmethod
|
182
184
|
def _fix_decoded(
|
@@ -210,6 +212,8 @@ class SubnetHyperparameters(InfoBase):
|
|
210
212
|
alpha_high=decoded.get("alpha_high"),
|
211
213
|
alpha_low=decoded.get("alpha_low"),
|
212
214
|
liquid_alpha_enabled=decoded.get("liquid_alpha_enabled"),
|
215
|
+
yuma3_enabled=decoded.get("yuma3_enabled"),
|
216
|
+
alpha_sigmoid_steepness=decoded.get("alpha_sigmoid_steepness"),
|
213
217
|
)
|
214
218
|
|
215
219
|
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/extrinsics/registration.py
RENAMED
@@ -1611,7 +1611,8 @@ def _update_curr_block(
|
|
1611
1611
|
"""
|
1612
1612
|
Update the current block data with the provided block information and difficulty.
|
1613
1613
|
|
1614
|
-
This function updates the current block
|
1614
|
+
This function updates the current block
|
1615
|
+
and its difficulty in a thread-safe manner. It sets the current block
|
1615
1616
|
number, hashes the block with the hotkey, updates the current block bytes, and packs the difficulty.
|
1616
1617
|
|
1617
1618
|
:param curr_diff: Shared array to store the current difficulty.
|
@@ -1745,6 +1746,7 @@ async def swap_hotkey_extrinsic(
|
|
1745
1746
|
subtensor: "SubtensorInterface",
|
1746
1747
|
wallet: Wallet,
|
1747
1748
|
new_wallet: Wallet,
|
1749
|
+
netuid: Optional[int] = None,
|
1748
1750
|
prompt: bool = False,
|
1749
1751
|
) -> bool:
|
1750
1752
|
"""
|
@@ -1756,43 +1758,81 @@ async def swap_hotkey_extrinsic(
|
|
1756
1758
|
netuids_registered = await subtensor.get_netuids_for_hotkey(
|
1757
1759
|
wallet.hotkey.ss58_address, block_hash=block_hash
|
1758
1760
|
)
|
1759
|
-
|
1761
|
+
netuids_registered_new_hotkey = await subtensor.get_netuids_for_hotkey(
|
1762
|
+
new_wallet.hotkey.ss58_address, block_hash=block_hash
|
1763
|
+
)
|
1764
|
+
|
1765
|
+
if netuid is not None and netuid not in netuids_registered:
|
1766
|
+
err_console.print(
|
1767
|
+
f":cross_mark: [red]Failed[/red]: Original hotkey {wallet.hotkey.ss58_address} is not registered on subnet {netuid}"
|
1768
|
+
)
|
1769
|
+
return False
|
1770
|
+
|
1771
|
+
elif not len(netuids_registered) > 0:
|
1760
1772
|
err_console.print(
|
1761
|
-
f"
|
1773
|
+
f"Original hotkey [dark_orange]{wallet.hotkey.ss58_address}[/dark_orange] is not registered on any subnet. "
|
1762
1774
|
f"Please register and try again"
|
1763
1775
|
)
|
1764
1776
|
return False
|
1765
1777
|
|
1778
|
+
if netuid is not None:
|
1779
|
+
if netuid in netuids_registered_new_hotkey:
|
1780
|
+
err_console.print(
|
1781
|
+
f":cross_mark: [red]Failed[/red]: New hotkey {new_wallet.hotkey.ss58_address} "
|
1782
|
+
f"is already registered on subnet {netuid}"
|
1783
|
+
)
|
1784
|
+
return False
|
1785
|
+
else:
|
1786
|
+
if len(netuids_registered_new_hotkey) > 0:
|
1787
|
+
err_console.print(
|
1788
|
+
f":cross_mark: [red]Failed[/red]: New hotkey {new_wallet.hotkey.ss58_address} "
|
1789
|
+
f"is already registered on subnet(s) {netuids_registered_new_hotkey}"
|
1790
|
+
)
|
1791
|
+
return False
|
1792
|
+
|
1766
1793
|
if not unlock_key(wallet).success:
|
1767
1794
|
return False
|
1768
1795
|
|
1769
1796
|
if prompt:
|
1770
1797
|
# Prompt user for confirmation.
|
1771
|
-
if not
|
1772
|
-
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1798
|
+
if netuid is not None:
|
1799
|
+
confirm_message = (
|
1800
|
+
f"Do you want to swap [dark_orange]{wallet.name}[/dark_orange] hotkey \n\t"
|
1801
|
+
f"[dark_orange]{wallet.hotkey.ss58_address} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
|
1802
|
+
f"[dark_orange]{new_wallet.hotkey.ss58_address} ({new_wallet.hotkey_str})[/dark_orange] on subnet {netuid}\n"
|
1803
|
+
"This operation will cost [bold cyan]1 TAO (recycled)[/bold cyan]"
|
1804
|
+
)
|
1805
|
+
else:
|
1806
|
+
confirm_message = (
|
1807
|
+
f"Do you want to swap [dark_orange]{wallet.name}[/dark_orange] hotkey \n\t"
|
1808
|
+
f"[dark_orange]{wallet.hotkey.ss58_address} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
|
1809
|
+
f"[dark_orange]{new_wallet.hotkey.ss58_address} ({new_wallet.hotkey_str})[/dark_orange] on all subnets\n"
|
1810
|
+
"This operation will cost [bold cyan]1 TAO (recycled)[/bold cyan]"
|
1811
|
+
)
|
1812
|
+
|
1813
|
+
if not Confirm.ask(confirm_message):
|
1777
1814
|
return False
|
1778
1815
|
print_verbose(
|
1779
|
-
f"Swapping {wallet.name}'s hotkey ({wallet.hotkey.ss58_address}) with "
|
1780
|
-
f"{new_wallet.name}s hotkey ({new_wallet.hotkey.ss58_address})"
|
1816
|
+
f"Swapping {wallet.name}'s hotkey ({wallet.hotkey.ss58_address} - {wallet.hotkey_str}) with "
|
1817
|
+
f"{new_wallet.name}'s hotkey ({new_wallet.hotkey.ss58_address} - {new_wallet.hotkey_str})"
|
1781
1818
|
)
|
1782
1819
|
with console.status(":satellite: Swapping hotkeys...", spinner="aesthetic"):
|
1820
|
+
call_params = {
|
1821
|
+
"hotkey": wallet.hotkey.ss58_address,
|
1822
|
+
"new_hotkey": new_wallet.hotkey.ss58_address,
|
1823
|
+
"netuid": netuid,
|
1824
|
+
}
|
1825
|
+
|
1783
1826
|
call = await subtensor.substrate.compose_call(
|
1784
1827
|
call_module="SubtensorModule",
|
1785
1828
|
call_function="swap_hotkey",
|
1786
|
-
call_params=
|
1787
|
-
"hotkey": wallet.hotkey.ss58_address,
|
1788
|
-
"new_hotkey": new_wallet.hotkey.ss58_address,
|
1789
|
-
},
|
1829
|
+
call_params=call_params,
|
1790
1830
|
)
|
1791
1831
|
success, err_msg = await subtensor.sign_and_send_extrinsic(call, wallet)
|
1792
1832
|
|
1793
1833
|
if success:
|
1794
1834
|
console.print(
|
1795
|
-
f"Hotkey {wallet.hotkey} swapped for new hotkey: {new_wallet.hotkey}"
|
1835
|
+
f"Hotkey {wallet.hotkey.ss58_address} ({wallet.hotkey_str}) swapped for new hotkey: {new_wallet.hotkey.ss58_address} ({new_wallet.hotkey_str})"
|
1796
1836
|
)
|
1797
1837
|
return True
|
1798
1838
|
else:
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/subtensor_interface.py
RENAMED
@@ -1128,14 +1128,22 @@ class SubtensorInterface:
|
|
1128
1128
|
Understanding the hyperparameters is crucial for comprehending how subnets are configured and
|
1129
1129
|
managed, and how they interact with the network's consensus and incentive mechanisms.
|
1130
1130
|
"""
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1131
|
+
main_result, yuma3_result, sigmoid_steepness = await asyncio.gather(
|
1132
|
+
self.query_runtime_api(
|
1133
|
+
runtime_api="SubnetInfoRuntimeApi",
|
1134
|
+
method="get_subnet_hyperparams",
|
1135
|
+
params=[netuid],
|
1136
|
+
block_hash=block_hash,
|
1137
|
+
),
|
1138
|
+
self.query("SubtensorModule", "Yuma3On", [netuid]),
|
1139
|
+
self.query("SubtensorModule", "AlphaSigmoidSteepness", [netuid]),
|
1136
1140
|
)
|
1137
|
-
|
1138
|
-
|
1141
|
+
result = {
|
1142
|
+
**main_result,
|
1143
|
+
**{"yuma3_enabled": yuma3_result},
|
1144
|
+
**{"alpha_sigmoid_steepness": sigmoid_steepness},
|
1145
|
+
}
|
1146
|
+
if not main_result:
|
1139
1147
|
return []
|
1140
1148
|
|
1141
1149
|
return SubnetHyperparameters.from_any(result)
|
@@ -3,7 +3,6 @@ from collections import namedtuple
|
|
3
3
|
import math
|
4
4
|
import os
|
5
5
|
import sqlite3
|
6
|
-
import platform
|
7
6
|
import webbrowser
|
8
7
|
from pathlib import Path
|
9
8
|
from typing import TYPE_CHECKING, Any, Collection, Optional, Union, Callable
|
@@ -33,6 +32,9 @@ if TYPE_CHECKING:
|
|
33
32
|
from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters
|
34
33
|
from rich.prompt import PromptBase
|
35
34
|
|
35
|
+
BT_DOCS_LINK = "https://docs.bittensor.com"
|
36
|
+
|
37
|
+
|
36
38
|
console = Console()
|
37
39
|
json_console = Console()
|
38
40
|
err_console = Console(stderr=True)
|
@@ -527,10 +529,11 @@ def format_error_message(error_message: Union[dict, Exception]) -> str:
|
|
527
529
|
elif all(x in d for x in ["code", "message", "data"]):
|
528
530
|
new_error_message = d
|
529
531
|
break
|
530
|
-
except ValueError:
|
532
|
+
except (ValueError, TypeError, SyntaxError, MemoryError, RecursionError):
|
531
533
|
pass
|
532
534
|
if new_error_message is None:
|
533
535
|
return_val = " ".join(error_message.args)
|
536
|
+
|
534
537
|
return f"Subtensor returned: {return_val}"
|
535
538
|
else:
|
536
539
|
error_message = new_error_message
|
@@ -549,8 +552,7 @@ def format_error_message(error_message: Union[dict, Exception]) -> str:
|
|
549
552
|
# subtensor custom error marker
|
550
553
|
if err_data.startswith("Custom error:"):
|
551
554
|
err_description = (
|
552
|
-
f"{err_data} | Please consult "
|
553
|
-
f"https://docs.bittensor.com/subtensor-nodes/subtensor-error-messages"
|
555
|
+
f"{err_data} | Please consult {BT_DOCS_LINK}/errors/custom"
|
554
556
|
)
|
555
557
|
else:
|
556
558
|
err_description = err_data
|
@@ -563,10 +565,20 @@ def format_error_message(error_message: Union[dict, Exception]) -> str:
|
|
563
565
|
err_type = error_message.get("type", err_type)
|
564
566
|
err_name = error_message.get("name", err_name)
|
565
567
|
err_docs = error_message.get("docs", [err_description])
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
568
|
+
err_description = " ".join(err_docs)
|
569
|
+
err_description += (
|
570
|
+
f" | Please consult {BT_DOCS_LINK}/errors/subtensor#{err_name.lower()}"
|
571
|
+
)
|
572
|
+
|
573
|
+
elif error_message.get("code") and error_message.get("message"):
|
574
|
+
err_type = error_message.get("code", err_name)
|
575
|
+
err_name = "Custom type"
|
576
|
+
err_description = error_message.get("message", err_description)
|
577
|
+
|
578
|
+
else:
|
579
|
+
print_error(
|
580
|
+
f"String representation of real error_message: {str(error_message)}"
|
581
|
+
)
|
570
582
|
|
571
583
|
return f"Subtensor returned `{err_name}({err_type})` error. This means: `{err_description}`."
|
572
584
|
|
@@ -706,11 +718,14 @@ def millify_tao(n: float, start_at: str = "K") -> str:
|
|
706
718
|
|
707
719
|
def normalize_hyperparameters(
|
708
720
|
subnet: "SubnetHyperparameters",
|
721
|
+
json_output: bool = False,
|
709
722
|
) -> list[tuple[str, str, str]]:
|
710
723
|
"""
|
711
724
|
Normalizes the hyperparameters of a subnet.
|
712
725
|
|
713
726
|
:param subnet: The subnet hyperparameters object.
|
727
|
+
:param json_output: Whether this normalisation will be for a JSON output or console string (determines whether
|
728
|
+
items get stringified or safe for JSON encoding)
|
714
729
|
|
715
730
|
:return: A list of tuples containing the parameter name, value, and normalized value.
|
716
731
|
"""
|
@@ -724,6 +739,7 @@ def normalize_hyperparameters(
|
|
724
739
|
"kappa": u16_normalized_float,
|
725
740
|
"alpha_high": u16_normalized_float,
|
726
741
|
"alpha_low": u16_normalized_float,
|
742
|
+
"alpha_sigmoid_steepness": u16_normalized_float,
|
727
743
|
"min_burn": Balance.from_rao,
|
728
744
|
"max_burn": Balance.from_rao,
|
729
745
|
}
|
@@ -737,13 +753,17 @@ def normalize_hyperparameters(
|
|
737
753
|
norm_value = param_mappings[param](value)
|
738
754
|
if isinstance(norm_value, float):
|
739
755
|
norm_value = f"{norm_value:.{10}g}"
|
756
|
+
if isinstance(norm_value, Balance) and json_output:
|
757
|
+
norm_value = norm_value.to_dict()
|
740
758
|
else:
|
741
759
|
norm_value = value
|
742
760
|
except Exception:
|
743
761
|
# bittensor.logging.warning(f"Error normalizing parameter '{param}': {e}")
|
744
762
|
norm_value = "-"
|
745
|
-
|
746
|
-
|
763
|
+
if not json_output:
|
764
|
+
normalized_values.append((param, str(value), str(norm_value)))
|
765
|
+
else:
|
766
|
+
normalized_values.append((param, value, norm_value))
|
747
767
|
|
748
768
|
return normalized_values
|
749
769
|
|
@@ -31,7 +31,7 @@ if TYPE_CHECKING:
|
|
31
31
|
async def stake_add(
|
32
32
|
wallet: Wallet,
|
33
33
|
subtensor: "SubtensorInterface",
|
34
|
-
|
34
|
+
netuids: Optional[list[int]],
|
35
35
|
stake_all: bool,
|
36
36
|
amount: float,
|
37
37
|
prompt: bool,
|
@@ -48,7 +48,7 @@ async def stake_add(
|
|
48
48
|
Args:
|
49
49
|
wallet: wallet object
|
50
50
|
subtensor: SubtensorInterface object
|
51
|
-
|
51
|
+
netuids: the netuids to stake to (None indicates all subnets)
|
52
52
|
stake_all: whether to stake all available balance
|
53
53
|
amount: specified amount of balance to stake
|
54
54
|
prompt: whether to prompt the user
|
@@ -72,7 +72,7 @@ async def stake_add(
|
|
72
72
|
hotkey_ss58_: str,
|
73
73
|
price_limit: Balance,
|
74
74
|
status=None,
|
75
|
-
) -> bool:
|
75
|
+
) -> tuple[bool, str]:
|
76
76
|
err_out = partial(print_error, status=status)
|
77
77
|
failure_prelude = (
|
78
78
|
f":cross_mark: [red]Failed[/red] to stake {amount_} on Netuid {netuid_}"
|
@@ -104,25 +104,24 @@ async def stake_add(
|
|
104
104
|
)
|
105
105
|
except SubstrateRequestException as e:
|
106
106
|
if "Custom error: 8" in str(e):
|
107
|
-
|
108
|
-
f"
|
107
|
+
err_msg = (
|
108
|
+
f"{failure_prelude}: Price exceeded tolerance limit. "
|
109
109
|
f"Transaction rejected because partial staking is disabled. "
|
110
|
-
f"Either increase price tolerance or enable partial staking."
|
111
|
-
status=status,
|
110
|
+
f"Either increase price tolerance or enable partial staking."
|
112
111
|
)
|
113
|
-
|
112
|
+
print_error("\n" + err_msg, status=status)
|
114
113
|
else:
|
115
|
-
|
116
|
-
|
114
|
+
err_msg = f"{failure_prelude} with error: {format_error_message(e)}"
|
115
|
+
err_out("\n" + err_msg)
|
116
|
+
return False, err_msg
|
117
117
|
if not await response.is_success:
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
return False
|
118
|
+
err_msg = f"{failure_prelude} with error: {format_error_message(await response.error_message)}"
|
119
|
+
err_out("\n" + err_msg)
|
120
|
+
return False, err_msg
|
122
121
|
else:
|
123
122
|
if json_output:
|
124
123
|
# the rest of this checking is not necessary if using json_output
|
125
|
-
return True
|
124
|
+
return True, ""
|
126
125
|
block_hash = await subtensor.substrate.get_chain_head()
|
127
126
|
new_balance, new_stake = await asyncio.gather(
|
128
127
|
subtensor.get_balance(wallet.coldkeypub.ss58_address, block_hash),
|
@@ -160,11 +159,11 @@ async def stake_add(
|
|
160
159
|
f":arrow_right: "
|
161
160
|
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
|
162
161
|
)
|
163
|
-
return True
|
162
|
+
return True, ""
|
164
163
|
|
165
164
|
async def stake_extrinsic(
|
166
165
|
netuid_i, amount_, current, staking_address_ss58, status=None
|
167
|
-
) -> bool:
|
166
|
+
) -> tuple[bool, str]:
|
168
167
|
err_out = partial(print_error, status=status)
|
169
168
|
current_balance, next_nonce, call = await asyncio.gather(
|
170
169
|
subtensor.get_balance(wallet.coldkeypub.ss58_address),
|
@@ -190,18 +189,18 @@ async def stake_add(
|
|
190
189
|
extrinsic, wait_for_inclusion=True, wait_for_finalization=False
|
191
190
|
)
|
192
191
|
except SubstrateRequestException as e:
|
193
|
-
|
194
|
-
|
192
|
+
err_msg = f"{failure_prelude} with error: {format_error_message(e)}"
|
193
|
+
err_out("\n" + err_msg)
|
194
|
+
return False, err_msg
|
195
195
|
else:
|
196
196
|
if not await response.is_success:
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
return False
|
197
|
+
err_msg = f"{failure_prelude} with error: {format_error_message(await response.error_message)}"
|
198
|
+
err_out("\n" + err_msg)
|
199
|
+
return False, err_msg
|
201
200
|
else:
|
202
201
|
if json_output:
|
203
202
|
# the rest of this is not necessary if using json_output
|
204
|
-
return True
|
203
|
+
return True, ""
|
205
204
|
new_block_hash = await subtensor.substrate.get_chain_head()
|
206
205
|
new_balance, new_stake = await asyncio.gather(
|
207
206
|
subtensor.get_balance(
|
@@ -230,12 +229,10 @@ async def stake_add(
|
|
230
229
|
f":arrow_right: "
|
231
230
|
f"[{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_stake}\n"
|
232
231
|
)
|
233
|
-
return True
|
232
|
+
return True, ""
|
234
233
|
|
235
234
|
netuids = (
|
236
|
-
|
237
|
-
if netuid is not None
|
238
|
-
else await subtensor.get_all_subnet_netuids()
|
235
|
+
netuids if netuids is not None else await subtensor.get_all_subnet_netuids()
|
239
236
|
)
|
240
237
|
|
241
238
|
hotkeys_to_stake_to = _get_hotkeys_to_stake_to(
|
@@ -419,13 +416,17 @@ async def stake_add(
|
|
419
416
|
for _, staking_address in hotkeys_to_stake_to
|
420
417
|
}
|
421
418
|
successes = defaultdict(dict)
|
419
|
+
error_messages = defaultdict(dict)
|
422
420
|
with console.status(f"\n:satellite: Staking on netuid(s): {netuids} ..."):
|
423
421
|
# We can gather them all at once but balance reporting will be in race-condition.
|
424
422
|
for (ni, staking_address), coroutine in stake_coroutines.items():
|
425
|
-
success = await coroutine
|
423
|
+
success, er_msg = await coroutine
|
426
424
|
successes[ni][staking_address] = success
|
425
|
+
error_messages[ni][staking_address] = er_msg
|
427
426
|
if json_output:
|
428
|
-
json_console.print(
|
427
|
+
json_console.print(
|
428
|
+
json.dumps({"staking_success": successes, "error_messages": error_messages})
|
429
|
+
)
|
429
430
|
|
430
431
|
|
431
432
|
# Helper functions
|
@@ -445,10 +446,10 @@ def _prompt_stake_amount(
|
|
445
446
|
while True:
|
446
447
|
amount_input = Prompt.ask(
|
447
448
|
f"\nEnter the amount to {action_name}"
|
448
|
-
f"[{COLOR_PALETTE
|
449
|
-
f"[{COLOR_PALETTE
|
449
|
+
f"[{COLOR_PALETTE.S.STAKE_AMOUNT}]{Balance.get_unit(netuid)}[/{COLOR_PALETTE.S.STAKE_AMOUNT}] "
|
450
|
+
f"[{COLOR_PALETTE.S.STAKE_AMOUNT}](max: {current_balance})[/{COLOR_PALETTE.S.STAKE_AMOUNT}] "
|
450
451
|
f"or "
|
451
|
-
f"[{COLOR_PALETTE
|
452
|
+
f"[{COLOR_PALETTE.S.STAKE_AMOUNT}]'all'[/{COLOR_PALETTE.S.STAKE_AMOUNT}] "
|
452
453
|
f"for entire balance"
|
453
454
|
)
|
454
455
|
|
@@ -463,7 +464,7 @@ def _prompt_stake_amount(
|
|
463
464
|
if amount > current_balance.tao:
|
464
465
|
console.print(
|
465
466
|
f"[red]Amount exceeds available balance of "
|
466
|
-
f"[{COLOR_PALETTE
|
467
|
+
f"[{COLOR_PALETTE.S.STAKE_AMOUNT}]{current_balance}[/{COLOR_PALETTE.S.STAKE_AMOUNT}]"
|
467
468
|
f"[/red]"
|
468
469
|
)
|
469
470
|
continue
|
@@ -542,10 +543,10 @@ def _define_stake_table(
|
|
542
543
|
Table: An initialized rich Table object with appropriate columns
|
543
544
|
"""
|
544
545
|
table = Table(
|
545
|
-
title=f"\n[{COLOR_PALETTE
|
546
|
-
f"Wallet: [{COLOR_PALETTE
|
547
|
-
f"Coldkey ss58: [{COLOR_PALETTE
|
548
|
-
f"Network: {subtensor.network}[/{COLOR_PALETTE
|
546
|
+
title=f"\n[{COLOR_PALETTE.G.HEADER}]Staking to:\n"
|
547
|
+
f"Wallet: [{COLOR_PALETTE.G.CK}]{wallet.name}[/{COLOR_PALETTE.G.CK}], "
|
548
|
+
f"Coldkey ss58: [{COLOR_PALETTE.G.CK}]{wallet.coldkeypub.ss58_address}[/{COLOR_PALETTE.G.CK}]\n"
|
549
|
+
f"Network: {subtensor.network}[/{COLOR_PALETTE.G.HEADER}]\n",
|
549
550
|
show_footer=True,
|
550
551
|
show_edge=False,
|
551
552
|
header_style="bold white",
|
@@ -609,9 +610,13 @@ def _print_table_and_slippage(table: Table, max_slippage: float, safe_staking: b
|
|
609
610
|
|
610
611
|
# Greater than 5%
|
611
612
|
if max_slippage > 5:
|
612
|
-
message =
|
613
|
-
|
614
|
-
|
613
|
+
message = (
|
614
|
+
f"[{COLOR_PALETTE.S.SLIPPAGE_TEXT}]" + ("-" * 115) + "\n"
|
615
|
+
f"[bold]WARNING:[/bold] The slippage on one of your operations is high: "
|
616
|
+
f"[{COLOR_PALETTE.S.SLIPPAGE_PERCENT}]{max_slippage} %[/{COLOR_PALETTE.S.SLIPPAGE_PERCENT}], "
|
617
|
+
f"this may result in a loss of funds.\n" + ("-" * 115) + "\n"
|
618
|
+
)
|
619
|
+
|
615
620
|
console.print(message)
|
616
621
|
|
617
622
|
# Table description
|
@@ -2323,7 +2323,7 @@ async def get_start_schedule(
|
|
2323
2323
|
print_error(f"Subnet {netuid} does not exist.")
|
2324
2324
|
return None
|
2325
2325
|
block_hash = await subtensor.substrate.get_chain_head()
|
2326
|
-
registration_block,
|
2326
|
+
registration_block, min_blocks_to_start_, current_block = await asyncio.gather(
|
2327
2327
|
subtensor.query(
|
2328
2328
|
module="SubtensorModule",
|
2329
2329
|
storage_function="NetworkRegisteredAt",
|
@@ -2337,6 +2337,7 @@ async def get_start_schedule(
|
|
2337
2337
|
),
|
2338
2338
|
subtensor.substrate.get_block_number(block_hash=block_hash),
|
2339
2339
|
)
|
2340
|
+
min_blocks_to_start = getattr(min_blocks_to_start_, "value", min_blocks_to_start_)
|
2340
2341
|
|
2341
2342
|
potential_start_block = registration_block + min_blocks_to_start
|
2342
2343
|
if current_block < potential_start_block:
|
@@ -73,6 +73,13 @@ def allowed_value(
|
|
73
73
|
return True, value
|
74
74
|
|
75
75
|
|
76
|
+
def string_to_bool(val) -> bool:
|
77
|
+
try:
|
78
|
+
return {"true": True, "1": True, "0": False, "false": False}[val.lower()]
|
79
|
+
except KeyError:
|
80
|
+
return ValueError
|
81
|
+
|
82
|
+
|
76
83
|
def search_metadata(
|
77
84
|
param_name: str, value: Union[str, bool, float, list[float]], netuid: int, metadata
|
78
85
|
) -> tuple[bool, Optional[dict]]:
|
@@ -91,12 +98,6 @@ def search_metadata(
|
|
91
98
|
|
92
99
|
"""
|
93
100
|
|
94
|
-
def string_to_bool(val) -> bool:
|
95
|
-
try:
|
96
|
-
return {"true": True, "1": True, "0": False, "false": False}[val.lower()]
|
97
|
-
except KeyError:
|
98
|
-
return ValueError
|
99
|
-
|
100
101
|
def type_converter_with_retry(type_, val, arg_name):
|
101
102
|
try:
|
102
103
|
if val is None:
|
@@ -112,37 +113,55 @@ def search_metadata(
|
|
112
113
|
|
113
114
|
call_crafter = {"netuid": netuid}
|
114
115
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
arg["typeName"], None, arg["name"]
|
134
|
-
)
|
135
|
-
return True, call_crafter
|
116
|
+
pallet = metadata.get_metadata_pallet("AdminUtils")
|
117
|
+
for call in pallet.calls:
|
118
|
+
if call.name == param_name:
|
119
|
+
if "netuid" not in [x.name for x in call.args]:
|
120
|
+
return False, None
|
121
|
+
call_args = [arg for arg in call.args if arg.value["name"] != "netuid"]
|
122
|
+
if len(call_args) == 1:
|
123
|
+
arg = call_args[0].value
|
124
|
+
call_crafter[arg["name"]] = type_converter_with_retry(
|
125
|
+
arg["typeName"], value, arg["name"]
|
126
|
+
)
|
127
|
+
else:
|
128
|
+
for arg_ in call_args:
|
129
|
+
arg = arg_.value
|
130
|
+
call_crafter[arg["name"]] = type_converter_with_retry(
|
131
|
+
arg["typeName"], None, arg["name"]
|
132
|
+
)
|
133
|
+
return True, call_crafter
|
136
134
|
else:
|
137
135
|
return False, None
|
138
136
|
|
139
137
|
|
138
|
+
def requires_bool(metadata, param_name) -> bool:
|
139
|
+
"""
|
140
|
+
Determines whether a given hyperparam takes a single arg (besides netuid) that is of bool type.
|
141
|
+
"""
|
142
|
+
pallet = metadata.get_metadata_pallet("AdminUtils")
|
143
|
+
for call in pallet.calls:
|
144
|
+
if call.name == param_name:
|
145
|
+
if "netuid" not in [x.name for x in call.args]:
|
146
|
+
return False, None
|
147
|
+
call_args = [arg for arg in call.args if arg.value["name"] != "netuid"]
|
148
|
+
if len(call_args) != 1:
|
149
|
+
return False
|
150
|
+
else:
|
151
|
+
arg = call_args[0].value
|
152
|
+
if arg["typeName"] == "bool":
|
153
|
+
return True
|
154
|
+
else:
|
155
|
+
return False
|
156
|
+
raise ValueError(f"{param_name} not found in pallet.")
|
157
|
+
|
158
|
+
|
140
159
|
async def set_hyperparameter_extrinsic(
|
141
160
|
subtensor: "SubtensorInterface",
|
142
161
|
wallet: "Wallet",
|
143
162
|
netuid: int,
|
144
163
|
parameter: str,
|
145
|
-
value: Optional[Union[str,
|
164
|
+
value: Optional[Union[str, float, list[float]]],
|
146
165
|
wait_for_inclusion: bool = False,
|
147
166
|
wait_for_finalization: bool = True,
|
148
167
|
prompt: bool = True,
|
@@ -221,15 +240,20 @@ async def set_hyperparameter_extrinsic(
|
|
221
240
|
]
|
222
241
|
|
223
242
|
if len(value) < len(non_netuid_fields):
|
224
|
-
|
243
|
+
err_console.print(
|
225
244
|
"Not enough values provided in the list for all parameters"
|
226
245
|
)
|
246
|
+
return False
|
227
247
|
|
228
248
|
call_params.update(
|
229
249
|
{str(name): val for name, val in zip(non_netuid_fields, value)}
|
230
250
|
)
|
231
251
|
|
232
252
|
else:
|
253
|
+
if requires_bool(
|
254
|
+
substrate.metadata, param_name=extrinsic
|
255
|
+
) and isinstance(value, str):
|
256
|
+
value = string_to_bool(value)
|
233
257
|
value_argument = extrinsic_params["fields"][
|
234
258
|
len(extrinsic_params["fields"]) - 1
|
235
259
|
]
|
@@ -252,12 +276,13 @@ async def set_hyperparameter_extrinsic(
|
|
252
276
|
)
|
253
277
|
if not success:
|
254
278
|
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
|
255
|
-
|
279
|
+
return False
|
256
280
|
elif arbitrary_extrinsic:
|
257
281
|
console.print(
|
258
282
|
f":white_heavy_check_mark: "
|
259
283
|
f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]"
|
260
284
|
)
|
285
|
+
return True
|
261
286
|
# Successful registration, final check for membership
|
262
287
|
else:
|
263
288
|
console.print(
|
@@ -581,28 +606,11 @@ async def sudo_set_hyperparameter(
|
|
581
606
|
json_output: bool,
|
582
607
|
):
|
583
608
|
"""Set subnet hyperparameters."""
|
584
|
-
|
585
|
-
normalized_value: Union[str, bool]
|
586
|
-
if param_name in [
|
587
|
-
"registration_allowed",
|
588
|
-
"network_pow_registration_allowed",
|
589
|
-
"commit_reveal_weights_enabled",
|
590
|
-
"liquid_alpha_enabled",
|
591
|
-
]:
|
592
|
-
normalized_value = param_value.lower() in ["true", "1"]
|
593
|
-
elif param_value in ("True", "False"):
|
594
|
-
normalized_value = {
|
595
|
-
"True": True,
|
596
|
-
"False": False,
|
597
|
-
}[param_value]
|
598
|
-
else:
|
599
|
-
normalized_value = param_value
|
600
|
-
|
601
|
-
is_allowed_value, value = allowed_value(param_name, normalized_value)
|
609
|
+
is_allowed_value, value = allowed_value(param_name, param_value)
|
602
610
|
if not is_allowed_value:
|
603
611
|
err_console.print(
|
604
612
|
f"Hyperparameter [dark_orange]{param_name}[/dark_orange] value is not within bounds. "
|
605
|
-
f"Value is {
|
613
|
+
f"Value is {param_value} but must be {value}"
|
606
614
|
)
|
607
615
|
return False
|
608
616
|
success = await set_hyperparameter_extrinsic(
|
@@ -625,8 +633,9 @@ async def get_hyperparameters(
|
|
625
633
|
if not await subtensor.subnet_exists(netuid):
|
626
634
|
print_error(f"Subnet with netuid {netuid} does not exist.")
|
627
635
|
return False
|
628
|
-
subnet = await
|
629
|
-
|
636
|
+
subnet, subnet_info = await asyncio.gather(
|
637
|
+
subtensor.get_subnet_hyperparameters(netuid), subtensor.subnet(netuid)
|
638
|
+
)
|
630
639
|
if subnet_info is None:
|
631
640
|
print_error(f"Subnet with netuid {netuid} does not exist.")
|
632
641
|
return False
|
@@ -648,17 +657,18 @@ async def get_hyperparameters(
|
|
648
657
|
)
|
649
658
|
dict_out = []
|
650
659
|
|
651
|
-
normalized_values = normalize_hyperparameters(subnet)
|
652
|
-
|
660
|
+
normalized_values = normalize_hyperparameters(subnet, json_output=json_output)
|
653
661
|
for param, value, norm_value in normalized_values:
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
+
if not json_output:
|
663
|
+
table.add_row(" " + param, value, norm_value)
|
664
|
+
else:
|
665
|
+
dict_out.append(
|
666
|
+
{
|
667
|
+
"hyperparameter": param,
|
668
|
+
"value": value,
|
669
|
+
"normalized_value": norm_value,
|
670
|
+
}
|
671
|
+
)
|
662
672
|
if json_output:
|
663
673
|
json_console.print(json.dumps(dict_out))
|
664
674
|
else:
|
@@ -1632,6 +1632,7 @@ async def swap_hotkey(
|
|
1632
1632
|
original_wallet: Wallet,
|
1633
1633
|
new_wallet: Wallet,
|
1634
1634
|
subtensor: SubtensorInterface,
|
1635
|
+
netuid: Optional[int],
|
1635
1636
|
prompt: bool,
|
1636
1637
|
json_output: bool,
|
1637
1638
|
):
|
@@ -1640,6 +1641,7 @@ async def swap_hotkey(
|
|
1640
1641
|
subtensor,
|
1641
1642
|
original_wallet,
|
1642
1643
|
new_wallet,
|
1644
|
+
netuid=netuid,
|
1643
1645
|
prompt=prompt,
|
1644
1646
|
)
|
1645
1647
|
if json_output:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bittensor-cli
|
3
|
-
Version: 9.
|
3
|
+
Version: 9.7.0
|
4
4
|
Summary: Bittensor CLI
|
5
5
|
Author: bittensor.com
|
6
6
|
Project-URL: homepage, https://github.com/opentensor/btcli
|
@@ -71,10 +71,10 @@ 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, or
|
74
|
+
You can install `btcli` on your local machine directly from source, PyPI, or Homebrew. **Make sure you verify your installation after you install**:
|
75
75
|
|
76
76
|
|
77
|
-
### Install from PyPI
|
77
|
+
### Install from [PyPI](https://pypi.org/project/bittensor/)
|
78
78
|
|
79
79
|
Run
|
80
80
|
```
|
@@ -86,6 +86,12 @@ Alternatively, if you prefer to use [uv](https://pypi.org/project/uv/):
|
|
86
86
|
uv pip install bittensor-cli
|
87
87
|
```
|
88
88
|
|
89
|
+
### Install from [Homebrew](https://formulae.brew.sh/formula/btcli#default)
|
90
|
+
|
91
|
+
```shell
|
92
|
+
brew install btcli
|
93
|
+
```
|
94
|
+
|
89
95
|
### Install from source
|
90
96
|
|
91
97
|
1. Create and activate a virtual environment.
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/extrinsics/__init__.py
RENAMED
File without changes
|
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/extrinsics/transfer.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/main-filters.j2
RENAMED
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/main-header.j2
RENAMED
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/neuron-details.j2
RENAMED
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/price-multi.j2
RENAMED
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/price-single.j2
RENAMED
File without changes
|
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/subnet-details.j2
RENAMED
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/subnet-metrics.j2
RENAMED
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/bittensor/templates/subnets-table.j2
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{bittensor_cli-9.5.1 → bittensor_cli-9.7.0}/bittensor_cli/src/commands/stake/children_hotkeys.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|