bittensor-cli 9.19.0rc2__tar.gz → 9.20.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.19.0rc2 → bittensor_cli-9.20.0}/PKG-INFO +3 -2
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/cli.py +96 -25
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/__init__.py +7 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/chain_data.py +18 -6
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/subtensor_interface.py +92 -54
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/utils.py +3 -2
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/proxy.py +46 -19
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/add.py +156 -49
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/list.py +10 -6
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/remove.py +242 -45
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/wallets.py +150 -4
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/pyproject.toml +3 -2
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/README.md +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/__init__.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/doc_generation_helper.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/__init__.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/balances.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/__init__.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/mev_shield.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/registration.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/root.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/serving.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/extrinsics/transfer.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/minigraph.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/networking.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/main-filters.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/main-header.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/neuron-details.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/price-multi.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/price-single.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/subnet-details-header.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/subnet-details.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/subnet-metrics.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/subnets-table.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/table.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/view.css +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/view.j2 +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/templates/view.js +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/__init__.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/axon/__init__.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/axon/axon.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/__init__.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/contribute.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/contributors.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/create.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/dissolve.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/refund.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/update.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/utils.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/crowd/view.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/liquidity/__init__.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/liquidity/liquidity.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/liquidity/utils.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/__init__.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/auto_staking.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/children_hotkeys.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/claim.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/move.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/stake/wizard.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/__init__.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/mechanisms.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/price.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/subnets/subnets.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/sudo.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/view.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/commands/weights.py +0 -0
- {bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bittensor-cli
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.20.0
|
|
4
4
|
Summary: Bittensor CLI
|
|
5
5
|
Author: bittensor.com
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -32,7 +32,8 @@ Requires-Dist: PyYAML~=6.0
|
|
|
32
32
|
Requires-Dist: rich>=13.7,<15.0
|
|
33
33
|
Requires-Dist: scalecodec==1.2.12
|
|
34
34
|
Requires-Dist: typer>=0.16
|
|
35
|
-
Requires-Dist:
|
|
35
|
+
Requires-Dist: typing_extensions>4.0.0; python_version<'3.11'
|
|
36
|
+
Requires-Dist: bittensor-wallet==4.0.1
|
|
36
37
|
Requires-Dist: packaging
|
|
37
38
|
Requires-Dist: plotille>=5.0.0
|
|
38
39
|
Requires-Dist: plotly>=6.0.0
|
|
@@ -641,8 +641,8 @@ def get_creation_data(
|
|
|
641
641
|
return mnemonic, seed, json_path, json_password
|
|
642
642
|
|
|
643
643
|
|
|
644
|
-
def config_selector(conf: dict, title: str):
|
|
645
|
-
def curses_selector(stdscr):
|
|
644
|
+
def config_selector(conf: dict[str, bool], title: str) -> dict[str, bool]:
|
|
645
|
+
def curses_selector(stdscr) -> dict[str, bool]:
|
|
646
646
|
"""
|
|
647
647
|
Enhanced Curses TUI to make selections.
|
|
648
648
|
"""
|
|
@@ -698,7 +698,7 @@ def config_selector(conf: dict, title: str):
|
|
|
698
698
|
return curses.wrapper(curses_selector)
|
|
699
699
|
|
|
700
700
|
|
|
701
|
-
def version_callback(value: bool):
|
|
701
|
+
def version_callback(value: bool) -> None:
|
|
702
702
|
"""
|
|
703
703
|
Prints the current version/branch-name
|
|
704
704
|
"""
|
|
@@ -716,7 +716,7 @@ def version_callback(value: bool):
|
|
|
716
716
|
raise typer.Exit()
|
|
717
717
|
|
|
718
718
|
|
|
719
|
-
def commands_callback(value: bool):
|
|
719
|
+
def commands_callback(value: bool) -> None:
|
|
720
720
|
"""
|
|
721
721
|
Prints a tree of commands for the app
|
|
722
722
|
"""
|
|
@@ -726,7 +726,7 @@ def commands_callback(value: bool):
|
|
|
726
726
|
raise typer.Exit()
|
|
727
727
|
|
|
728
728
|
|
|
729
|
-
def debug_callback(value: bool):
|
|
729
|
+
def debug_callback(value: bool) -> None:
|
|
730
730
|
if value:
|
|
731
731
|
debug_file_loc = Path(
|
|
732
732
|
os.getenv("BTCLI_DEBUG_FILE")
|
|
@@ -1389,7 +1389,7 @@ class CLIManager:
|
|
|
1389
1389
|
Generates a rich.Tree of the commands, subcommands, and groups of this app
|
|
1390
1390
|
"""
|
|
1391
1391
|
|
|
1392
|
-
def build_rich_tree(data: dict, parent: Tree):
|
|
1392
|
+
def build_rich_tree(data: dict, parent: Tree) -> None:
|
|
1393
1393
|
for group, content in data.get("groups", {}).items():
|
|
1394
1394
|
group_node = parent.add(
|
|
1395
1395
|
f"[bold cyan]{group}[/]"
|
|
@@ -1943,7 +1943,7 @@ class CLIManager:
|
|
|
1943
1943
|
with open(self.config_path, "w") as f:
|
|
1944
1944
|
safe_dump(self.config, f)
|
|
1945
1945
|
|
|
1946
|
-
def get_config(self):
|
|
1946
|
+
def get_config(self) -> None:
|
|
1947
1947
|
"""
|
|
1948
1948
|
Prints the current config file in a table.
|
|
1949
1949
|
"""
|
|
@@ -2079,7 +2079,7 @@ class CLIManager:
|
|
|
2079
2079
|
print_error(f"Proxy {name} not found in address book.")
|
|
2080
2080
|
self.config_get_proxies()
|
|
2081
2081
|
|
|
2082
|
-
def config_get_proxies(self):
|
|
2082
|
+
def config_get_proxies(self) -> None:
|
|
2083
2083
|
"""
|
|
2084
2084
|
Displays the current proxies address book
|
|
2085
2085
|
|
|
@@ -2228,7 +2228,7 @@ class CLIManager:
|
|
|
2228
2228
|
console.print("Proxy updated")
|
|
2229
2229
|
self.config_get_proxies()
|
|
2230
2230
|
|
|
2231
|
-
def config_clear_proxy_book(self):
|
|
2231
|
+
def config_clear_proxy_book(self) -> None:
|
|
2232
2232
|
"""
|
|
2233
2233
|
Clears the proxy address book. Use with caution.
|
|
2234
2234
|
Really only useful if you have corrupted your proxy address book.
|
|
@@ -4144,7 +4144,11 @@ class CLIManager:
|
|
|
4144
4144
|
self,
|
|
4145
4145
|
action: str = typer.Argument(
|
|
4146
4146
|
None,
|
|
4147
|
-
help=
|
|
4147
|
+
help=(
|
|
4148
|
+
"Action to perform: 'announce' to announce intent, "
|
|
4149
|
+
"'execute' to complete swap after delay, 'dispute' to freeze the swap, "
|
|
4150
|
+
"'clear' to withdraw announcement, 'check' to view status."
|
|
4151
|
+
),
|
|
4148
4152
|
),
|
|
4149
4153
|
wallet_name: Optional[str] = Options.wallet_name,
|
|
4150
4154
|
wallet_path: Optional[str] = Options.wallet_path,
|
|
@@ -4176,6 +4180,9 @@ class CLIManager:
|
|
|
4176
4180
|
If you suspect compromise, you can [bold]Dispute[/bold] an active announcement to freeze
|
|
4177
4181
|
all activity for the coldkey until the triumvirate can intervene.
|
|
4178
4182
|
|
|
4183
|
+
If you want to withdraw your announcement, you can [bold]Clear[/bold] (withdraw) an announcement once the
|
|
4184
|
+
reannouncement delay has elapsed.
|
|
4185
|
+
|
|
4179
4186
|
EXAMPLES
|
|
4180
4187
|
|
|
4181
4188
|
Step 1 - Announce your intent to swap:
|
|
@@ -4190,9 +4197,13 @@ class CLIManager:
|
|
|
4190
4197
|
|
|
4191
4198
|
[green]$[/green] btcli wallet swap-coldkey dispute
|
|
4192
4199
|
|
|
4193
|
-
|
|
4200
|
+
Clear (withdraw) an announcement:
|
|
4201
|
+
|
|
4202
|
+
[green]$[/green] btcli wallet swap-coldkey clear
|
|
4194
4203
|
|
|
4195
|
-
|
|
4204
|
+
Check status of your swap:
|
|
4205
|
+
|
|
4206
|
+
[green]$[/green] btcli wallet swap-coldkey check
|
|
4196
4207
|
"""
|
|
4197
4208
|
self.verbosity_handler(quiet, verbose, prompt=False, json_output=False)
|
|
4198
4209
|
|
|
@@ -4201,18 +4212,19 @@ class CLIManager:
|
|
|
4201
4212
|
"\n[bold][blue]Coldkey Swap Actions:[/blue][/bold]\n"
|
|
4202
4213
|
" [dark_sea_green3]announce[/dark_sea_green3] - Start the swap process (pays fee, starts delay timer)\n"
|
|
4203
4214
|
" [dark_sea_green3]execute[/dark_sea_green3] - Complete the swap (after delay period)\n"
|
|
4204
|
-
" [dark_sea_green3]dispute[/dark_sea_green3] - Freeze the swap process if you suspect compromise\n
|
|
4205
|
-
" [
|
|
4215
|
+
" [dark_sea_green3]dispute[/dark_sea_green3] - Freeze the swap process if you suspect compromise\n"
|
|
4216
|
+
" [dark_sea_green3]clear[/dark_sea_green3] - Withdraw your swap announcement\n"
|
|
4217
|
+
" [dark_sea_green3]check[/dark_sea_green3] - Check the status of your swap\n\n"
|
|
4206
4218
|
)
|
|
4207
4219
|
action = Prompt.ask(
|
|
4208
4220
|
"Select action",
|
|
4209
|
-
choices=["announce", "execute", "dispute"],
|
|
4221
|
+
choices=["announce", "execute", "dispute", "clear", "check"],
|
|
4210
4222
|
default="announce",
|
|
4211
4223
|
)
|
|
4212
4224
|
|
|
4213
|
-
if action.lower() not in ("announce", "execute", "dispute"):
|
|
4225
|
+
if action.lower() not in ("announce", "execute", "dispute", "clear", "check"):
|
|
4214
4226
|
print_error(
|
|
4215
|
-
f"Invalid action: {action}. Must be 'announce', 'execute', or '
|
|
4227
|
+
f"Invalid action: {action}. Must be 'announce', 'execute', 'dispute', 'clear', or 'check'."
|
|
4216
4228
|
)
|
|
4217
4229
|
raise typer.Exit(1)
|
|
4218
4230
|
|
|
@@ -4233,7 +4245,7 @@ class CLIManager:
|
|
|
4233
4245
|
)
|
|
4234
4246
|
|
|
4235
4247
|
new_wallet_coldkey_ss58 = None
|
|
4236
|
-
if action
|
|
4248
|
+
if action not in ("dispute", "clear", "check"):
|
|
4237
4249
|
if not new_wallet_or_ss58:
|
|
4238
4250
|
new_wallet_or_ss58 = Prompt.ask(
|
|
4239
4251
|
"Enter the [blue]new wallet name[/blue] or [blue]SS58 address[/blue] of the new coldkey",
|
|
@@ -4285,6 +4297,24 @@ class CLIManager:
|
|
|
4285
4297
|
mev_protection=mev_protection,
|
|
4286
4298
|
)
|
|
4287
4299
|
)
|
|
4300
|
+
elif action == "clear":
|
|
4301
|
+
return self._run_command(
|
|
4302
|
+
wallets.clear_coldkey_swap_announcement(
|
|
4303
|
+
wallet=wallet,
|
|
4304
|
+
subtensor=self.initialize_chain(network),
|
|
4305
|
+
decline=decline,
|
|
4306
|
+
quiet=quiet,
|
|
4307
|
+
prompt=prompt,
|
|
4308
|
+
mev_protection=mev_protection,
|
|
4309
|
+
)
|
|
4310
|
+
)
|
|
4311
|
+
elif action == "check":
|
|
4312
|
+
return self._run_command(
|
|
4313
|
+
wallets.check_swap_status(
|
|
4314
|
+
subtensor=self.initialize_chain(network),
|
|
4315
|
+
origin_ss58=wallet.coldkeypub.ss58_address,
|
|
4316
|
+
)
|
|
4317
|
+
)
|
|
4288
4318
|
else:
|
|
4289
4319
|
return self._run_command(
|
|
4290
4320
|
wallets.execute_coldkey_swap(
|
|
@@ -9807,13 +9837,18 @@ class CLIManager:
|
|
|
9807
9837
|
def proxy_remove(
|
|
9808
9838
|
self,
|
|
9809
9839
|
delegate: Annotated[
|
|
9810
|
-
str,
|
|
9840
|
+
Optional[str],
|
|
9811
9841
|
typer.Option(
|
|
9812
9842
|
callback=is_valid_ss58_address_param,
|
|
9813
|
-
prompt=
|
|
9814
|
-
help="The SS58 address of the delegate to remove",
|
|
9843
|
+
prompt=False,
|
|
9844
|
+
help="The SS58 address of the delegate to remove (required if --all is not used)",
|
|
9815
9845
|
),
|
|
9816
|
-
] =
|
|
9846
|
+
] = None,
|
|
9847
|
+
all_: bool = typer.Option(
|
|
9848
|
+
False,
|
|
9849
|
+
"--all",
|
|
9850
|
+
help="Remove all proxies associated with this account",
|
|
9851
|
+
),
|
|
9817
9852
|
network: Optional[list[str]] = Options.network,
|
|
9818
9853
|
proxy_type: ProxyType = Options.proxy_type,
|
|
9819
9854
|
delay: int = typer.Option(0, help="Delay, in number of blocks"),
|
|
@@ -9840,10 +9875,10 @@ class CLIManager:
|
|
|
9840
9875
|
[green]$[/green] btcli proxy remove --delegate 5GDel... --proxy-type Transfer
|
|
9841
9876
|
|
|
9842
9877
|
"""
|
|
9843
|
-
# TODO should add a --all flag to call Proxy.remove_proxies ?
|
|
9844
9878
|
logger.debug(
|
|
9845
9879
|
"args:\n"
|
|
9846
9880
|
f"delegate: {delegate}\n"
|
|
9881
|
+
f"all: {all_}\n"
|
|
9847
9882
|
f"network: {network}\n"
|
|
9848
9883
|
f"proxy_type: {proxy_type}\n"
|
|
9849
9884
|
f"delay: {delay}\n"
|
|
@@ -9851,6 +9886,41 @@ class CLIManager:
|
|
|
9851
9886
|
f"wait_for_inclusion: {wait_for_inclusion}\n"
|
|
9852
9887
|
f"era: {period}\n"
|
|
9853
9888
|
)
|
|
9889
|
+
# Validate that --delegate and --all are not used together
|
|
9890
|
+
if all_ and delegate:
|
|
9891
|
+
if not json_output:
|
|
9892
|
+
print_error("--delegate cannot be used together with --all flag.")
|
|
9893
|
+
else:
|
|
9894
|
+
json_console.print_json(
|
|
9895
|
+
data={
|
|
9896
|
+
"success": False,
|
|
9897
|
+
"message": "--delegate cannot be used together with --all flag.",
|
|
9898
|
+
"extrinsic_identifier": None,
|
|
9899
|
+
}
|
|
9900
|
+
)
|
|
9901
|
+
return
|
|
9902
|
+
|
|
9903
|
+
# If --all is not used and delegate is not provided, prompt or error
|
|
9904
|
+
if not all_ and not delegate:
|
|
9905
|
+
if not prompt:
|
|
9906
|
+
if not json_output:
|
|
9907
|
+
print_error(
|
|
9908
|
+
"Either --delegate must be provided or --all flag must be used."
|
|
9909
|
+
)
|
|
9910
|
+
else:
|
|
9911
|
+
json_console.print_json(
|
|
9912
|
+
data={
|
|
9913
|
+
"success": False,
|
|
9914
|
+
"message": "Either --delegate must be provided or --all flag must be used.",
|
|
9915
|
+
"extrinsic_identifier": None,
|
|
9916
|
+
}
|
|
9917
|
+
)
|
|
9918
|
+
return
|
|
9919
|
+
delegate = Prompt.ask(
|
|
9920
|
+
"Enter the SS58 address of the delegate to remove, e.g. 5dxds..."
|
|
9921
|
+
)
|
|
9922
|
+
delegate = is_valid_ss58_address_param(delegate)
|
|
9923
|
+
|
|
9854
9924
|
self.verbosity_handler(quiet, verbose, json_output, prompt)
|
|
9855
9925
|
wallet = self.wallet_ask(
|
|
9856
9926
|
wallet_name=wallet_name,
|
|
@@ -9873,6 +9943,7 @@ class CLIManager:
|
|
|
9873
9943
|
wait_for_finalization=wait_for_finalization,
|
|
9874
9944
|
period=period,
|
|
9875
9945
|
json_output=json_output,
|
|
9946
|
+
remove_all=all_,
|
|
9876
9947
|
)
|
|
9877
9948
|
)
|
|
9878
9949
|
|
|
@@ -10275,11 +10346,11 @@ class CLIManager:
|
|
|
10275
10346
|
)
|
|
10276
10347
|
return True
|
|
10277
10348
|
|
|
10278
|
-
def run(self):
|
|
10349
|
+
def run(self) -> None:
|
|
10279
10350
|
self.app()
|
|
10280
10351
|
|
|
10281
10352
|
|
|
10282
|
-
def main():
|
|
10353
|
+
def main() -> None:
|
|
10283
10354
|
manager = CLIManager()
|
|
10284
10355
|
manager.run()
|
|
10285
10356
|
|
|
@@ -692,6 +692,7 @@ HYPERPARAMS = {
|
|
|
692
692
|
"alpha_low": ("", RootSudoOnly.FALSE), # Derived from alpha_values
|
|
693
693
|
"subnet_is_active": ("", RootSudoOnly.FALSE), # Set via btcli subnets start
|
|
694
694
|
"yuma_version": ("", RootSudoOnly.FALSE), # Related to yuma3_enabled
|
|
695
|
+
"max_allowed_uids": ("sudo_set_max_allowed_uids", RootSudoOnly.FALSE),
|
|
695
696
|
}
|
|
696
697
|
|
|
697
698
|
HYPERPARAMS_MODULE = {
|
|
@@ -941,6 +942,12 @@ HYPERPARAMS_METADATA = {
|
|
|
941
942
|
"owner_settable": True,
|
|
942
943
|
"docs_link": "docs.learnbittensor.org/subnets/subnet-hyperparameters#yuma3",
|
|
943
944
|
},
|
|
945
|
+
"max_allowed_uids": {
|
|
946
|
+
"description": "Maximum number of UIDs (neurons) on the subnet, essentially 'untrimming'.",
|
|
947
|
+
"side_effects": "See description for min_allowed_uids",
|
|
948
|
+
"owner_settable": True,
|
|
949
|
+
"docs_link": "docs.learnbittensor.org/subnets/subnet-hyperparameters#maxalloweduids",
|
|
950
|
+
},
|
|
944
951
|
}
|
|
945
952
|
|
|
946
953
|
# Help Panels for cli help
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
+
from collections.abc import Sequence
|
|
3
4
|
from enum import Enum
|
|
4
|
-
from typing import Optional, Any, Union
|
|
5
|
+
from typing import Optional, Any, Union, Callable, Hashable
|
|
5
6
|
|
|
6
7
|
import netaddr
|
|
7
8
|
from scalecodec.utils.ss58 import ss58_encode
|
|
@@ -16,6 +17,11 @@ from bittensor_cli.src.bittensor.utils import (
|
|
|
16
17
|
get_netuid_and_subuid_by_storage_index,
|
|
17
18
|
)
|
|
18
19
|
|
|
20
|
+
try:
|
|
21
|
+
from typing import Self
|
|
22
|
+
except ImportError:
|
|
23
|
+
from typing_extensions import Self
|
|
24
|
+
|
|
19
25
|
|
|
20
26
|
class ChainDataType(Enum):
|
|
21
27
|
NeuronInfo = 1
|
|
@@ -69,9 +75,12 @@ def _chr_str(codes: tuple[int]) -> str:
|
|
|
69
75
|
return "".join(map(chr, codes))
|
|
70
76
|
|
|
71
77
|
|
|
72
|
-
def process_nested(
|
|
78
|
+
def process_nested(
|
|
79
|
+
data: Sequence[dict[Hashable, tuple[int]]] | dict | Any,
|
|
80
|
+
chr_transform: Callable[[tuple[int]], str],
|
|
81
|
+
) -> list[dict[Hashable, str]] | dict[Hashable, str] | Any:
|
|
73
82
|
"""Processes nested data structures by applying a transformation function to their elements."""
|
|
74
|
-
if isinstance(data,
|
|
83
|
+
if isinstance(data, Sequence):
|
|
75
84
|
if len(data) > 0 and isinstance(data[0], dict):
|
|
76
85
|
return [
|
|
77
86
|
{k: chr_transform(v) for k, v in item.items()}
|
|
@@ -79,9 +88,12 @@ def process_nested(data: Union[tuple, dict], chr_transform):
|
|
|
79
88
|
else None
|
|
80
89
|
for item in data
|
|
81
90
|
]
|
|
91
|
+
# TODO @abe why do we kind of silently fail here?
|
|
82
92
|
return {}
|
|
83
93
|
elif isinstance(data, dict):
|
|
84
94
|
return {k: chr_transform(v) for k, v in data.items()}
|
|
95
|
+
else:
|
|
96
|
+
return data
|
|
85
97
|
|
|
86
98
|
|
|
87
99
|
@dataclass
|
|
@@ -127,17 +139,17 @@ class InfoBase:
|
|
|
127
139
|
"""Base dataclass for info objects."""
|
|
128
140
|
|
|
129
141
|
@abstractmethod
|
|
130
|
-
def _fix_decoded(self, decoded: Any) ->
|
|
142
|
+
def _fix_decoded(self, decoded: Any) -> Self:
|
|
131
143
|
raise NotImplementedError(
|
|
132
144
|
"This is an abstract method and must be implemented in a subclass."
|
|
133
145
|
)
|
|
134
146
|
|
|
135
147
|
@classmethod
|
|
136
|
-
def from_any(cls, data: Any) ->
|
|
148
|
+
def from_any(cls, data: Any) -> Self:
|
|
137
149
|
return cls._fix_decoded(data)
|
|
138
150
|
|
|
139
151
|
@classmethod
|
|
140
|
-
def list_from_any(cls, data_list: list[Any]) -> list[
|
|
152
|
+
def list_from_any(cls, data_list: list[Any]) -> list[Self]:
|
|
141
153
|
return [cls.from_any(data) for data in data_list]
|
|
142
154
|
|
|
143
155
|
def __getitem__(self, item):
|
{bittensor_cli-9.19.0rc2 → bittensor_cli-9.20.0}/bittensor_cli/src/bittensor/subtensor_interface.py
RENAMED
|
@@ -297,7 +297,7 @@ class SubtensorInterface:
|
|
|
297
297
|
self,
|
|
298
298
|
hotkey_ss58: str,
|
|
299
299
|
coldkey_ss58: str,
|
|
300
|
-
netuid:
|
|
300
|
+
netuid: int,
|
|
301
301
|
block_hash: Optional[str] = None,
|
|
302
302
|
) -> Balance:
|
|
303
303
|
"""
|
|
@@ -305,42 +305,18 @@ class SubtensorInterface:
|
|
|
305
305
|
|
|
306
306
|
:param hotkey_ss58: The SS58 address of the hotkey.
|
|
307
307
|
:param coldkey_ss58: The SS58 address of the coldkey.
|
|
308
|
-
:param netuid: The subnet ID
|
|
309
|
-
subnet.
|
|
308
|
+
:param netuid: The subnet ID for the stake query.
|
|
310
309
|
:param block_hash: The block hash at which to query the stake information.
|
|
311
310
|
|
|
312
311
|
:return: Balance: The stake under the coldkey - hotkey pairing.
|
|
313
312
|
"""
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
block_hash=block_hash,
|
|
320
|
-
),
|
|
321
|
-
self.query(
|
|
322
|
-
module="SubtensorModule",
|
|
323
|
-
storage_function="TotalHotkeyAlpha",
|
|
324
|
-
params=[hotkey_ss58, netuid],
|
|
325
|
-
block_hash=block_hash,
|
|
326
|
-
),
|
|
327
|
-
self.query(
|
|
328
|
-
module="SubtensorModule",
|
|
329
|
-
storage_function="TotalHotkeyShares",
|
|
330
|
-
params=[hotkey_ss58, netuid],
|
|
331
|
-
block_hash=block_hash,
|
|
332
|
-
),
|
|
313
|
+
result = await self.query_runtime_api(
|
|
314
|
+
runtime_api="StakeInfoRuntimeApi",
|
|
315
|
+
method="get_stake_info_for_hotkey_coldkey_netuid",
|
|
316
|
+
params=[hotkey_ss58, coldkey_ss58, netuid],
|
|
317
|
+
block_hash=block_hash,
|
|
333
318
|
)
|
|
334
|
-
|
|
335
|
-
alpha_shares_as_float = fixed_to_float(alpha_shares or 0)
|
|
336
|
-
hotkey_shares_as_float = fixed_to_float(hotkey_shares or 0)
|
|
337
|
-
|
|
338
|
-
if hotkey_shares_as_float == 0:
|
|
339
|
-
return Balance.from_rao(0).set_unit(netuid=netuid)
|
|
340
|
-
|
|
341
|
-
stake = alpha_shares_as_float / hotkey_shares_as_float * (hotkey_alpha or 0)
|
|
342
|
-
|
|
343
|
-
return Balance.from_rao(int(stake)).set_unit(netuid=netuid)
|
|
319
|
+
return StakeInfo.from_any(result).stake
|
|
344
320
|
|
|
345
321
|
# Alias
|
|
346
322
|
get_stake = get_stake_for_coldkey_and_hotkey
|
|
@@ -1249,6 +1225,9 @@ class SubtensorInterface:
|
|
|
1249
1225
|
)
|
|
1250
1226
|
inner_hash = ""
|
|
1251
1227
|
if mev_protection:
|
|
1228
|
+
max_mev_era = 8
|
|
1229
|
+
if era is None or era["period"] > max_mev_era:
|
|
1230
|
+
era = {"period": max_mev_era}
|
|
1252
1231
|
next_nonce = await self.substrate.get_account_next_index(
|
|
1253
1232
|
keypair.ss58_address
|
|
1254
1233
|
)
|
|
@@ -1292,8 +1271,8 @@ class SubtensorInterface:
|
|
|
1292
1271
|
err_msg = format_error_message(e)
|
|
1293
1272
|
if mev_protection and "'result': 'invalid'" in str(e).lower():
|
|
1294
1273
|
err_msg = (
|
|
1295
|
-
|
|
1296
|
-
|
|
1274
|
+
"MEV Shield extrinsic rejected as invalid. "
|
|
1275
|
+
"This usually means the MEV Shield NextKey changed between fetching and submission."
|
|
1297
1276
|
)
|
|
1298
1277
|
if proxy and "Invalid Transaction" in err_msg:
|
|
1299
1278
|
extrinsic_fee, signer_balance = await asyncio.gather(
|
|
@@ -1310,6 +1289,84 @@ class SubtensorInterface:
|
|
|
1310
1289
|
)
|
|
1311
1290
|
return False, err_msg, None
|
|
1312
1291
|
|
|
1292
|
+
async def sign_and_send_batch_extrinsic(
|
|
1293
|
+
self,
|
|
1294
|
+
calls: list[GenericCall],
|
|
1295
|
+
wallet: Wallet,
|
|
1296
|
+
wait_for_inclusion: bool = True,
|
|
1297
|
+
wait_for_finalization: bool = False,
|
|
1298
|
+
era: Optional[dict[str, int]] = None,
|
|
1299
|
+
proxy: Optional[str] = None,
|
|
1300
|
+
nonce: Optional[int] = None,
|
|
1301
|
+
sign_with: Literal["coldkey", "hotkey", "coldkeypub"] = "coldkey",
|
|
1302
|
+
announce_only: bool = False,
|
|
1303
|
+
mev_protection: bool = False,
|
|
1304
|
+
block_hash: Optional[str] = None,
|
|
1305
|
+
) -> tuple[bool, str, Optional[AsyncExtrinsicReceipt]]:
|
|
1306
|
+
"""
|
|
1307
|
+
Wraps multiple extrinsic calls into a single Utility.batch_all transaction
|
|
1308
|
+
and submits it. This reduces fees by combining N separate transactions into one.
|
|
1309
|
+
|
|
1310
|
+
batch_all is atomic: if any call in the batch fails, the entire batch reverts.
|
|
1311
|
+
|
|
1312
|
+
For a single call, this delegates directly to sign_and_send_extrinsic without
|
|
1313
|
+
wrapping, so there's no overhead for non-batch use cases.
|
|
1314
|
+
|
|
1315
|
+
:param calls: list of prepared GenericCall objects to batch together.
|
|
1316
|
+
:param wallet: the wallet whose key will sign the extrinsic.
|
|
1317
|
+
:param wait_for_inclusion: wait until the extrinsic is included on chain.
|
|
1318
|
+
:param wait_for_finalization: wait until the extrinsic is finalized on chain.
|
|
1319
|
+
:param era: validity period in blocks for the transaction.
|
|
1320
|
+
:param proxy: the real account if using a proxy. None otherwise.
|
|
1321
|
+
:param nonce: explicit nonce for submission. Fetched automatically if None.
|
|
1322
|
+
:param sign_with: which wallet keypair signs the extrinsic.
|
|
1323
|
+
:param announce_only: make the call as a proxy announcement.
|
|
1324
|
+
:param mev_protection: encrypt the extrinsic via MEV Shield.
|
|
1325
|
+
:param block_hash: cached block hash for compose_call. Fetched if None.
|
|
1326
|
+
|
|
1327
|
+
:return: (success, error message or inner hash, extrinsic receipt | None)
|
|
1328
|
+
"""
|
|
1329
|
+
if not calls:
|
|
1330
|
+
return False, "No calls to batch", None
|
|
1331
|
+
|
|
1332
|
+
# No need to wrap a single call in a batch
|
|
1333
|
+
if len(calls) == 1:
|
|
1334
|
+
return await self.sign_and_send_extrinsic(
|
|
1335
|
+
call=calls[0],
|
|
1336
|
+
wallet=wallet,
|
|
1337
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
1338
|
+
wait_for_finalization=wait_for_finalization,
|
|
1339
|
+
era=era,
|
|
1340
|
+
proxy=proxy,
|
|
1341
|
+
nonce=nonce,
|
|
1342
|
+
sign_with=sign_with,
|
|
1343
|
+
announce_only=announce_only,
|
|
1344
|
+
mev_protection=mev_protection,
|
|
1345
|
+
)
|
|
1346
|
+
|
|
1347
|
+
if block_hash is None:
|
|
1348
|
+
block_hash = await self.substrate.get_chain_head()
|
|
1349
|
+
|
|
1350
|
+
batch_call = await self.substrate.compose_call(
|
|
1351
|
+
call_module="Utility",
|
|
1352
|
+
call_function="batch_all",
|
|
1353
|
+
call_params={"calls": calls},
|
|
1354
|
+
block_hash=block_hash,
|
|
1355
|
+
)
|
|
1356
|
+
|
|
1357
|
+
return await self.sign_and_send_extrinsic(
|
|
1358
|
+
call=batch_call,
|
|
1359
|
+
wallet=wallet,
|
|
1360
|
+
wait_for_inclusion=wait_for_inclusion,
|
|
1361
|
+
wait_for_finalization=wait_for_finalization,
|
|
1362
|
+
era=era,
|
|
1363
|
+
proxy=proxy,
|
|
1364
|
+
nonce=nonce,
|
|
1365
|
+
sign_with=sign_with,
|
|
1366
|
+
announce_only=announce_only,
|
|
1367
|
+
mev_protection=mev_protection,
|
|
1368
|
+
)
|
|
1369
|
+
|
|
1313
1370
|
async def get_children(self, hotkey, netuid) -> tuple[bool, list, str]:
|
|
1314
1371
|
"""
|
|
1315
1372
|
This method retrieves the children of a given hotkey and netuid. It queries the SubtensorModule's ChildKeys
|
|
@@ -1488,25 +1545,6 @@ class SubtensorInterface:
|
|
|
1488
1545
|
|
|
1489
1546
|
return all_delegates_details
|
|
1490
1547
|
|
|
1491
|
-
async def get_stake_for_coldkey_and_hotkey_on_netuid(
|
|
1492
|
-
self,
|
|
1493
|
-
hotkey_ss58: str,
|
|
1494
|
-
coldkey_ss58: str,
|
|
1495
|
-
netuid: int,
|
|
1496
|
-
block_hash: Optional[str] = None,
|
|
1497
|
-
) -> "Balance":
|
|
1498
|
-
"""Returns the stake under a coldkey - hotkey - netuid pairing"""
|
|
1499
|
-
_result = await self.query(
|
|
1500
|
-
"SubtensorModule",
|
|
1501
|
-
"Alpha",
|
|
1502
|
-
[hotkey_ss58, coldkey_ss58, netuid],
|
|
1503
|
-
block_hash,
|
|
1504
|
-
)
|
|
1505
|
-
if _result is None:
|
|
1506
|
-
return Balance(0).set_unit(netuid)
|
|
1507
|
-
else:
|
|
1508
|
-
return Balance.from_rao(fixed_to_float(_result)).set_unit(int(netuid))
|
|
1509
|
-
|
|
1510
1548
|
async def get_mechagraph_info(
|
|
1511
1549
|
self, netuid: int, mech_id: int, block_hash: Optional[str] = None
|
|
1512
1550
|
) -> Optional[MetagraphInfo]:
|
|
@@ -2387,7 +2425,7 @@ class SubtensorInterface:
|
|
|
2387
2425
|
After manual claim, claimable (available) stake will be added to subnet stake.
|
|
2388
2426
|
"""
|
|
2389
2427
|
root_stake, root_claimable_rate, root_claimed = await asyncio.gather(
|
|
2390
|
-
self.
|
|
2428
|
+
self.get_stake(
|
|
2391
2429
|
coldkey_ss58=coldkey_ss58,
|
|
2392
2430
|
hotkey_ss58=hotkey_ss58,
|
|
2393
2431
|
netuid=0,
|
|
@@ -1757,8 +1757,9 @@ def prompt_for_subnet_identity(
|
|
|
1757
1757
|
"github_repo",
|
|
1758
1758
|
"[blue]GitHub repository URL [dim](optional)[/blue]",
|
|
1759
1759
|
github_repo,
|
|
1760
|
-
lambda x:
|
|
1761
|
-
|
|
1760
|
+
lambda x: (
|
|
1761
|
+
x and (not is_valid_github_url(x) or len(x.encode("utf-8")) > 1024)
|
|
1762
|
+
),
|
|
1762
1763
|
"[red]Error:[/red] Please enter a valid GitHub repository URL (e.g., https://github.com/username/repo).",
|
|
1763
1764
|
),
|
|
1764
1765
|
(
|
|
@@ -255,8 +255,8 @@ async def create_proxy(
|
|
|
255
255
|
async def remove_proxy(
|
|
256
256
|
subtensor: "SubtensorInterface",
|
|
257
257
|
wallet: "Wallet",
|
|
258
|
-
proxy_type: ProxyType,
|
|
259
|
-
delegate: str,
|
|
258
|
+
proxy_type: Optional[ProxyType],
|
|
259
|
+
delegate: Optional[str],
|
|
260
260
|
delay: int,
|
|
261
261
|
prompt: bool,
|
|
262
262
|
decline: bool,
|
|
@@ -265,18 +265,35 @@ async def remove_proxy(
|
|
|
265
265
|
wait_for_finalization: bool,
|
|
266
266
|
period: int,
|
|
267
267
|
json_output: bool,
|
|
268
|
+
remove_all: bool = False,
|
|
268
269
|
) -> None:
|
|
269
270
|
"""
|
|
270
|
-
Executes the remove proxy call on the chain
|
|
271
|
+
Executes the remove proxy call on the chain.
|
|
272
|
+
|
|
273
|
+
If remove_all is True, removes all proxies for the account.
|
|
274
|
+
Otherwise, removes a specific proxy identified by delegate, proxy_type, and delay.
|
|
271
275
|
"""
|
|
276
|
+
# Handle confirmation prompt
|
|
272
277
|
if prompt:
|
|
273
|
-
if
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
278
|
+
if remove_all:
|
|
279
|
+
confirmation = Prompt.ask(
|
|
280
|
+
"[red]WARNING:[/red] This will remove ALL proxies associated with this account.\n"
|
|
281
|
+
"[red]All proxy relationships will be permanently lost.[/red]\n"
|
|
282
|
+
"To proceed, enter [red]REMOVE[/red]"
|
|
283
|
+
)
|
|
284
|
+
if confirmation != "REMOVE":
|
|
285
|
+
print_error("Invalid input. Operation cancelled.")
|
|
286
|
+
return None
|
|
287
|
+
else:
|
|
288
|
+
if not confirm_action(
|
|
289
|
+
f"This will remove a proxy of type {proxy_type.value} for delegate {delegate}. "
|
|
290
|
+
f"Do you want to proceed?",
|
|
291
|
+
decline=decline,
|
|
292
|
+
quiet=quiet,
|
|
293
|
+
):
|
|
294
|
+
return None
|
|
295
|
+
|
|
296
|
+
# Unlock wallet
|
|
280
297
|
if not (ulw := unlock_key(wallet, print_out=not json_output)).success:
|
|
281
298
|
if not json_output:
|
|
282
299
|
print_error(ulw.message)
|
|
@@ -289,15 +306,25 @@ async def remove_proxy(
|
|
|
289
306
|
}
|
|
290
307
|
)
|
|
291
308
|
return None
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
"
|
|
297
|
-
"
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
309
|
+
|
|
310
|
+
# Compose the appropriate call
|
|
311
|
+
if remove_all:
|
|
312
|
+
call = await subtensor.substrate.compose_call(
|
|
313
|
+
call_module="Proxy",
|
|
314
|
+
call_function="remove_proxies",
|
|
315
|
+
call_params={},
|
|
316
|
+
)
|
|
317
|
+
else:
|
|
318
|
+
call = await subtensor.substrate.compose_call(
|
|
319
|
+
call_module="Proxy",
|
|
320
|
+
call_function="remove_proxy",
|
|
321
|
+
call_params={
|
|
322
|
+
"proxy_type": proxy_type.value,
|
|
323
|
+
"delay": delay,
|
|
324
|
+
"delegate": delegate,
|
|
325
|
+
},
|
|
326
|
+
)
|
|
327
|
+
|
|
301
328
|
return await submit_proxy(
|
|
302
329
|
subtensor=subtensor,
|
|
303
330
|
wallet=wallet,
|